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;
+ }
+}