You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 17:29:51 UTC

[02/53] [partial] incubator-juneau git commit: Merge changes from GitHub repo.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayCache.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayCache.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayCache.java
new file mode 100644
index 0000000..9747902
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayCache.java
@@ -0,0 +1,106 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * A utility class for caching byte arrays in memory so that duplicate arrays can be reused.
+ * <p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class ByteArrayCache {
+
+	/**
+	 * Default global byte array cache.
+	 * Note that this can't ever get garbage collected so don't add really large arrays!
+	 */
+	public static final ByteArrayCache DEFAULT = new ByteArrayCache();
+
+	private final ConcurrentHashMap<ByteArray,byte[]> cache = new ConcurrentHashMap<ByteArray,byte[]>();
+
+	/**
+	 * Add the specified byte array to this cache.
+	 *
+	 * @param contents The byte array to add to this cache.
+	 * @return Either the same byte array or a previously cached byte array depending on whether the byte array
+	 * 	already exists in the cache.
+	 */
+	public byte[] cache(byte[] contents) {
+		if (contents == null)
+			return null;
+		ByteArray ba = new ByteArray(contents);
+		cache.putIfAbsent(ba, ba.contents);
+		return cache.get(ba);
+	}
+
+	/**
+	 * Add the specified input stream to this cache.
+	 *
+	 * @param contents The input stream whose contents are to be added to this cache.
+	 * @return Either the same byte array or a previously cached byte array depending on whether the byte array
+	 * 	already exists in the cache.
+	 * @throws IOException
+	 */
+	public byte[] cache(InputStream contents) throws IOException {
+		if (contents == null)
+			return null;
+		ByteArray ba = new ByteArray(IOUtils.readBytes(contents, 1024));
+		cache.putIfAbsent(ba, ba.contents);
+		return cache.get(ba);
+	}
+
+	/**
+	 * Returns the number of byte arrays in this cache.
+	 *
+	 * @return The number of byte arrays in this cache.
+	 */
+	public int size() {
+		return cache.size();
+	}
+
+	private static class ByteArray {
+		private int hashCode;
+		private byte[] contents;
+
+		private ByteArray(byte[] contents) {
+			this.contents = contents;
+			int multiplier = 1;
+			for (int i = 0; i < contents.length; i++) {
+				hashCode += contents[i] * multiplier;
+				int shifted = multiplier << 5;
+				multiplier = shifted - multiplier;
+			}
+		}
+
+		@Override /* Object */
+		public int hashCode() {
+			if (hashCode == 0) {
+			}
+			return hashCode;
+		}
+
+		@Override /* Object */
+		public boolean equals(Object o) {
+			if (o instanceof ByteArray) {
+				ByteArray ba = (ByteArray)o;
+				if (ba.hashCode == hashCode)
+					return Arrays.equals(ba.contents, contents);
+			}
+			return false;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayInOutStream.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayInOutStream.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayInOutStream.java
new file mode 100644
index 0000000..d104c77
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ByteArrayInOutStream.java
@@ -0,0 +1,32 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.io.*;
+
+/**
+ * Subclass of a ByteArrayOutputStream that avoids a byte array copy when reading from an input stream.
+ * <p>
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class ByteArrayInOutStream extends ByteArrayOutputStream {
+
+	/**
+	 * Creates a new input stream from this object.
+	 *
+	 * @return A new input stream from this object.
+	 */
+	public ByteArrayInputStream getInputStream() {
+		return new ByteArrayInputStream(this.buf, 0, this.count);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/CharSequenceReader.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/CharSequenceReader.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/CharSequenceReader.java
new file mode 100644
index 0000000..fe89635
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/CharSequenceReader.java
@@ -0,0 +1,100 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.io.*;
+
+/**
+ * Similar to {@link StringReader} except reads from a generic {@link CharSequenceReader}.
+ *
+ * @author jbognar
+ */
+public final class CharSequenceReader extends BufferedReader {
+
+	private final CharSequence cs;
+	private String s;
+	private StringBuffer sb;
+	private StringBuilder sb2;
+	private int length;
+	private int next = 0;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param cs The char sequence to read from.  Can be <jk>null</jk>.
+	 */
+	public CharSequenceReader(CharSequence cs) {
+		super(new StringReader(""), 1);   // Does not actually use a reader.
+		if (cs == null)
+			cs = "";
+		this.cs = cs;
+		if (cs instanceof String)
+			s = (String)cs;
+		else if (cs instanceof StringBuffer)
+			sb = (StringBuffer)cs;
+		else if (cs instanceof StringBuilder)
+			sb2 = (StringBuilder)cs;
+		this.length = cs.length();
+	}
+
+	@Override /* Reader */
+	public int read() {
+		if (next >= length)
+			return -1;
+		return cs.charAt(next++);
+	}
+
+	@Override /* Reader */
+	public boolean markSupported() {
+		return false;
+	}
+
+	@Override /* Reader */
+	public int read(final char[] cbuf, final int off, final int len) {
+		if (next >= length)
+			return -1;
+		int n = Math.min(length - next, len);
+		if (s != null)
+			s.getChars(next, next + n, cbuf, off);
+		else if (sb != null)
+			sb.getChars(next, next + n, cbuf, off);
+		else if (sb2 != null)
+			sb2.getChars(next, next + n, cbuf, off);
+		else {
+			for (int i = 0; i < n; i++)
+				cbuf[off+i] = cs.charAt(next+i);
+		}
+		next += n;
+		return n;
+	}
+
+	@Override /* Reader */
+	public long skip(long ns) {
+		if (next >= length)
+			return 0;
+		long n = Math.min(length - next, ns);
+		n = Math.max(-next, n);
+		next += n;
+		return n;
+	}
+
+	@Override /* Reader */
+	public void close() {
+		// no-op
+	}
+
+	@Override /* Object */
+	public String toString() {
+		return cs.toString();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/ClassUtils.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ClassUtils.java
new file mode 100644
index 0000000..fce5caf
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -0,0 +1,323 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Class-related utility methods.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class ClassUtils {
+
+	/**
+	 * Given the specified list of objects, return readable names for the class types of the objects.
+	 *
+	 * @param o The objects.
+	 * @return An array of readable class type strings.
+	 */
+	public static ObjectList getReadableClassNames(Object[] o) {
+		ObjectList l = new ObjectList();
+		for (int i = 0; i < o.length; i++)
+			l.add(o[i] == null ? "null" : getReadableClassName(o[i].getClass()));
+		return l;
+	}
+
+	/**
+	 * Shortcut for calling <code><jsm>getReadableClassName</jsm>(c.getName())</code>
+	 *
+	 * @param c The class.
+	 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>.
+	 */
+	public static String getReadableClassName(Class<?> c) {
+		if (c == null)
+			return null;
+		return getReadableClassName(c.getName());
+	}
+
+	/**
+	 * Shortcut for calling <code><jsm>getReadableClassName</jsm>(c.getClass().getName())</code>
+	 *
+	 * @param o The object whose class we want to render.
+	 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>.
+	 */
+	public static String getReadableClassNameForObject(Object o) {
+		if (o == null)
+			return null;
+		return getReadableClassName(o.getClass().getName());
+	}
+
+	/**
+	 * Converts the specified class name to a readable form when class name is a special construct like <js>"[[Z"</js>.
+	 * <p>
+	 * Examples:
+	 * <p class='bcode'>
+	 * 	<jsm>getReadableClassName</jsm>(<js>"java.lang.Object"</js>);  <jc>// Returns "java.lang.Object"</jc>
+	 * 	<jsm>getReadableClassName</jsm>(<js>"boolean"</js>);  <jc>// Returns "boolean"</jc>
+	 * 	<jsm>getReadableClassName</jsm>(<js>"[Z"</js>);  <jc>// Returns "boolean[]"</jc>
+	 * 	<jsm>getReadableClassName</jsm>(<js>"[[Z"</js>);  <jc>// Returns "boolean[][]"</jc>
+	 * 	<jsm>getReadableClassName</jsm>(<js>"[Ljava.lang.Object;"</js>);  <jc>// Returns "java.lang.Object[]"</jc>
+	 * 	<jsm>getReadableClassName</jsm>(<jk>null</jk>);  <jc>// Returns null</jc>
+	 * </p>
+	 *
+	 * @param className The class name.
+	 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>.
+	 */
+	public static String getReadableClassName(String className) {
+		if (className == null)
+			return null;
+		if (! StringUtils.startsWith(className, '['))
+			return className;
+		int depth = 0;
+		for (int i = 0; i < className.length(); i++) {
+			if (className.charAt(i) == '[')
+				depth++;
+			else
+				break;
+		}
+		char type = className.charAt(depth);
+		String c;
+		switch (type) {
+			case 'Z': c = "boolean"; break;
+			case 'B': c = "byte"; break;
+			case 'C': c = "char"; break;
+			case 'D': c = "double"; break;
+			case 'F': c = "float"; break;
+			case 'I': c = "int"; break;
+			case 'J': c = "long"; break;
+			case 'S': c = "short"; break;
+			default: c = className.substring(depth+1, className.length()-1);
+		}
+		StringBuilder sb = new StringBuilder(c.length() + 2*depth).append(c);
+		for (int i = 0; i < depth; i++)
+			sb.append("[]");
+		return sb.toString();
+	}
+
+	/**
+	 * Converts the string generated by {@link #getReadableClassName(Class)} back into a {@link Class}.
+	 * <p>
+	 * Generics are stripped from the string since they cannot be converted to a class.
+	 *
+	 * @param cl The classloader to use to load the class.
+	 * @param name The readable class name.
+	 * @return The class object.
+	 * @throws ClassNotFoundException
+	 */
+	public static Class<?> getClassFromReadableName(ClassLoader cl, String name) throws ClassNotFoundException {
+		return cl.loadClass(name);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if <code>parent</code> is a parent class of <code>child</code>.
+	 *
+	 * @param parent The parent class.
+	 * @param child The child class.
+	 * @param strict If <jk>true</jk> returns <jk>false</jk> if the classes are the same.
+	 * @return <jk>true</jk> if <code>parent</code> is a parent class of <code>child</code>.
+	 */
+	public static boolean isParentClass(Class<?> parent, Class<?> child, boolean strict) {
+		return parent.isAssignableFrom(child) && ((!strict) || ! parent.equals(child));
+	}
+
+	/**
+	 * Returns <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>.
+	 *
+	 * @param parent The parent class.
+	 * @param child The child class.
+	 * @return <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>.
+	 */
+	public static boolean isParentClass(Class<?> parent, Class<?> child) {
+		return isParentClass(parent, child, false);
+	}
+
+	/**
+	 * Comparator for use with {@link TreeMap TreeMaps} with {@link Class} keys.
+	 *
+	 * @author James Bognar (james.bognar@salesforce.com)
+	 */
+	public final static class ClassComparator implements Comparator<Class<?>>, Serializable {
+
+		private static final long serialVersionUID = 1L;
+
+		@Override /* Comparator */
+		public int compare(Class<?> object1, Class<?> object2) {
+			return object1.getName().compareTo(object2.getName());
+		}
+	}
+
+	/**
+	 * Returns the signature of the specified method.
+	 * For no-arg methods, the signature will be a simple string such as <js>"toString"</js>.
+	 * For methods with one or more args, the arguments will be fully-qualified class names (e.g. <js>"append(java.util.StringBuilder,boolean)"</js>)
+	 *
+	 * @param m The methods to get the signature on.
+	 * @return The methods signature.
+	 */
+	public static String getMethodSignature(Method m) {
+		StringBuilder sb = new StringBuilder(m.getName());
+		Class<?>[] pt = m.getParameterTypes();
+		if (pt.length > 0) {
+			sb.append('(');
+			for (int i = 0; i < pt.length; i++) {
+				if (i > 0)
+					sb.append(',');
+				sb.append(getReadableClassName(pt[i]));
+			}
+			sb.append(')');
+		}
+		return sb.toString();
+	}
+
+	private final static Map<Class<?>, Class<?>> pmap1 = new HashMap<Class<?>, Class<?>>(), pmap2 = new HashMap<Class<?>, Class<?>>();
+	static {
+		pmap1.put(boolean.class, Boolean.class);
+		pmap1.put(byte.class, Byte.class);
+		pmap1.put(short.class, Short.class);
+		pmap1.put(char.class, Character.class);
+		pmap1.put(int.class, Integer.class);
+		pmap1.put(long.class, Long.class);
+		pmap1.put(float.class, Float.class);
+		pmap1.put(double.class, Double.class);
+		pmap2.put(Boolean.class, boolean.class);
+		pmap2.put(Byte.class, byte.class);
+		pmap2.put(Short.class, short.class);
+		pmap2.put(Character.class, char.class);
+		pmap2.put(Integer.class, int.class);
+		pmap2.put(Long.class, long.class);
+		pmap2.put(Float.class, float.class);
+		pmap2.put(Double.class, double.class);
+	}
+
+	/**
+	 * If the specified class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>)
+	 * 	returns it's wrapper class (e.g. <code>Integer.<jk>class</jk></code>).
+	 *
+	 * @param c The class.
+	 * @return The wrapper class, or <jk>null</jk> if class is not a primitive.
+	 */
+	public static Class<?> getPrimitiveWrapper(Class<?> c) {
+		return pmap1.get(c);
+	}
+
+	/**
+	 * If the specified class is a primitive wrapper (e.g. <code><jk>Integer</jk>.<jk>class</jk></code>)
+	 * 	returns it's primitive class (e.g. <code>int.<jk>class</jk></code>).
+	 *
+	 * @param c The class.
+	 * @return The primitive class, or <jk>null</jk> if class is not a primitive wrapper.
+	 */
+	public static Class<?> getPrimitiveForWrapper(Class<?> c) {
+		return pmap2.get(c);
+	}
+
+	/**
+	 * If the specified class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>)
+	 * 	returns it's wrapper class (e.g. <code>Integer.<jk>class</jk></code>).
+	 *
+	 * @param c The class.
+	 * @return The wrapper class if it's primitive, or the same class if class is not a primitive.
+	 */
+	public static Class<?> getWrapperIfPrimitive(Class<?> c) {
+		if (! c.isPrimitive())
+			return c;
+		return pmap1.get(c);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified class has the {@link Deprecated @Deprecated} annotation on it.
+	 *
+	 * @param c The class.
+	 * @return <jk>true</jk> if the specified class has the {@link Deprecated @Deprecated} annotation on it.
+	 */
+	public static boolean isNotDeprecated(Class<?> c) {
+		return ! c.isAnnotationPresent(Deprecated.class);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified method has the {@link Deprecated @Deprecated} annotation on it.
+	 *
+	 * @param m The method.
+	 * @return <jk>true</jk> if the specified method has the {@link Deprecated @Deprecated} annotation on it.
+	 */
+	public static boolean isNotDeprecated(Method m) {
+		return ! m.isAnnotationPresent(Deprecated.class);
+
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified constructor has the {@link Deprecated @Deprecated} annotation on it.
+	 *
+	 * @param c The constructor.
+	 * @return <jk>true</jk> if the specified constructor has the {@link Deprecated @Deprecated} annotation on it.
+	 */
+	public static boolean isNotDeprecated(Constructor<?> c) {
+		return ! c.isAnnotationPresent(Deprecated.class);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified class is public.
+	 *
+	 * @param c The class.
+	 * @return <jk>true</jk> if the specified class is public.
+	 */
+	public static boolean isPublic(Class<?> c) {
+		return Modifier.isPublic(c.getModifiers());
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified class is public.
+	 *
+	 * @param c The class.
+	 * @return <jk>true</jk> if the specified class is public.
+	 */
+	public static boolean isStatic(Class<?> c) {
+		return Modifier.isStatic(c.getModifiers());
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified method is public.
+	 *
+	 * @param m The method.
+	 * @return <jk>true</jk> if the specified method is public.
+	 */
+	public static boolean isPublic(Method m) {
+		return Modifier.isPublic(m.getModifiers());
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified method is static.
+	 *
+	 * @param m The method.
+	 * @return <jk>true</jk> if the specified method is static.
+	 */
+	public static boolean isStatic(Method m) {
+		return Modifier.isStatic(m.getModifiers());
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified constructor is public.
+	 *
+	 * @param c The constructor.
+	 * @return <jk>true</jk> if the specified constructor is public.
+	 */
+	public static boolean isPublic(Constructor<?> c) {
+		return Modifier.isPublic(c.getModifiers());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/CollectionUtils.java
new file mode 100644
index 0000000..9c8ad8f
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -0,0 +1,57 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.util.*;
+
+/**
+ * Utility methods for collections.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class CollectionUtils {
+
+	/**
+	 * Reverses the order of a {@link LinkedHashMap}.
+	 *
+	 * @param in The map to reverse the order on.
+	 * @return A new {@link LinkedHashMap} with keys in reverse order.
+	 */
+	public static <K,V> LinkedHashMap<K,V> reverse(LinkedHashMap<K,V> in) {
+		if (in == null)
+			return null;
+		LinkedHashMap<K,V> m = new LinkedHashMap<K,V>();
+
+		// Note:  Entry objects are reusable in an entry set, so we simply can't
+		// create a reversed iteration of that set.
+		List<K> keys = new ArrayList<K>(in.keySet());
+		List<V> values = new ArrayList<V>(in.values());
+		for (int i = in.size()-1; i >= 0; i--)
+			m.put(keys.get(i), values.get(i));
+
+		return m;
+	}
+
+	/**
+	 * Add a value to a list if the value is not null.
+	 *
+	 * @param l The list to add to.
+	 * @param o The element to add.
+	 * @return The same list.
+	 */
+	public static <T> List<T> addIfNotNull(List<T> l, T o) {
+		if (o != null)
+			l.add(o);
+		return l;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
new file mode 100644
index 0000000..6110f2a
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
@@ -0,0 +1,127 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Represents a wrapped {@link BeanMap} where property values can be overridden, removed, or reordered
+ * 	without affecting the underlying bean.
+ * <p>
+ * 	Provides the {@link #filterKeys(List)} method for specifying the keys to keep in the bean map
+ * 		and in what order they should appear.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <T> The class type of the wrapped bean.
+ */
+@SuppressWarnings("hiding")
+public class DelegateBeanMap<T> extends BeanMap<T> {
+
+	private Set<String> keys = Collections.newSetFromMap(new LinkedHashMap<String,Boolean>());
+	private ObjectMap overrideValues = new ObjectMap();
+
+	/**
+	 * Constructor.
+	 *
+	 * @param bean The bean being wrapped.
+	 * @param bc The bean context that created this bean map.
+	 */
+	@SuppressWarnings("unchecked")
+	public
+	DelegateBeanMap(T bean, BeanContext bc) {
+		super(bean, bc.getBeanMeta((Class<T>)bean.getClass()));
+	}
+
+	/**
+	 * Add a key in the next position.
+	 *
+	 * @param key The key to add.
+	 */
+	public void addKey(String key) {
+		this.keys.add(key);
+	}
+
+	@Override /* Map */
+	public Object put(String key, Object val) {
+		this.overrideValues.put(key, val);
+		this.keys.add(key);
+		return null;
+	}
+
+	@Override /* Map */
+	public Object get(Object key) {
+		if (overrideValues.containsKey(key))
+			return overrideValues.get(key);
+		return super.get(key);
+	}
+
+	@Override /* Map */
+	public Set<String> keySet() {
+		return keys;
+	}
+
+	/**
+	 * Remove all but the specified properties from this bean map.
+	 * <p>
+	 * This does not affect the underlying bean.
+	 *
+	 * @param keys The remaining keys in the bean map (in the specified order).
+	 */
+	public void filterKeys(List<String> keys) {
+		this.keys.clear();
+		this.keys.addAll(keys);
+	}
+
+	@Override /* Map */
+	public Object remove(Object key) {
+		keys.remove(key);
+		return null;
+	}
+
+	@Override /* BeanMap */
+	public BeanMeta<T> getMeta() {
+		return new BeanMetaFiltered<T>(super.getMeta(), keys);
+	}
+
+	@Override /* Map */
+	public Set<Entry<String,Object>> entrySet() {
+		Set<Entry<String,Object>> s = Collections.newSetFromMap(new LinkedHashMap<Map.Entry<String,Object>,Boolean>());
+		for (final String key : keys) {
+			BeanMapEntry<T> bme;
+			if (overrideValues.containsKey(key))
+				bme = new BeanMapEntryOverride<T>(this, this.getPropertyMeta(key), overrideValues.get(key));
+			else
+				bme = this.getProperty(key);
+			if (bme == null)
+				throw new BeanRuntimeException(super.getClassMeta().getInnerClass(), "Property ''{0}'' not found on class.", key);
+			s.add(bme);
+		}
+		return s;
+	}
+
+	private class BeanMapEntryOverride<T2> extends BeanMapEntry<T2> {
+		Object value;
+
+		private BeanMapEntryOverride(BeanMap<T2> bm, BeanPropertyMeta<T2> bpm, Object value) {
+			super(bm, bpm);
+			this.value = value;
+		}
+
+		@Override /* Map.Entry */
+		public Object getValue() {
+			return value;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateList.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateList.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateList.java
new file mode 100644
index 0000000..bde29c7
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateList.java
@@ -0,0 +1,44 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Represents a wrapped {@link Collection} where entries in the list can be removed or reordered without
+ * 	affecting the underlying list.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <T> The class type of the wrapped bean.
+ */
+public class DelegateList<T extends Collection<?>> extends ObjectList implements Delegate<T> {
+	private static final long serialVersionUID = 1L;
+
+	private transient ClassMeta<T> classMeta;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param classMeta The metadata object that created this delegate list.
+	 */
+	public DelegateList(ClassMeta<T> classMeta) {
+		this.classMeta = classMeta;
+	}
+
+	@Override /* Delegate */
+	public ClassMeta<T> getClassMeta() {
+		return classMeta;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateMap.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateMap.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateMap.java
new file mode 100644
index 0000000..0b9a4f4
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/DelegateMap.java
@@ -0,0 +1,59 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Represents a wrapped {@link Map} where entries in the map can be removed without
+ * 	affecting the underlying map.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <T> The class type of the wrapped bean.
+ */
+public class DelegateMap<T> extends ObjectMap implements Delegate<T> {
+	private static final long serialVersionUID = 1L;
+
+	private transient ClassMeta<T> classMeta;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param classMeta The metadata object that created this delegate object.
+	 */
+	public DelegateMap(ClassMeta<T> classMeta) {
+		this.classMeta = classMeta;
+	}
+
+	@Override /* Delegate */
+	public ClassMeta<T> getClassMeta() {
+		return classMeta;
+	}
+
+	/**
+	 * Remove all but the specified keys from this map.
+	 * <p>
+	 * This does not affect the underlying map.
+	 *
+	 * @param keys The remaining keys in the map (in the specified order).
+	 */
+	public void filterKeys(List<String> keys) {
+		ObjectMap m2 = new ObjectMap();
+		for (String k : keys)
+			m2.put(k, get(k));
+		this.clear();
+		this.putAll(m2);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/FileUtils.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/FileUtils.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/FileUtils.java
new file mode 100644
index 0000000..c01ab20
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/FileUtils.java
@@ -0,0 +1,134 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.io.*;
+
+/**
+ * File utilities.
+ */
+public class FileUtils {
+
+	/**
+	 * Same as {@link File#mkdirs()} except throws a RuntimeExeption if directory could not be created.
+	 *
+	 * @param f The directory to create.  Must not be <jk>null</jk>.
+	 * @param clean If <jk>true</jk>, deletes the contents of the directory if it already exists.
+	 * @return The same file.
+	 * @throws RuntimeException if directory could not be created.
+	 */
+	public static File mkdirs(File f, boolean clean) {
+		assertFieldNotNull(f, "f");
+		if (f.exists()) {
+			if (clean) {
+				if (! delete(f))
+					throw new RuntimeException("Could not clean directory '"+f.getAbsolutePath()+"'");
+			} else {
+				return f;
+			}
+		}
+		if (! f.mkdirs())
+			throw new RuntimeException("Could not create directory '" + f.getAbsolutePath() + "'");
+		return f;
+	}
+
+	/**
+	 * Same as {@link #mkdirs(String, boolean)} but uses String path.
+	 *
+	 * @param path The path of the directory to create.  Must not be <jk>null</jk>
+	 * @param clean If <jk>true</jk>, deletes the contents of the directory if it already exists.
+	 * @return The directory.
+	 */
+	public static File mkdirs(String path, boolean clean) {
+		assertFieldNotNull(path, "path");
+		return mkdirs(new File(path), clean);
+	}
+
+	/**
+	 * Recursively deletes a file or directory.
+	 *
+	 * @param f The file or directory to delete.
+	 * @return <jk>true</jk> if file or directory was successfully deleted.
+	 */
+	public static boolean delete(File f) {
+		if (f == null)
+			return true;
+		if (f.isDirectory()) {
+			File[] cf = f.listFiles();
+			if (cf != null)
+				for (File c : cf)
+					delete(c);
+		}
+		return f.delete();
+	}
+
+	/**
+	 * Creates a file if it doesn't already exist using {@link File#createNewFile()}.
+	 * Throws a {@link RuntimeException} if the file could not be created.
+	 *
+	 * @param f The file to create.
+	 */
+	public static void create(File f) {
+		if (f.exists())
+			return;
+		try {
+			if (! f.createNewFile())
+				throw new RuntimeException("Could not create file '"+f.getAbsolutePath()+"'");
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * Updates the modified timestamp on the specified file.
+	 * Method ensures that the timestamp changes even if it's been modified within the past millisecond.
+	 *
+	 * @param f The file to modify the modified timestamp on.
+	 */
+	public static void modifyTimestamp(File f) {
+		long lm = f.lastModified();
+		long l = System.currentTimeMillis();
+		if (lm == l)
+			l++;
+		if (! f.setLastModified(l))
+			throw new RuntimeException("Could not modify timestamp on file '"+f.getAbsolutePath()+"'");
+
+		// Linux only gives 1s precision, so set the date 1s into the future.
+		if (lm == f.lastModified()) {
+			l += 1000;
+			if (! f.setLastModified(l))
+				throw new RuntimeException("Could not modify timestamp on file '"+f.getAbsolutePath()+"'");
+		}
+	}
+
+	/**
+	 * Create a temporary file with the specified name.
+	 * <p>
+	 * The name is broken into file name and suffix, and the parts
+	 * are passed to {@link File#createTempFile(String, String)}.
+	 * <p>
+	 * {@link File#deleteOnExit()} is called on the resulting file before being returned by this method.
+	 *
+	 * @param name The file name
+	 * @return A newly-created temporary file.
+	 * @throws IOException
+	 */
+	public static File createTempFile(String name) throws IOException {
+		String[] parts = name.split("\\.");
+		File f = File.createTempFile(parts[0], "." + parts[1]);
+		f.deleteOnExit();
+		return f;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/FilteredMap.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/FilteredMap.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/FilteredMap.java
new file mode 100644
index 0000000..b4ce73b
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/FilteredMap.java
@@ -0,0 +1,96 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.util.*;
+
+/**
+ * Wrapper around a map where the key names are overridden.
+ *
+ * @param <K> The key class type.
+ * @param <V> The value class type.
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class FilteredMap<K,V> extends AbstractMap<K,V> {
+
+	private Map<K,V> innerMap;
+	private Set<Map.Entry<K,V>> entries;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param innerMap The map being wrapped.  Must not be <jk>null</jk>.
+	 * @param keys The keys in the new map.  Must not be <jk>null</jk>.
+	 */
+	public FilteredMap(Map<K,V> innerMap, K[] keys) {
+		assertFieldNotNull(innerMap, "innerMap");
+		assertFieldNotNull(keys, "keys");
+
+		this.innerMap = innerMap;
+			List<Map.Entry<K,V>> l = new ArrayList<Map.Entry<K,V>>(keys.length);
+			for (K k : keys)
+				if (innerMap.containsKey(k))
+					l.add(createEntry(k));
+			entries = new ListSet<Map.Entry<K,V>>(l);
+		}
+
+	private Map.Entry<K,V> createEntry(final K key) {
+		return new Map.Entry<K,V>() {
+
+			@Override /* Map.Entry */
+			public K getKey() {
+				return key;
+			}
+
+			@Override /* Map.Entry */
+			public V getValue() {
+				return innerMap.get(key);
+			}
+
+			@Override /* Map.Entry */
+			public V setValue(V v) {
+				return innerMap.put(key, v);
+			}
+		};
+	}
+
+
+	@Override /* Map */
+	public Set<Map.Entry<K,V>> entrySet() {
+		return entries;
+	}
+
+	/**
+	 * A set with ordered entries (i.e. a List with a Set API).
+	 */
+	private static class ListSet<E> extends AbstractSet<E> {
+
+		private List<E> entries;
+
+		public ListSet(List<E> entries) {
+			this.entries = entries;
+		}
+
+		@Override /* Set */
+		public Iterator<E> iterator() {
+			return entries.iterator();
+		}
+
+		@Override /* Set */
+		public int size() {
+			return entries.size();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java
new file mode 100644
index 0000000..5bdfb80
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/HashCode.java
@@ -0,0 +1,71 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+/**
+ * Utility class for generating integer hash codes.
+ * <p>
+ * 	General usage:
+ * <p class='bcode'>
+ * 	int hashCode = new HashCode().add("foobar").add(myobject).add(123).get();
+ * </p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class HashCode {
+
+	private int hashCode = 1;
+
+	/**
+	 * Create a new HashCode object.
+	 *
+	 * @return A new HashCode object.
+	 */
+	public static final HashCode create() {
+		return new HashCode();
+	}
+
+
+	/**
+	 * Hashes the hashcode of the specified object into this object.
+	 *
+	 * @param o The object whose hashcode will be hashed with this object.
+	 * @return This object (for method chaining).
+	 */
+	public HashCode add(Object o) {
+		add(o == null ? 1 : o.hashCode());
+		return this;
+	}
+
+	/**
+	 * Hashes the hashcode into this object.
+	 * <p>
+	 * The formula is simply <code>hashCode = 31*hashCode + i;</code>
+	 *
+	 * @param i The hashcode to hash into this object's hashcode.
+	 * @return This object (for method chaining).
+	 */
+	public HashCode add(int i) {
+      hashCode = 31*hashCode + i;
+		return this;
+	}
+
+	/**
+	 * Return the calculated hashcode value.
+	 *
+	 * @return The calculated hashcode.
+	 */
+	public int get() {
+      return hashCode;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/IOUtils.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/IOUtils.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/IOUtils.java
new file mode 100644
index 0000000..e37134c
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -0,0 +1,349 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.io.*;
+import java.nio.charset.*;
+
+import org.apache.juneau.utils.*;
+
+/**
+ * Various I/O related utility methods.
+ *
+ * @author jbognar
+ */
+public final class IOUtils {
+
+	/** UTF-8 charset */
+	public static final Charset UTF8 = Charset.forName("UTF-8");
+
+	/**
+	 * Reads the contents of a file into a string.
+	 *
+	 * @param path The path of the file to read using default character encoding.
+	 * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
+	 * @throws IOException If a problem occurred trying to read from the reader.
+	 */
+	public static String readFile(String path) throws IOException {
+		return read(new File(path));
+	}
+
+	/**
+	 * Reads the contents of a file into a string.
+	 *
+	 * @param in The file to read using default character encoding.
+	 * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
+	 * @throws IOException If a problem occurred trying to read from the reader.
+	 */
+	public static String read(File in) throws IOException {
+		if (in == null || ! in.exists())
+			return null;
+		Reader r = new InputStreamReader(new FileInputStream(in), Charset.defaultCharset());
+		return read(r, 0, 1024);
+	}
+
+	/**
+	 * Writes the contents of the specified <code>Reader</code> to the specified file.
+	 *
+	 * @param out The file to write the output to.
+	 * @param in The reader to pipe from.
+	 * @return The number of characters written to the file.
+	 * @throws IOException
+	 */
+	public static int write(File out, Reader in) throws IOException {
+		assertFieldNotNull(out, "out");
+		assertFieldNotNull(in, "in");
+		Writer w = new OutputStreamWriter(new FileOutputStream(out), Charset.defaultCharset());
+		try {
+			return IOPipe.create(in, w).closeOut().run();
+		} finally {
+			w.close();
+		}
+	}
+
+	/**
+	 * Writes the contents of the specified <code>InputStream</code> to the specified file.
+	 *
+	 * @param out The file to write the output to.
+	 * @param in The input stream to pipe from.
+	 * @return The number of characters written to the file.
+	 * @throws IOException
+	 */
+	public static int write(File out, InputStream in) throws IOException {
+		assertFieldNotNull(out, "out");
+		assertFieldNotNull(in, "in");
+		OutputStream os = new FileOutputStream(out);
+		try {
+			return IOPipe.create(in, os).closeOut().run();
+		} finally {
+			os.close();
+		}
+	}
+
+	/**
+	 * Reads the contents of a reader into a string.
+	 *
+	 * @param in The input reader.
+	 * @return The contents of the reader as a string.
+	 * @throws IOException If a problem occurred trying to read from the reader.
+	 */
+	public static String read(Reader in) throws IOException {
+		return read(in, 0, 1024);
+	}
+
+	/**
+	 * Reads the contents of an input stream into a string using the specified charset.
+	 *
+	 * @param in The input stream.
+	 * @param cs The charset of the contents of the input stream.
+	 * @return The contents of the reader as a string.  <jk>null</jk> if input stream was null.
+	 * @throws IOException If a problem occurred trying to read from the input stream.
+	 */
+	public static String read(InputStream in, Charset cs) throws IOException {
+		if (in == null)
+			return null;
+		return read(new InputStreamReader(in, cs));
+	}
+
+	/**
+	 * Reads the contents of an input stream into a string using the system default charset.
+	 *
+	 * @param in The input stream.
+	 * @return The contents of the reader as a string, or <jk>null</jk> if the input stream is null.
+	 * @throws IOException If a problem occurred trying to read from the input stream.
+	 */
+	public static String read(InputStream in) throws IOException {
+		if (in == null)
+			return null;
+		return read(new InputStreamReader(in, Charset.defaultCharset()));
+	}
+
+	/**
+	 * Read the specified input stream into a byte array and closes the stream.
+	 *
+	 * @param in The input stream.
+	 * @param bufferSize The expected size of the buffer.
+	 * @return The contents of the stream as a byte array.
+	 * @throws IOException Thrown by underlying stream.
+	 */
+	public static byte[] readBytes(InputStream in, int bufferSize) throws IOException {
+		if (in == null)
+			return null;
+		ByteArrayOutputStream buff = new ByteArrayOutputStream(bufferSize);
+		int nRead;
+		byte[] b = new byte[Math.min(bufferSize, 8192)];
+
+		try {
+			while ((nRead = in.read(b, 0, b.length)) != -1)
+				  buff.write(b, 0, nRead);
+
+				buff.flush();
+
+				return buff.toByteArray();
+		} finally {
+			in.close();
+		}
+	}
+
+
+	/**
+	 * Reads the specified input into a {@link String} until the end of the input is reached.
+	 * <p>
+	 * 	The {@code Reader} is automatically closed.
+	 * <p>
+	 * 	If the {@code Reader} is not an instance of a {@code BufferedReader}, then it gets wrapped in a {@code BufferedReader}.
+	 *
+	 * @param in The input reader.
+	 * @param length Specify a positive number if the length of the input is known.
+	 * @param bufferSize Specify the buffer size to use.
+	 * @return The contents of the reader as a string.  <jk>null</jk> if reader was null.
+	 * @throws IOException If a problem occurred trying to read from the reader.
+	 */
+	public static String read(Reader in, int length, int bufferSize) throws IOException {
+		if (in == null)
+			return null;
+		length = (length <= 0 ? bufferSize : length);
+		StringBuilder sb = new StringBuilder(length); // Assume they're ASCII characters.
+		try {
+			char[] buf = new char[Math.min(bufferSize, length)];
+			int i = 0;
+			while ((i = in.read(buf)) != -1)
+				sb.append(buf, 0, i);
+			return sb.toString();
+		} finally {
+			in.close();
+		}
+	}
+
+	/**
+	 * Pipes the contents of the specified reader into the writer.
+	 * The reader is closed, the writer is not.
+	 *
+	 * @param in The reader to pipe from.
+	 * @param out The writer to pipe to.
+	 * @throws IOException
+	 */
+   public static void pipe(Reader in, Writer out) throws IOException {
+		assertFieldNotNull(out, "out");
+		assertFieldNotNull(in, "in");
+      IOPipe.create(in, out).run();
+   }
+
+	/**
+	 * Wraps the specified reader in a buffered reader.
+	 *
+	 * @param r The reader being wrapped.
+	 * @return The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already
+	 * 	a buffered reader.
+	 */
+	public static Reader getBufferedReader(Reader r) {
+		if (r instanceof BufferedReader || r instanceof StringReader)
+			return r;
+		return new BufferedReader(r);
+	}
+
+	/**
+	 * Counts the number of bytes in the input stream and then closes the stream.
+	 *
+	 * @param is The input stream to read from.
+	 * @return The number of bytes read.
+	 * @throws IOException
+	 */
+	public static long count(InputStream is) throws IOException {
+		assertFieldNotNull(is, "is");
+		long c = 0;
+		long i;
+		try {
+			while ((i = is.skip(1024)) != 0)
+				c += i;
+		} finally {
+			is.close();
+		}
+		return c;
+	}
+
+	/**
+	 * Counts the number of characters in the reader and then closes the reader.
+	 *
+	 * @param r The reader to read from.
+	 * @return The number of characters read.
+	 * @throws IOException
+	 */
+	public static long count(Reader r) throws IOException {
+		assertFieldNotNull(r, "r");
+		long c = 0;
+		long i;
+		try {
+			while ((i = r.skip(1024)) != 0)
+				c += i;
+		} finally {
+			r.close();
+		}
+		return c;
+	}
+
+	/**
+	 * Given the specified <js>"Content-Length"</js> header value, return an appropriate buffer size.
+	 * The maximum buffer size is 1MB.
+	 *
+	 * @param contentLength The value of the <js>"Content-Length"</js> header.
+	 * @return The appropriate buffer size.
+	 */
+	public static int getBufferSize(String contentLength) {
+		try {
+			if (! StringUtils.isEmpty(contentLength)) {
+				long l = Long.decode(contentLength);
+				if (l > 1048576)
+					return 1048576;
+				if (l <= 0)
+					return 8192;
+				return (int)l;
+			}
+		} catch (Exception e) {
+			return 8192;
+		}
+		return 8192;
+	}
+
+	/**
+	 * Close input stream and ignore any exceptions.
+	 * No-op if input stream is <jk>null</jk>.
+	 *
+	 * @param is The input stream to close.
+	 */
+	public static void closeQuietly(InputStream is) {
+		try {
+			if (is != null)
+				is.close();
+		} catch (IOException e) {}
+	}
+
+	/**
+	 * Close output stream and ignore any exceptions.
+	 * No-op if output stream is <jk>null</jk>.
+	 *
+	 * @param os The output stream to close.
+	 */
+	public static void closeQuietly(OutputStream os) {
+		try {
+			if (os != null)
+				os.close();
+		} catch (IOException e) {}
+	}
+
+	/**
+	 * Close reader and ignore any exceptions.
+	 * No-op if reader is <jk>null</jk>.
+	 *
+	 * @param r The reader to close.
+	 */
+	public static void closeQuietly(Reader r) {
+		try {
+			if (r != null)
+				r.close();
+		} catch (IOException e) {}
+	}
+
+	/**
+	 * Close writer and ignore any exceptions.
+	 * No-op if writer is <jk>null</jk>.
+	 *
+	 * @param w The writer to close.
+	 */
+	public static void closeQuietly(Writer w) {
+		try {
+			if (w != null)
+				w.close();
+		} catch (IOException e) {}
+	}
+
+	/**
+	 * Quietly close all specified input streams, output streams, readers, and writers.
+	 *
+	 * @param o The list of all objects to quietly close.
+	 */
+	public static void closeQuietly(Object...o) {
+		for (Object o2 : o) {
+			if (o2 instanceof InputStream)
+				closeQuietly((InputStream)o2);
+			if (o2 instanceof OutputStream)
+				closeQuietly((OutputStream)o2);
+			if (o2 instanceof Reader)
+				closeQuietly((Reader)o2);
+			if (o2 instanceof Writer)
+				closeQuietly((Writer)o2);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/IdentityList.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/IdentityList.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/IdentityList.java
new file mode 100644
index 0000000..2d9345d
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/IdentityList.java
@@ -0,0 +1,49 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import java.util.*;
+
+/**
+ * Combination of a {@link LinkedList} and <code>IdentitySet</code>.
+ * <ul class='spaced-list'>
+ * 	<li>Duplicate objects (by identity) will be skipped during insertion.
+ * 	<li>Order of insertion maintained.
+ * </ul>
+ * <p>
+ * 	Note:  This class is NOT thread safe, and is intended for use on small lists.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <T> Entry type.
+ */
+public class IdentityList<T> extends LinkedList<T> {
+
+	private static final long serialVersionUID = 1L;
+
+	@Override /* List */
+	public boolean add(T t) {
+		for (T t2 : this)
+			if (t2 == t)
+				return false;
+		super.add(t);
+		return true;
+	}
+
+	@Override /* List */
+	public boolean contains(Object t) {
+		for (T t2 : this)
+			if (t2 == t)
+				return true;
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/JuneauLogger.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/JuneauLogger.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/JuneauLogger.java
new file mode 100644
index 0000000..4bd90ef
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/JuneauLogger.java
@@ -0,0 +1,295 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static java.util.logging.Level.*;
+
+import java.text.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.logging.*;
+
+import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transforms.*;
+
+/**
+ * Wraps and extends the {@link java.util.logging.Logger} class to provide some additional convenience methods.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class JuneauLogger extends java.util.logging.Logger {
+
+	private static final WriterSerializer serializer = JsonSerializer.DEFAULT_LAX.clone()
+		.addTransforms(
+			CalendarTransform.ISO8601DTZ.class,
+			DateTransform.ISO8601DTZ.class,
+			EnumerationTransform.class,
+			IteratorTransform.class
+		);
+
+	private static final ConcurrentHashMap<Class<?>,String> rbMap = new ConcurrentHashMap<Class<?>,String>();
+
+	private final ResourceBundle rb;
+	private final java.util.logging.Logger innerLogger;
+
+	/**
+	 * Get logger for specified class.
+	 *
+	 * @param forClass The class to create a logger for.
+	 * @return A new <l>Logger</l>.
+	 */
+	public static JuneauLogger getLogger(Class<?> forClass) {
+		return new JuneauLogger(java.util.logging.Logger.getLogger(forClass.getName()));
+	}
+
+	/**
+	 * Get logger for specified class using the specified resource bundle name.
+	 *
+	 * @param forClass The class to create a logger for.
+	 * @param resourceBundleName The name of the resource bundle.
+	 * 	Can be any of the following formats:
+	 * 	<ol>
+	 * 		<li>An absolute path.  E.g. <js>"com/ibm/nls/Messages"</js>.
+	 * 		<li>A path relative to the package of the class.  E.g. <js>"nls/Messages"</js>.
+	 * 	</ol>
+	 * 	Both <js>'.'</js> and <js>'/'</js> can be used as path delimiters.
+	 * @return A new <l>Logger</l>.
+	 */
+	public static JuneauLogger getLogger(Class<?> forClass, String resourceBundleName) {
+		return new JuneauLogger(java.util.logging.Logger.getLogger(forClass.getName(), resolveResourceBundleName(forClass, resourceBundleName)));
+	}
+
+	/**
+	 * Get logger with specified name using the specified resource bundle name.
+	 *
+	 * @param name The name of the logger to use.
+	 * @param resourceBundleName The name of the resource bundle.
+	 * 	Can be any of the following formats:
+	 * 	<ol>
+	 * 		<li>An absolute path.  E.g. <js>"com/ibm/nls/Messages"</js>.
+	 * 		<li>A path relative to the package of the class.  E.g. <js>"nls/Messages"</js>.
+	 * 	</ol>
+	 * 	Both <js>'.'</js> and <js>'/'</js> can be used as path delimiters.
+	 * @return A new <l>Logger</l>.
+	 */
+	public static JuneauLogger getLogger(String name, String resourceBundleName) {
+		return new JuneauLogger(java.util.logging.Logger.getLogger(name, resourceBundleName));
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param innerLogger The wrapped logger.
+	 */
+	protected JuneauLogger(java.util.logging.Logger innerLogger) {
+		super(innerLogger.getName(), innerLogger.getResourceBundleName());
+		this.innerLogger = innerLogger;
+		this.rb = getResourceBundle();
+	}
+
+	/**
+     * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#SEVERE} level.
+     *
+     * @param msg The message to log.
+     * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void severe(String msg, Object...args) {
+		if (isLoggable(SEVERE))
+			log(SEVERE, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#WARNING} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void warning(String msg, Object...args) {
+		if (isLoggable(WARNING))
+			log(WARNING, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#INFO} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void info(String msg, Object...args) {
+		if (isLoggable(INFO))
+			log(INFO, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#CONFIG} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void config(String msg, Object...args) {
+		if (isLoggable(CONFIG))
+			log(CONFIG, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINE} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void fine(String msg, Object...args) {
+		if (isLoggable(FINE))
+			log(FINE, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINER} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void finer(String msg, Object...args) {
+		if (isLoggable(FINER))
+			log(FINER, msg, args);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINEST} level.
+    *
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void finest(String msg, Object...args) {
+		if (isLoggable(FINEST))
+			log(FINEST, msg, args);
+	}
+
+	/**
+    * Logs an exception as {@link Level#SEVERE} level.
+	 *
+	 * @param t The Throwable object to log.
+	 */
+	public void severe(Throwable t) {
+		if (isLoggable(SEVERE))
+			log(SEVERE, t.getLocalizedMessage(), t);
+	}
+
+	/**
+    * Logs an exception as {@link Level#WARNING} level.
+	 *
+	 * @param t The Throwable object to log.
+	 */
+	public void warning(Throwable t) {
+		if (isLoggable(WARNING))
+			log(WARNING, t.getLocalizedMessage(), t);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#SEVERE} level.
+	 *
+	 * @param t The Throwable object associated with the event that needs to be logged.
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void severe(Throwable t, String msg, Object...args) {
+		if (isLoggable(SEVERE))
+			log(SEVERE, getMessage(msg, args), t);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#WARNING} level.
+	 *
+	 * @param t The Throwable object associated with the event that needs to be logged.
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void warning(Throwable t, String msg, Object...args) {
+		if (isLoggable(WARNING))
+			log(WARNING, getMessage(msg, args), t);
+	}
+
+	/**
+    * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#INFO} level.
+	 *
+	 * @param t The Throwable object associated with the event that needs to be logged.
+    * @param msg The message to log.
+    * @param args The {@link MessageFormat}-style arguments.
+	 */
+	public void info(Throwable t, String msg, Object...args) {
+		if (isLoggable(INFO))
+			log(INFO, getMessage(msg, args), t);
+	}
+
+   @Override /* Logger */
+	public void log(LogRecord record) {
+   	innerLogger.log(record);
+   }
+
+   @Override /* Logger */
+	public boolean isLoggable(Level level) {
+   	return innerLogger.isLoggable(level);
+   }
+
+	/**
+    * Similar to {@link #log(Level, String, Object[])}, except arguments are converted to objects
+    * 	that are serialized using the {@link JsonSerializer#toStringObject(Object)} method.
+    * This allows arbitrary POJOs to be serialized as message parameters.
+	 *
+	 * @param level The level of the given message.
+    * @param msg The message to log.
+    * @param args The POJO arguments.
+	 */
+	public void logObjects(Level level, String msg, Object...args) {
+		if (isLoggable(level)) {
+			for (int i = 0; i < args.length; i++)
+				args[i] = serializer.toStringObject(args[i]);
+			log(level, msg, args);
+		}
+	}
+
+	private String getMessage(String msg, Object...args) {
+		if (args.length == 0)
+			return msg;
+		if (rb != null && rb.containsKey(msg))
+			msg = rb.getString(msg);
+		return MessageFormat.format(msg, args);
+	}
+
+	private static String resolveResourceBundleName(Class<?> forClass, String path) {
+		if (StringUtils.isEmpty(path))
+			return null;
+		String rb = rbMap.get(forClass);
+		if (rb == null) {
+			path = path.replace('/', '.');
+			if (path.startsWith("."))
+				path = path.substring(1);
+			ClassLoader cl = forClass.getClassLoader();
+			try {
+				ResourceBundle.getBundle(path, Locale.getDefault(), cl);
+				rbMap.putIfAbsent(forClass, path);
+			} catch (MissingResourceException e) {
+				try {
+					path = forClass.getPackage().getName() + '.' + path;
+					ResourceBundle.getBundle(path, Locale.getDefault(), cl);
+					rbMap.putIfAbsent(forClass, path);
+				} catch (MissingResourceException e2) {
+					rbMap.putIfAbsent(forClass, "");
+				}
+			}
+			rb = rbMap.get(forClass);
+		}
+		return ("".equals(rb) ? null : rb);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/KeywordSet.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/KeywordSet.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/KeywordSet.java
new file mode 100644
index 0000000..c23a912
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/KeywordSet.java
@@ -0,0 +1,90 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+/**
+ * Stores a set of language keywords for quick lookup.
+ * <p>
+ * Keywords must be:
+ * <ul class='spaced-list'>
+ * 	<li>2 or more characters in length.
+ * 	<li>Lowercase ASCII.
+ * </ul>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class KeywordSet {
+	final char[][][][] store;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param keywords The list of keywords.
+	 */
+	public KeywordSet(String... keywords) {
+		this.store = new char[26][][][];
+
+		for (String keyword : keywords) {
+			if (keyword.length() < 2)
+				illegalArg("Invalid keyword '{0}' passed to KeywordStore.", keyword);
+			int c0 = keyword.charAt(0) - 'a';
+			int c1 = keyword.charAt(1) - 'a';
+			if (c0 < 0 || c0 > 25 || c1 < 0 || c1 > 25)
+				illegalArg("Invalid keyword '{0}' passed to KeywordStore.", keyword);
+			if (this.store[c0] == null)
+				this.store[c0] = new char[26][][];
+			char[][][] x1 = this.store[c0];
+			char[][] x2;
+			if (x1[c1] == null)
+				x2 = new char[1][];
+			else {
+				x2 = new char[x1[c1].length+1][];
+				System.arraycopy(x1[c1], 0, x2, 0, x1[c1].length);
+			}
+			x2[x2.length-1] = keyword.toCharArray();
+			x1[c1] = x2;
+		}
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified string exists in this store.
+	 *
+	 * @param s The string to check.
+	 * @return <jk>true</jk> if the specified string exists in this store.
+	 */
+	public boolean contains(String s) {
+		if (s == null || s.length() < 2)
+			return false;
+		int c0 = s.charAt(0) - 'a', c1 = s.charAt(1) - 'a';
+		if (c0 < 0 || c0 > 25 || c1 < 0 || c1 > 25)
+			return false;
+		char[][][] x1 = store[c0];
+		if (x1 == null)
+			return false;
+		char[][] x2 = x1[c1];
+		if (x2 == null)
+			return false;
+		for (int i = 0; i < x2.length; i++) {
+			char[] keyword = x2[i];
+			if (keyword.length == s.length()) {
+				for (int j = 0; j < keyword.length; j++)
+					if (keyword[j] != s.charAt(j))
+						return false;
+				return true;
+			}
+		}
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiIterable.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiIterable.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiIterable.java
new file mode 100644
index 0000000..19f5078
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiIterable.java
@@ -0,0 +1,78 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.util.*;
+
+/**
+ * Utility class for defining an iterator over one or more iterables.
+ * @param <E> The element class type.
+ */
+public class MultiIterable<E> implements Iterable<E> {
+
+	final List<Iterator<E>> iterators = new LinkedList<Iterator<E>>();
+
+	/**
+	 * Constructor.
+	 *
+	 * @param iterators The list of iterators to iterate over.
+	 */
+	public MultiIterable(Iterator<E>...iterators) {
+		for (Iterator<E> i : iterators)
+			append(i);
+	}
+
+	/**
+	 * Appends the specified iterator to this list of iterators.
+	 *
+	 * @param iterator The iterator to append.
+	 * @return This object (for method chaining).
+	 */
+	public MultiIterable<E> append(Iterator<E> iterator) {
+		assertFieldNotNull(iterator, "iterator");
+		this.iterators.add(iterator);
+		return this;
+	}
+
+	@Override /* Iterable */
+	public Iterator<E> iterator() {
+		return new Iterator<E>() {
+			Iterator<Iterator<E>> i1 = iterators.iterator();
+			Iterator<E> i2 = i1.hasNext() ? i1.next() : null;
+
+			@Override /* Iterator */
+			public boolean hasNext() {
+				while (i2 != null && ! i2.hasNext())
+					i2 = (i1.hasNext() ? i1.next() : null);
+				return (i2 != null);
+			}
+
+			@Override /* Iterator */
+			public E next() {
+				hasNext();
+				if (i2 == null)
+					throw new NoSuchElementException();
+				return i2.next();
+			}
+
+			@Override /* Iterator */
+			public void remove() {
+				if (i2 == null)
+					throw new NoSuchElementException();
+				i2.remove();
+			}
+		};
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiSet.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiSet.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiSet.java
new file mode 100644
index 0000000..49b84f0
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/MultiSet.java
@@ -0,0 +1,111 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.util.*;
+
+/**
+ * Encapsulates multiple collections so they can be iterated over as if they
+ * were all part of the same collection.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <E> The object type of this set.
+ */
+public class MultiSet<E> extends AbstractSet<E> {
+
+	/** Inner collections. */
+	private List<Collection<E>> l = new ArrayList<Collection<E>>();
+
+	/**
+	 * Create a new Set that consists as a coalesced set of the specified collections.
+	 *
+	 * @param c Zero or more collections to add to this set.
+	 */
+	public MultiSet(Collection<E>...c) {
+		for (Collection<E> cc : c)
+			append(cc);
+	}
+
+	/**
+	 * Appends the specified collection to this set of collections.
+	 *
+	 * @param c The collection to append to this set of collections.
+	 * @return This object (for method chaining).
+	 */
+	public MultiSet<E> append(Collection<E> c) {
+		assertFieldNotNull(c, "c");
+		l.add(c);
+		return this;
+	}
+
+	/**
+	 * Iterates over all entries in all collections.
+	 */
+	@Override /* Set */
+	public Iterator<E> iterator() {
+		return new Iterator<E>() {
+			int i = 0;
+			Iterator<E> i2 = (l.size() > 0 ? l.get(i++).iterator() : null);
+
+			@Override /* Iterator */
+			public boolean hasNext() {
+				if (i2 == null)
+					return false;
+				if (i2.hasNext())
+					return true;
+				for (int j = i; j < l.size(); j++)
+					if (l.get(j).size() > 0)
+						return true;
+				return false;
+			}
+
+			@Override /* Iterator */
+			public E next() {
+				if (i2 == null)
+					throw new NoSuchElementException();
+				while (! i2.hasNext()) {
+					if (i >= l.size())
+						throw new NoSuchElementException();
+					i2 = l.get(i++).iterator();
+				}
+				return i2.next();
+			}
+
+			@Override /* Iterator */
+			public void remove() {
+				if (i2 == null)
+					throw new NoSuchElementException();
+				i2.remove();
+			}
+		};
+	}
+
+	/**
+	 * Enumerates over all entries in all collections.
+	 *
+	 * @return An enumeration wrapper around this set.
+	 */
+	public Enumeration<E> enumerator() {
+		return Collections.enumeration(this);
+	}
+
+	@Override /* Set */
+	public int size() {
+		int i = 0;
+		for (Collection<E> c : l)
+			i += c.size();
+		return i;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/internal/ReflectionUtils.java b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
new file mode 100644
index 0000000..0936d91
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
@@ -0,0 +1,163 @@
+/***************************************************************************************************************************
+ * 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.juneau.internal;
+
+import static org.apache.juneau.internal.CollectionUtils.*;
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.util.*;
+
+/**
+ * Reflection utilities.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class ReflectionUtils {
+
+	/**
+	 * Similar to {@link Class#getAnnotation(Class)} except also searches annotations on interfaces.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class.
+	 * @param c The annotated class.
+	 * @return The annotation, or <jk>null</jk> if not found.
+	 */
+	public static <T extends Annotation> T getAnnotation(Class<T> a, Class<?> c) {
+		if (c == null)
+			return null;
+
+		T t = getDeclaredAnnotation(a, c);
+		if (t != null)
+			return t;
+
+		t = getAnnotation(a, c.getSuperclass());
+		if (t != null)
+			return t;
+
+		for (Class<?> c2 : c.getInterfaces()) {
+			t = getAnnotation(a, c2);
+			if (t != null)
+				return t;
+		}
+		return null;
+	}
+
+	/**
+	 * Returns the specified annotation only if it's been declared on the specified class.
+	 * <p>
+	 * 	More efficient than calling {@link Class#getAnnotation(Class)} since it doesn't
+	 * 	recursively look for the class up the parent chain.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class.
+	 * @param c The annotated class.
+	 * @return The annotation, or <jk>null</jk> if not found.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T extends Annotation> T getDeclaredAnnotation(Class<T> a, Class<?> c) {
+		for (Annotation a2 : c.getDeclaredAnnotations())
+			if (a2.annotationType() == a)
+				return (T)a2;
+		return null;
+	}
+
+	/**
+	 * Returns all instances of the specified annotation on the specified class.
+	 * <p>
+	 * Searches all superclasses and superinterfaces.
+	 * <p>
+	 * Results are ordered child-to-parent.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class type.
+	 * @param c The class being searched.
+	 * @return The found matches, or an empty array if annotation was not found.
+	 */
+	public static <T extends Annotation> List<T> findAnnotations(Class<T> a, Class<?> c) {
+		List<T> l = new LinkedList<T>();
+		appendAnnotations(a, c, l);
+		return l;
+	}
+
+	/**
+	 * Sames as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
+	 * with the keys being the class on which the annotation was found.
+	 * <p>
+	 * Results are ordered child-to-parent.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class type.
+	 * @param c The class being searched.
+	 * @return The found matches, or an empty array if annotation was not found.
+	 */
+	public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMap(Class<T> a, Class<?> c) {
+		LinkedHashMap<Class<?>,T> m = new LinkedHashMap<Class<?>,T>();
+		findAnnotationsMap(a, c, m);
+		return m;
+	}
+
+	private static <T extends Annotation> void findAnnotationsMap(Class<T> a, Class<?> c, Map<Class<?>,T> m) {
+		if (c == null)
+			return;
+
+		T t = getDeclaredAnnotation(a, c);
+		if (t != null)
+			m.put(c, t);
+
+		findAnnotationsMap(a, c.getSuperclass(), m);
+
+		for (Class<?> c2 : c.getInterfaces())
+			findAnnotationsMap(a, c2, m);
+	}
+
+	/**
+	 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified list.
+	 *
+	 * @param a The annotation.
+	 * @param c The class.
+	 * @param l The list of annotations.
+	 */
+	public static <T extends Annotation> void appendAnnotations(Class<T> a, Class<?> c, List<T> l) {
+		if (c == null)
+			return;
+
+		addIfNotNull(l, getDeclaredAnnotation(a, c));
+
+		if (c.getPackage() != null)
+			addIfNotNull(l, c.getPackage().getAnnotation(a));
+
+		appendAnnotations(a, c.getSuperclass(), l);
+
+		for (Class<?> c2 : c.getInterfaces())
+			appendAnnotations(a, c2, l);
+	}
+
+	/**
+	 * Similar to {@link Class#getResourceAsStream(String)} except looks up the
+	 * parent hierarchy for the existence of the specified resource.
+	 *
+	 * @param c The class to return the resource on.
+	 * @param name The resource name.
+	 * @return An input stream on the specified resource, or <jk>null</jk> if the resource could not be found.
+	 */
+	public static InputStream getResource(Class<?> c, String name) {
+		while (c != null) {
+			InputStream is = c.getResourceAsStream(name);
+			if (is != null)
+				return is;
+			c = c.getSuperclass();
+		}
+		return null;
+	}
+}