Repository: parquet-mr
Updated Branches:
  refs/heads/master df9f8d869 -> ddbeb4dd1


http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java
----------------------------------------------------------------------
diff --git 
a/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java 
b/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java
new file mode 100644
index 0000000..769f31c
--- /dev/null
+++ b/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java
@@ -0,0 +1,520 @@
+/*
+ * 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.parquet.util;
+
+import org.apache.parquet.Preconditions;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+
+import static org.apache.parquet.Exceptions.throwIfInstance;
+
+public class DynMethods {
+
+  /**
+   * Convenience wrapper class around {@link java.lang.reflect.Method}.
+   *
+   * Allows callers to invoke the wrapped method with all Exceptions wrapped by
+   * RuntimeException, or with a single Exception catch block.
+   */
+  public static class UnboundMethod {
+
+    private final Method method;
+    private final String name;
+    private final int argLength;
+
+    UnboundMethod(Method method, String name) {
+      this.method = method;
+      this.name = name;
+      this.argLength = (method == null || method.isVarArgs()) ? -1 :
+          method.getParameterTypes().length;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <R> R invokeChecked(Object target, Object... args) throws Exception 
{
+      try {
+        if (argLength < 0) {
+          return (R) method.invoke(target, args);
+        } else {
+          return (R) method.invoke(target, Arrays.copyOfRange(args, 0, 
argLength));
+        }
+
+      } catch (InvocationTargetException e) {
+        throwIfInstance(e.getCause(), Exception.class);
+        throwIfInstance(e.getCause(), RuntimeException.class);
+        throw new RuntimeException(e.getCause());
+      }
+    }
+
+    public <R> R invoke(Object target, Object... args) {
+      try {
+        return this.<R>invokeChecked(target, args);
+      } catch (Exception e) {
+        throwIfInstance(e, RuntimeException.class);
+        throw new RuntimeException(e);
+      }
+    }
+
+    /**
+     * Returns this method as a BoundMethod for the given receiver.
+     *
+     * @param receiver an Object to receive the method invocation
+     * @return a {@link BoundMethod} for this method and the receiver
+     * @throws IllegalStateException if the method is static
+     * @throws IllegalArgumentException if the receiver's class is incompatible
+     */
+    public BoundMethod bind(Object receiver) {
+      Preconditions.checkState(!isStatic(),
+          "Cannot bind static method " + method.toGenericString());
+      Preconditions.checkArgument(
+          method.getDeclaringClass().isAssignableFrom(receiver.getClass()),
+          "Cannot bind " + method.toGenericString() + " to instance of " +
+              receiver.getClass());
+
+      return new BoundMethod(this, receiver);
+    }
+
+    /**
+     * @return whether the method is a static method
+     */
+    public boolean isStatic() {
+      return Modifier.isStatic(method.getModifiers());
+    }
+
+    /**
+     * @return whether the method is a noop
+     */
+    public boolean isNoop() {
+      return this == NOOP;
+    }
+
+    /**
+     * Returns this method as a StaticMethod.
+     *
+     * @return a {@link StaticMethod} for this method
+     * @throws IllegalStateException if the method is not static
+     */
+    public StaticMethod asStatic() {
+      Preconditions.checkState(isStatic(), "Method is not static");
+      return new StaticMethod(this);
+    }
+
+    public String toString() {
+      return "DynMethods.UnboundMethod(name=" + name +" method=" +
+          method.toGenericString() + ")";
+    }
+
+    /**
+     * Singleton {@link UnboundMethod}, performs no operation and returns null.
+     */
+    private static UnboundMethod NOOP = new UnboundMethod(null, "NOOP") {
+      @Override
+      public <R> R invokeChecked(Object target, Object... args) throws 
Exception {
+        return null;
+      }
+
+      @Override
+      public BoundMethod bind(Object receiver) {
+        return new BoundMethod(this, receiver);
+      }
+
+      @Override
+      public StaticMethod asStatic() {
+        return new StaticMethod(this);
+      }
+
+      @Override
+      public boolean isStatic() {
+        return true;
+      }
+
+      @Override
+      public String toString() {
+        return "DynMethods.UnboundMethod(NOOP)";
+      }
+    };
+  }
+
+  public static class BoundMethod {
+    private final UnboundMethod method;
+    private final Object receiver;
+
+    private BoundMethod(UnboundMethod method, Object receiver) {
+      this.method = method;
+      this.receiver = receiver;
+    }
+
+    public <R> R invokeChecked(Object... args) throws Exception {
+      return method.invokeChecked(receiver, args);
+    }
+
+    public <R> R invoke(Object... args) {
+      return method.invoke(receiver, args);
+    }
+  }
+
+  public static class StaticMethod {
+    private final UnboundMethod method;
+
+    private StaticMethod(UnboundMethod method) {
+      this.method = method;
+    }
+
+    public <R> R invokeChecked(Object... args) throws Exception {
+      return method.invokeChecked(null, args);
+    }
+
+    public <R> R invoke(Object... args) {
+      return method.invoke(null, args);
+    }
+  }
+
+  public static class Builder {
+    private final String name;
+    private ClassLoader loader = 
Thread.currentThread().getContextClassLoader();
+    private UnboundMethod method = null;
+
+    public Builder(String methodName) {
+      this.name = methodName;
+    }
+
+    /**
+     * Set the {@link ClassLoader} used to lookup classes by name.
+     * <p>
+     * If not set, the current thread's ClassLoader is used.
+     *
+     * @param loader a ClassLoader
+     * @return this Builder for method chaining
+     */
+    public Builder loader(ClassLoader loader) {
+      this.loader = loader;
+      return this;
+    }
+
+    /**
+     * If no implementation has been found, adds a NOOP method.
+     *
+     * Note: calls to impl will not match after this method is called!
+     *
+     * @return this Builder for method chaining
+     */
+    public Builder orNoop() {
+      if (method == null) {
+        this.method = UnboundMethod.NOOP;
+      }
+      return this;
+    }
+
+    /**
+     * Checks for an implementation, first finding the given class by name.
+     *
+     * @param className name of a class
+     * @param methodName name of a method (different from constructor)
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder impl(String className, String methodName, Class<?>... 
argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        Class<?> targetClass = Class.forName(className, true, loader);
+        impl(targetClass, methodName, argClasses);
+      } catch (ClassNotFoundException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    /**
+     * Checks for an implementation, first finding the given class by name.
+     *
+     * The name passed to the constructor is the method name used.
+     *
+     * @param className name of a class
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder impl(String className, Class<?>... argClasses) {
+      impl(className, name, argClasses);
+      return this;
+    }
+
+    /**
+     * Checks for a method implementation.
+     *
+     * @param methodName name of a method (different from constructor)
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder impl(Class<?> targetClass, String methodName, Class<?>... 
argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        this.method = new UnboundMethod(
+            targetClass.getMethod(methodName, argClasses), name);
+      } catch (NoSuchMethodException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    /**
+     * Checks for a method implementation.
+     *
+     * The name passed to the constructor is the method name used.
+     *
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder impl(Class<?> targetClass, Class<?>... argClasses) {
+      impl(targetClass, name, argClasses);
+      return this;
+    }
+
+    public Builder ctorImpl(Class<?> targetClass, Class<?>... argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        this.method = new DynConstructors.Builder()
+            .impl(targetClass, argClasses)
+            .buildChecked();
+      } catch (NoSuchMethodException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    public Builder ctorImpl(String className, Class<?>... argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        this.method = new DynConstructors.Builder()
+            .impl(className, argClasses)
+            .buildChecked();
+      } catch (NoSuchMethodException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    /**
+     * Checks for an implementation, first finding the given class by name.
+     *
+     * @param className name of a class
+     * @param methodName name of a method (different from constructor)
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder hiddenImpl(String className, String methodName, Class<?>... 
argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        Class<?> targetClass = Class.forName(className, true, loader);
+        hiddenImpl(targetClass, methodName, argClasses);
+      } catch (ClassNotFoundException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    /**
+     * Checks for an implementation, first finding the given class by name.
+     *
+     * The name passed to the constructor is the method name used.
+     *
+     * @param className name of a class
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder hiddenImpl(String className, Class<?>... argClasses) {
+      hiddenImpl(className, name, argClasses);
+      return this;
+    }
+
+    /**
+     * Checks for a method implementation.
+     *
+     * @param methodName name of a method (different from constructor)
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder hiddenImpl(Class<?> targetClass, String methodName, 
Class<?>... argClasses) {
+      // don't do any work if an implementation has been found
+      if (method != null) {
+        return this;
+      }
+
+      try {
+        Method hidden = targetClass.getDeclaredMethod(methodName, argClasses);
+        AccessController.doPrivileged(new MakeAccessible(hidden));
+        this.method = new UnboundMethod(hidden, name);
+      } catch (SecurityException e) {
+        // unusable
+      } catch (NoSuchMethodException e) {
+        // not the right implementation
+      }
+      return this;
+    }
+
+    /**
+     * Checks for a method implementation.
+     *
+     * The name passed to the constructor is the method name used.
+     *
+     * @param argClasses argument classes for the method
+     * @return this Builder for method chaining
+     * @see {@link java.lang.Class#forName(String)}
+     * @see {@link java.lang.Class#getMethod(String, Class[])}
+     */
+    public Builder hiddenImpl(Class<?> targetClass, Class<?>... argClasses) {
+      hiddenImpl(targetClass, name, argClasses);
+      return this;
+    }
+
+    /**
+     * Returns the first valid implementation as a UnboundMethod or throws a
+     * NoSuchMethodException if there is none.
+     *
+     * @return a {@link UnboundMethod} with a valid implementation
+     * @throws NoSuchMethodException if no implementation was found
+     */
+    public UnboundMethod buildChecked() throws NoSuchMethodException {
+      if (method != null) {
+        return method;
+      } else {
+        throw new NoSuchMethodException("Cannot find method: " + name);
+      }
+    }
+
+    /**
+     * Returns the first valid implementation as a UnboundMethod or throws a
+     * RuntimeError if there is none.
+     *
+     * @return a {@link UnboundMethod} with a valid implementation
+     * @throws RuntimeException if no implementation was found
+     */
+    public UnboundMethod build() {
+      if (method != null) {
+        return method;
+      } else {
+        throw new RuntimeException("Cannot find method: " + name);
+      }
+    }
+
+    /**
+     * Returns the first valid implementation as a BoundMethod or throws a
+     * NoSuchMethodException if there is none.
+     *
+     * @param receiver an Object to receive the method invocation
+     * @return a {@link BoundMethod} with a valid implementation and receiver
+     * @throws IllegalStateException if the method is static
+     * @throws IllegalArgumentException if the receiver's class is incompatible
+     * @throws NoSuchMethodException if no implementation was found
+     */
+    public BoundMethod buildChecked(Object receiver) throws 
NoSuchMethodException {
+      return buildChecked().bind(receiver);
+    }
+
+    /**
+     * Returns the first valid implementation as a BoundMethod or throws a
+     * RuntimeError if there is none.
+     *
+     * @param receiver an Object to receive the method invocation
+     * @return a {@link BoundMethod} with a valid implementation and receiver
+     * @throws IllegalStateException if the method is static
+     * @throws IllegalArgumentException if the receiver's class is incompatible
+     * @throws RuntimeException if no implementation was found
+     */
+    public BoundMethod build(Object receiver) {
+      return build().bind(receiver);
+    }
+
+    /**
+     * Returns the first valid implementation as a StaticMethod or throws a
+     * NoSuchMethodException if there is none.
+     *
+     * @return a {@link StaticMethod} with a valid implementation
+     * @throws IllegalStateException if the method is not static
+     * @throws NoSuchMethodException if no implementation was found
+     */
+    public StaticMethod buildStaticChecked() throws NoSuchMethodException {
+      return buildChecked().asStatic();
+    }
+
+    /**
+     * Returns the first valid implementation as a StaticMethod or throws a
+     * RuntimeException if there is none.
+     *
+     * @return a {@link StaticMethod} with a valid implementation
+     * @throws IllegalStateException if the method is not static
+     * @throws RuntimeException if no implementation was found
+     */
+    public StaticMethod buildStatic() {
+      return build().asStatic();
+    }
+
+  }
+
+  private static class MakeAccessible implements PrivilegedAction<Void> {
+    private Method hidden;
+
+    public MakeAccessible(Method hidden) {
+      this.hidden = hidden;
+    }
+
+    @Override
+    public Void run() {
+      hidden.setAccessible(true);
+      return null;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/parquet-common/src/test/java/org/apache/parquet/TestUtils.java
----------------------------------------------------------------------
diff --git a/parquet-common/src/test/java/org/apache/parquet/TestUtils.java 
b/parquet-common/src/test/java/org/apache/parquet/TestUtils.java
new file mode 100644
index 0000000..2062827
--- /dev/null
+++ b/parquet-common/src/test/java/org/apache/parquet/TestUtils.java
@@ -0,0 +1,70 @@
+/*
+ *  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.parquet;
+
+import org.junit.Assert;
+import java.util.concurrent.Callable;
+
+public class TestUtils {
+
+  /**
+   * A convenience method to avoid a large number of @Test(expected=...) tests
+   * @param message A String message to describe this assertion
+   * @param expected An Exception class that the Runnable should throw
+   * @param callable A Callable that is expected to throw the exception
+   */
+  public static void assertThrows(
+      String message, Class<? extends Exception> expected, Callable callable) {
+    try {
+      callable.call();
+      Assert.fail("No exception was thrown (" + message + "), expected: " +
+          expected.getName());
+    } catch (Exception actual) {
+      try {
+        Assert.assertEquals(message, expected, actual.getClass());
+      } catch (AssertionError e) {
+        e.addSuppressed(actual);
+        throw e;
+      }
+    }
+  }
+
+  /**
+   * A convenience method to avoid a large number of @Test(expected=...) tests
+   * @param message A String message to describe this assertion
+   * @param expected An Exception class that the Runnable should throw
+   * @param runnable A Runnable that is expected to throw the runtime exception
+   */
+  public static void assertThrows(
+      String message, Class<? extends Exception> expected, Runnable runnable) {
+    try {
+      runnable.run();
+      Assert.fail("No exception was thrown (" + message + "), expected: " +
+          expected.getName());
+    } catch (Exception actual) {
+      try {
+        Assert.assertEquals(message, expected, actual.getClass());
+      } catch (AssertionError e) {
+        e.addSuppressed(actual);
+        throw e;
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/parquet-common/src/test/java/org/apache/parquet/util/Concatenator.java
----------------------------------------------------------------------
diff --git 
a/parquet-common/src/test/java/org/apache/parquet/util/Concatenator.java 
b/parquet-common/src/test/java/org/apache/parquet/util/Concatenator.java
new file mode 100644
index 0000000..261c2be
--- /dev/null
+++ b/parquet-common/src/test/java/org/apache/parquet/util/Concatenator.java
@@ -0,0 +1,82 @@
+/*
+ *  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.parquet.util;
+
+/**
+ * This is a class for testing DynMethods and DynConstructors.
+ */
+public class Concatenator {
+  public static class SomeCheckedException extends Exception {
+  }
+
+  private String sep = "";
+
+  public Concatenator() {
+  }
+
+  public Concatenator(String sep) {
+    this.sep = sep;
+  }
+
+  private Concatenator(char sep) {
+    this.sep = String.valueOf(sep);
+  }
+
+  public Concatenator(Exception e) throws Exception {
+    throw e;
+  }
+
+  public static Concatenator newConcatenator(String sep) {
+    return new Concatenator(sep);
+  }
+
+  private void setSeparator(String sep) {
+    this.sep = sep;
+  }
+
+  public String concat(String left, String right) {
+    return left + sep + right;
+  }
+
+  public String concat(String left, String middle, String right) {
+    return left + sep + middle + sep + right;
+  }
+
+  public String concat(Exception e) throws Exception {
+    throw e;
+  }
+
+  public String concat(String... strings) {
+    if (strings.length >= 1) {
+      StringBuilder sb = new StringBuilder();
+      sb.append(strings[0]);
+      for (int i = 1; i < strings.length; i += 1) {
+        sb.append(sep);
+        sb.append(strings[i]);
+      }
+      return sb.toString();
+    }
+    return null;
+  }
+
+  public static String cat(String... strings) {
+    return new Concatenator().concat(strings);
+  }
+}

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java
----------------------------------------------------------------------
diff --git 
a/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java 
b/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java
new file mode 100644
index 0000000..1ab9582
--- /dev/null
+++ 
b/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java
@@ -0,0 +1,235 @@
+/*
+ *  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.parquet.util;
+
+import org.apache.parquet.TestUtils;
+import org.apache.parquet.util.Concatenator.SomeCheckedException;
+import org.junit.Assert;
+import org.junit.Test;
+import java.util.concurrent.Callable;
+
+public class TestDynConstructors {
+  @Test
+  public void testNoImplCall() {
+    final DynConstructors.Builder builder = new DynConstructors.Builder();
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testMissingClass() {
+    final DynConstructors.Builder builder = new DynConstructors.Builder()
+        .impl("not.a.RealClass");
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testMissingConstructor() {
+    final DynConstructors.Builder builder = new DynConstructors.Builder()
+        .impl(Concatenator.class, String.class, String.class);
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testFirstImplReturned() throws Exception {
+    final DynConstructors.Ctor<Concatenator> sepCtor = new 
DynConstructors.Builder()
+        .impl("not.a.RealClass", String.class)
+        .impl(Concatenator.class, String.class)
+        .impl(Concatenator.class)
+        .buildChecked();
+
+    Concatenator dashCat = sepCtor.newInstanceChecked("-");
+    Assert.assertEquals("Should construct with the 1-arg version",
+        "a-b", dashCat.concat("a", "b"));
+
+    TestUtils.assertThrows("Should complain about extra arguments",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return sepCtor.newInstanceChecked("/", "-");
+          }
+        });
+
+    TestUtils.assertThrows("Should complain about extra arguments",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return sepCtor.newInstance("/", "-");
+          }
+        });
+
+    DynConstructors.Ctor<Concatenator> defaultCtor = new 
DynConstructors.Builder()
+        .impl("not.a.RealClass", String.class)
+        .impl(Concatenator.class)
+        .impl(Concatenator.class, String.class)
+        .buildChecked();
+
+    Concatenator cat = defaultCtor.newInstanceChecked();
+    Assert.assertEquals("Should construct with the no-arg version",
+        "ab", cat.concat("a", "b"));
+  }
+
+  @Test
+  public void testExceptionThrown() throws Exception {
+    final SomeCheckedException exc = new SomeCheckedException();
+    final DynConstructors.Ctor<Concatenator> sepCtor = new 
DynConstructors.Builder()
+        .impl("not.a.RealClass", String.class)
+        .impl(Concatenator.class, Exception.class)
+        .buildChecked();
+
+    TestUtils.assertThrows("Should re-throw the exception",
+        SomeCheckedException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return sepCtor.newInstanceChecked(exc);
+          }
+        });
+
+    TestUtils.assertThrows("Should wrap the exception in RuntimeException",
+        RuntimeException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return sepCtor.newInstance(exc);
+          }
+        });
+  }
+
+  @Test
+  public void testStringClassname() throws Exception {
+    final DynConstructors.Ctor<Concatenator> sepCtor = new 
DynConstructors.Builder()
+        .impl(Concatenator.class.getName(), String.class)
+        .buildChecked();
+
+    Assert.assertNotNull("Should find 1-arg constructor", 
sepCtor.newInstance("-"));
+  }
+
+  @Test
+  public void testHiddenMethod() throws Exception {
+    TestUtils.assertThrows("Should fail to find hidden method",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return new DynMethods.Builder("setSeparator")
+                .impl(Concatenator.class, char.class)
+                .buildChecked();
+          }
+        });
+
+    final DynConstructors.Ctor<Concatenator> sepCtor = new 
DynConstructors.Builder()
+        .hiddenImpl(Concatenator.class.getName(), char.class)
+        .buildChecked();
+
+    Assert.assertNotNull("Should find hidden ctor with hiddenImpl", sepCtor);
+
+    Concatenator slashCat = sepCtor.newInstanceChecked('/');
+
+    Assert.assertEquals("Should use separator /",
+        "a/b", slashCat.concat("a", "b"));
+  }
+
+  @Test
+  public void testBind() throws Exception {
+    final DynConstructors.Ctor<Concatenator> ctor = new 
DynConstructors.Builder()
+        .impl(Concatenator.class.getName())
+        .buildChecked();
+
+    Assert.assertTrue("Should always be static", ctor.isStatic());
+
+    TestUtils.assertThrows("Should complain that method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return ctor.bind(null);
+          }
+        });
+  }
+
+  @Test
+  public void testInvoke() throws Exception {
+    final DynMethods.UnboundMethod ctor = new DynConstructors.Builder()
+        .impl(Concatenator.class.getName())
+        .buildChecked();
+
+    TestUtils.assertThrows("Should complain that target must be null",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return ctor.invokeChecked("a");
+          }
+        });
+
+    TestUtils.assertThrows("Should complain that target must be null",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return ctor.invoke("a");
+          }
+        });
+
+    Assert.assertNotNull("Should allow invokeChecked(null, ...)",
+        ctor.invokeChecked(null));
+    Assert.assertNotNull("Should allow invoke(null, ...)",
+        ctor.invoke(null));
+  }
+}

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java
----------------------------------------------------------------------
diff --git 
a/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java 
b/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java
new file mode 100644
index 0000000..7017c67
--- /dev/null
+++ b/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java
@@ -0,0 +1,410 @@
+/*
+ *  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.parquet.util;
+
+import org.apache.parquet.TestUtils;
+import org.apache.parquet.util.Concatenator.SomeCheckedException;
+import org.junit.Assert;
+import org.junit.Test;
+import java.util.concurrent.Callable;
+
+public class TestDynMethods {
+  @Test
+  public void testNoImplCall() {
+    final DynMethods.Builder builder = new DynMethods.Builder("concat");
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testMissingClass() {
+    final DynMethods.Builder builder = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class);
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testMissingMethod() {
+    final DynMethods.Builder builder = new DynMethods.Builder("concat")
+        .impl(Concatenator.class, "cat2strings", String.class, String.class);
+
+    TestUtils.assertThrows("Checked build should throw NoSuchMethodException",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return builder.buildChecked();
+          }
+        });
+
+    TestUtils.assertThrows("Normal build should throw RuntimeException",
+        RuntimeException.class, new Runnable() {
+          @Override
+          public void run() {
+            builder.build();
+          }
+        });
+  }
+
+  @Test
+  public void testFirstImplReturned() throws Exception {
+    Concatenator obj = new Concatenator("-");
+    DynMethods.UnboundMethod cat2 = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class)
+        .impl(Concatenator.class, String.class, String.class)
+        .impl(Concatenator.class, String.class, String.class, String.class)
+        .buildChecked();
+
+    Assert.assertEquals("Should call the 2-arg version successfully",
+        "a-b", cat2.invoke(obj, "a", "b"));
+
+    Assert.assertEquals("Should ignore extra arguments",
+        "a-b", cat2.invoke(obj, "a", "b", "c"));
+
+    DynMethods.UnboundMethod cat3 = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class)
+        .impl(Concatenator.class, String.class, String.class, String.class)
+        .impl(Concatenator.class, String.class, String.class)
+        .build();
+
+    Assert.assertEquals("Should call the 3-arg version successfully",
+        "a-b-c", cat3.invoke(obj, "a", "b", "c"));
+
+    Assert.assertEquals("Should call the 3-arg version null padding",
+        "a-b-null", cat3.invoke(obj, "a", "b"));
+  }
+
+  @Test
+  public void testVarArgs() throws Exception {
+    DynMethods.UnboundMethod cat = new DynMethods.Builder("concat")
+        .impl(Concatenator.class, String[].class)
+        .buildChecked();
+
+    Assert.assertEquals("Should use the varargs version", "abcde",
+        cat.invokeChecked(
+            new Concatenator(),
+            (Object) new String[] {"a", "b", "c", "d", "e"}));
+
+    Assert.assertEquals("Should use the varargs version", "abcde",
+        cat.bind(new Concatenator())
+            .invokeChecked((Object) new String[] {"a", "b", "c", "d", "e"}));
+  }
+
+  @Test
+  public void testIncorrectArguments() throws Exception {
+    final Concatenator obj = new Concatenator("-");
+    final DynMethods.UnboundMethod cat = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class)
+        .impl(Concatenator.class, String.class, String.class)
+        .buildChecked();
+
+    TestUtils.assertThrows("Should fail if non-string arguments are passed",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return cat.invoke(obj, 3, 4);
+          }
+        });
+
+    TestUtils.assertThrows("Should fail if non-string arguments are passed",
+        IllegalArgumentException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return cat.invokeChecked(obj, 3, 4);
+          }
+        });
+  }
+
+  @Test
+  public void testExceptionThrown() throws Exception {
+    final SomeCheckedException exc = new SomeCheckedException();
+    final Concatenator obj = new Concatenator("-");
+    final DynMethods.UnboundMethod cat = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class)
+        .impl(Concatenator.class, Exception.class)
+        .buildChecked();
+
+    TestUtils.assertThrows("Should re-throw the exception",
+        SomeCheckedException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return cat.invokeChecked(obj, exc);
+          }
+        });
+
+    TestUtils.assertThrows("Should wrap the exception in RuntimeException",
+        RuntimeException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return cat.invoke(obj, exc);
+          }
+        });
+  }
+
+  @Test
+  public void testNameChange() throws Exception {
+    Concatenator obj = new Concatenator("-");
+    DynMethods.UnboundMethod cat = new DynMethods.Builder("cat")
+        .impl(Concatenator.class, "concat", String.class, String.class)
+        .buildChecked();
+
+    Assert.assertEquals("Should find 2-arg concat method",
+        "a-b", cat.invoke(obj, "a", "b"));
+  }
+
+  @Test
+  public void testStringClassname() throws Exception {
+    Concatenator obj = new Concatenator("-");
+    DynMethods.UnboundMethod cat = new DynMethods.Builder("concat")
+        .impl(Concatenator.class.getName(), String.class, String.class)
+        .buildChecked();
+
+    Assert.assertEquals("Should find 2-arg concat method",
+        "a-b", cat.invoke(obj, "a", "b"));
+  }
+
+  @Test
+  public void testHiddenMethod() throws Exception {
+    Concatenator obj = new Concatenator("-");
+
+    TestUtils.assertThrows("Should fail to find hidden method",
+        NoSuchMethodException.class, new Callable() {
+          @Override
+          public Object call() throws NoSuchMethodException {
+            return new DynMethods.Builder("setSeparator")
+                .impl(Concatenator.class, String.class)
+                .buildChecked();
+          }
+        });
+
+    DynMethods.UnboundMethod changeSep = new DynMethods.Builder("setSeparator")
+        .hiddenImpl(Concatenator.class, String.class)
+        .buildChecked();
+
+    Assert.assertNotNull("Should find hidden method with hiddenImpl",
+        changeSep);
+
+    changeSep.invokeChecked(obj, "/");
+
+    Assert.assertEquals("Should use separator / instead of -",
+        "a/b", obj.concat("a", "b"));
+  }
+
+  @Test
+  public void testBoundMethod() throws Exception {
+    DynMethods.UnboundMethod cat = new DynMethods.Builder("concat")
+        .impl(Concatenator.class, String.class, String.class)
+        .buildChecked();
+
+    // Unbound methods can be bound multiple times
+    DynMethods.BoundMethod dashCat = cat.bind(new Concatenator("-"));
+    DynMethods.BoundMethod underCat = cat.bind(new Concatenator("_"));
+
+    Assert.assertEquals("Should use '-' object without passing",
+        "a-b", dashCat.invoke("a", "b"));
+    Assert.assertEquals("Should use '_' object without passing",
+        "a_b", underCat.invoke("a", "b"));
+
+    DynMethods.BoundMethod slashCat = new DynMethods.Builder("concat")
+        .impl(Concatenator.class, String.class, String.class)
+        .buildChecked(new Concatenator("/"));
+
+    Assert.assertEquals("Should use bound object from builder without passing",
+        "a/b", slashCat.invoke("a", "b"));
+  }
+
+  @Test
+  public void testBindStaticMethod() throws Exception {
+    final DynMethods.Builder builder = new DynMethods.Builder("cat")
+        .impl(Concatenator.class, String[].class);
+
+    TestUtils.assertThrows("Should complain that method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.buildChecked(new Concatenator());
+          }
+        });
+
+    TestUtils.assertThrows("Should complain that method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.build(new Concatenator());
+          }
+        });
+
+    final DynMethods.UnboundMethod staticCat = builder.buildChecked();
+    Assert.assertTrue("Should be static", staticCat.isStatic());
+
+    TestUtils.assertThrows("Should complain that method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return staticCat.bind(new Concatenator());
+          }
+        });
+  }
+
+  @Test
+  public void testStaticMethod() throws Exception {
+    DynMethods.StaticMethod staticCat = new DynMethods.Builder("cat")
+        .impl(Concatenator.class, String[].class)
+        .buildStaticChecked();
+
+    Assert.assertEquals("Should call varargs static method cat(String...)",
+        "abcde", staticCat.invokeChecked(
+            (Object) new String[] {"a", "b", "c", "d", "e"}));
+  }
+
+  @Test
+  public void testNonStaticMethod() throws Exception {
+    final DynMethods.Builder builder = new DynMethods.Builder("concat")
+        .impl(Concatenator.class, String.class, String.class);
+
+    TestUtils.assertThrows("Should complain that method is not static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.buildStatic();
+          }
+        });
+
+    TestUtils.assertThrows("Should complain that method is not static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.buildStaticChecked();
+          }
+        });
+
+    final DynMethods.UnboundMethod cat2 = builder.buildChecked();
+    Assert.assertFalse("concat(String,String) should not be static",
+        cat2.isStatic());
+
+    TestUtils.assertThrows("Should complain that method is not static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return cat2.asStatic();
+          }
+        });
+  }
+
+  @Test
+  public void testConstructorImpl() throws Exception {
+    final DynMethods.Builder builder = new 
DynMethods.Builder("newConcatenator")
+        .ctorImpl(Concatenator.class, String.class)
+        .impl(Concatenator.class, String.class);
+
+    DynMethods.UnboundMethod newConcatenator = builder.buildChecked();
+    Assert.assertTrue("Should find constructor implementation",
+        newConcatenator instanceof DynConstructors.Ctor);
+    Assert.assertTrue("Constructor should be a static method",
+        newConcatenator.isStatic());
+    Assert.assertFalse("Constructor should not be NOOP",
+        newConcatenator.isNoop());
+
+    // constructors cannot be bound
+    TestUtils.assertThrows("Should complain that ctor method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.buildChecked(new Concatenator());
+          }
+        });
+    TestUtils.assertThrows("Should complain that ctor method is static",
+        IllegalStateException.class, new Callable() {
+          @Override
+          public Object call() throws Exception {
+            return builder.build(new Concatenator());
+          }
+        });
+
+    Concatenator concatenator = newConcatenator.asStatic().invoke("*");
+    Assert.assertEquals("Should function as a concatenator",
+        "a*b", concatenator.concat("a", "b"));
+
+    concatenator = newConcatenator.asStatic().invokeChecked("@");
+    Assert.assertEquals("Should function as a concatenator",
+        "a@b", concatenator.concat("a", "b"));
+  }
+
+  @Test
+  public void testConstructorImplAfterFactoryMethod() throws Exception {
+    DynMethods.UnboundMethod newConcatenator = new 
DynMethods.Builder("newConcatenator")
+        .impl(Concatenator.class, String.class)
+        .ctorImpl(Concatenator.class, String.class)
+        .buildChecked();
+
+    Assert.assertFalse("Should find factory method before constructor method",
+        newConcatenator instanceof DynConstructors.Ctor);
+  }
+
+  @Test
+  public void testNoop() throws Exception {
+    // noop can be unbound, bound, or static
+    DynMethods.UnboundMethod noop = new DynMethods.Builder("concat")
+        .impl("not.a.RealClass", String.class, String.class)
+        .orNoop()
+        .buildChecked();
+
+    Assert.assertTrue("No implementation found, should return NOOP",
+        noop.isNoop());
+    Assert.assertNull("NOOP should always return null",
+        noop.invoke(new Concatenator(), "a"));
+    Assert.assertNull("NOOP can be called with null",
+        noop.invoke(null, "a"));
+    Assert.assertNull("NOOP can be bound",
+        noop.bind(new Concatenator()).invoke("a"));
+    Assert.assertNull("NOOP can be bound to null",
+        noop.bind(null).invoke("a"));
+    Assert.assertNull("NOOP can be static",
+        noop.asStatic().invoke("a"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/ddbeb4dd/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index df4bbd3..0d4df8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,6 +89,12 @@
     <avro.version>1.8.1</avro.version>
     <guava.version>20.0</guava.version>
     <mockito.version>1.10.19</mockito.version>
+
+    <!-- parquet-cli dependencies -->
+    <opencsv.version>2.3</opencsv.version>
+    <jackson2.version>2.3.1</jackson2.version>
+    <jcommander.version>1.35</jcommander.version>
+    <commons-codec.version>1.10</commons-codec.version>
   </properties>
 
   <modules>
@@ -97,6 +103,7 @@
     <module>parquet-benchmarks</module>
     <module>parquet-cascading</module>
     <module>parquet-cascading3</module>
+    <module>parquet-cli</module>
     <module>parquet-column</module>
     <module>parquet-common</module>
     <module>parquet-encoding</module>

Reply via email to