ORC-644: Support positional mapping for nested types.

Resolves #522

Signed-off-by: Owen O'Malley <omalley@apache.org>
diff --git a/java/core/src/java/org/apache/orc/Reader.java b/java/core/src/java/org/apache/orc/Reader.java
index 4aa1cff..84c5d91 100644
--- a/java/core/src/java/org/apache/orc/Reader.java
+++ b/java/core/src/java/org/apache/orc/Reader.java
@@ -332,9 +332,9 @@
     }
 
     /**
-     * Set no. of levels to force schema evolution to be positional instead of
+     * Set number of levels to force schema evolution to be positional instead of
      * based on the column names.
-     * @param value force positional evolution
+     * @param value number of levels of positional schema evolution
      * @return this
      */
     public Options positionalEvolutionLevel(int value) {
diff --git a/java/core/src/java/org/apache/orc/impl/SchemaEvolution.java b/java/core/src/java/org/apache/orc/impl/SchemaEvolution.java
index dac5437..d7c6bd2 100644
--- a/java/core/src/java/org/apache/orc/impl/SchemaEvolution.java
+++ b/java/core/src/java/org/apache/orc/impl/SchemaEvolution.java
@@ -497,7 +497,7 @@
           if (fileChildren.size() == readerChildren.size()) {
             for(int i=0; i < fileChildren.size(); ++i) {
               buildConversion(fileChildren.get(i),
-                              readerChildren.get(i), 0);
+                              readerChildren.get(i), positionalLevels - 1);
             }
           } else {
             isOk = false;
diff --git a/java/core/src/test/org/apache/orc/impl/TestSchemaEvolution.java b/java/core/src/test/org/apache/orc/impl/TestSchemaEvolution.java
index f5fe82f..9357c1b 100644
--- a/java/core/src/test/org/apache/orc/impl/TestSchemaEvolution.java
+++ b/java/core/src/test/org/apache/orc/impl/TestSchemaEvolution.java
@@ -1736,6 +1736,49 @@
     assertEquals(4, evo.getFileType(4).getId());
   }
 
+  @Test
+  public void testPositionalEvolutionForStructInArray() throws IOException {
+    options.forcePositionalEvolution(true);
+    options.positionalEvolutionLevel(Integer.MAX_VALUE);
+    TypeDescription file = TypeDescription.fromString("array<struct<x:int,y:int,z:int>>");
+    TypeDescription read = TypeDescription.fromString("array<struct<z:int,x:int,a:int,b:int>>");
+    SchemaEvolution evo = new SchemaEvolution(file, read, options);
+    assertEquals(1, evo.getFileType(1).getId());
+    assertEquals(2, evo.getFileType(2).getId());
+    assertEquals(3, evo.getFileType(3).getId());
+    assertEquals(4, evo.getFileType(4).getId());
+    assertEquals(null, evo.getFileType(5));
+  }
+
+  @Test
+  public void testPositionalEvolutionForTwoLayerNestedStruct() throws IOException {
+    options.forcePositionalEvolution(true);
+    options.positionalEvolutionLevel(Integer.MAX_VALUE);
+    TypeDescription file = TypeDescription.fromString("struct<s:struct<x:int,y:int,z:int>>");
+    TypeDescription read = TypeDescription.fromString("struct<s:struct<z:int,x:int,a:int,b:int>>");
+    SchemaEvolution evo = new SchemaEvolution(file, read, options);
+    assertEquals(1, evo.getFileType(1).getId());
+    assertEquals(2, evo.getFileType(2).getId());
+    assertEquals(3, evo.getFileType(3).getId());
+    assertEquals(4, evo.getFileType(4).getId());
+    assertNull(evo.getFileType(5));
+  }
+
+  @Test
+  public void testPositionalEvolutionForThreeLayerNestedStruct() throws IOException {
+    options.forcePositionalEvolution(true);
+    options.positionalEvolutionLevel(Integer.MAX_VALUE);
+    TypeDescription file = TypeDescription.fromString("struct<s1:struct<s2:struct<x:int,y:int,z:int>>>");
+    TypeDescription read = TypeDescription.fromString("struct<s1:struct<s:struct<z:int,x:int,a:int,b:int>>>");
+    SchemaEvolution evo = new SchemaEvolution(file, read, options);
+    assertEquals(1, evo.getFileType(1).getId());
+    assertEquals(2, evo.getFileType(2).getId());
+    assertEquals(3, evo.getFileType(3).getId());
+    assertEquals(4, evo.getFileType(4).getId());
+    assertEquals(5, evo.getFileType(5).getId());
+    assertNull(evo.getFileType(6));
+  }
+
   // These are helper methods that pull some of the common code into one
   // place.