Author: schor
Date: Tue Jun  7 13:29:34 2016
New Revision: 1747229

URL: http://svn.apache.org/viewvc?rev=1747229&view=rev
Log:
[UIMA-4518] rework reports to use Paths.
switch to built-in directory walkers.
make walkers into Jars work with nested Jars by extracting the nested Jar into 
a temporary dir (deleted on exit).
Call the close method on the walkers to insure file handles are released (got 
too-many-open-files error otherwise). improve translation in several cases. 
correct usage string. add workaround for empty implements bug.

Modified:
    
uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java

Modified: 
uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java
URL: 
http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java?rev=1747229&r1=1747228&r2=1747229&view=diff
==============================================================================
--- 
uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java
 (original)
+++ 
uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java
 Tue Jun  7 13:29:34 2016
@@ -24,13 +24,14 @@ import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
 import java.lang.reflect.Modifier;
-import java.net.URI;
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
+import java.nio.file.FileVisitOption;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,14 +41,16 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
 
 import org.apache.uima.cas.impl.TypeImpl;
 import org.apache.uima.cas.impl.TypeSystemImpl;
 import org.apache.uima.cas.impl.UimaDecompiler;
 import org.apache.uima.internal.util.CommandLineParser;
-import org.apache.uima.internal.util.Pair;
+import org.apache.uima.internal.util.Misc;
 import org.apache.uima.util.FileUtils;
-import org.apache.uima.util.Misc;
 
 import com.github.javaparser.ASTHelper;
 import com.github.javaparser.JavaParser;
@@ -128,7 +131,9 @@ public class MigrateJCas extends VoidVis
 
   private static final String OUTPUT_DIRECTORY = "-outputDirectory";
   
-  private static final String SKIP_TYPE_CHECK = "-skip_Type_check";
+  private static final String SKIP_TYPE_CHECK = "-skipTypeCheck";
+  
+  private Path tempDir = null;
     
   private boolean isSource = false;
   
@@ -155,20 +160,114 @@ private String outDirLog;
   
   private CompilationUnit cu;
   
+  private static abstract class Report2 {
+    public abstract Comparable getFirst(); // Eclipse on linux complained if 
not public, was OK on windows
+    public abstract Comparable getSecond();  
+    abstract int getFirstLength();
+  }
+  
+  private static class PathAndReason extends Report2 {
+    Path path;
+    String reason;
+    PathAndReason(Path path, String reason) {
+      this.path = path;
+      this.reason = reason;
+    }
+    @Override
+    public Comparable<Path> getFirst() { return path; }
+    @Override
+    public Comparable<String> getSecond() { return reason; }
+    @Override
+    int getFirstLength() { return path.toString().length(); }
+  }
+  
+  private static class ClassnameAndPath extends Report2 {
+    String classname;
+    Path path;
+    ClassnameAndPath(String classname, Path path) {
+      this.classname = classname;
+      this.path = path;
+    }
+    @Override
+    public Comparable<String> getFirst() { return classname; }
+    @Override
+    public Comparable<Path> getSecond() { return path; }
+    @Override
+    int getFirstLength() { return classname.length(); }
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((classname == null) ? 0 : 
classname.hashCode());
+      result = prime * result + ((path == null) ? 0 : path.hashCode());
+      return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (!(obj instanceof ClassnameAndPath))
+        return false;
+      ClassnameAndPath other = (ClassnameAndPath) obj;
+      if (classname == null) {
+        if (other.classname != null)
+          return false;
+      } else if (!classname.equals(other.classname))
+        return false;
+      if (path == null) {
+        if (other.path != null)
+          return false;
+      } else if (!path.equals(other.path))
+        return false;
+      return true;
+    }
+  }
+  
+  private static class String1AndString2 extends Report2 {
+    String s1;
+    String s2;
+    @Override
+    public Comparable<String> getFirst() { return s1; }
+    @Override
+    public Comparable<String> getSecond() { return s2; }
+    @Override
+    int getFirstLength() { return s1.toString().length(); }
+  }
+  
+  private static class PathAndPath extends Report2 {
+    Path p1;
+    Path p2;
+    PathAndPath(Path p1, Path p2) {
+      this.p1 = p1;
+      this.p2 = p2;
+    }
+    @Override
+    public Comparable<Path> getFirst() { return p1; }
+    @Override
+    public Comparable<Path> getSecond() { return p2; }
+    @Override
+    int getFirstLength() { return p1.toString().length(); }
+  }
+  
   /************************************
    * Reporting
    ************************************/
   private final List<Path> v2JCasFiles = new ArrayList<>();
   private final List<Path> v3JCasFiles = new ArrayList<>();
-  private final List<Pair<String, String>> nonJCasFiles = new ArrayList<>();  
// path, reason
-  private final List<Pair<String, String>> failedMigration = new 
ArrayList<>(); // path, reason
-  private final List<Pair<String, String>> c2ps = new ArrayList<>();           
 // class, path
-  private final List<Pair<String, String>> extendableBuiltins = new 
ArrayList<>();  // class, path
-  private final List<Pair<String, String>> nonExtendableBuiltins = new 
ArrayList<>();  // class, path
-  private final List<Pair<String, String>> nonIdenticalDuplicates = new 
ArrayList<>();  // class, path
-  private final List<Pair<String, String>> identicalDuplicates = new 
ArrayList<>();  // class, path
-  private final List<Pair<String, String>> deletedCheckModified = new 
ArrayList<>();  // class, deleted check string
-  private final List<Pair<String, String>> pathWorkaround = new ArrayList<>(); 
// original, workaround
+  
+  private final List<PathAndReason> nonJCasFiles = new ArrayList<>();  // 
path, reason
+  private final List<PathAndReason> failedMigration = new ArrayList<>(); // 
path, reason
+  private final List<ClassnameAndPath> c2ps = new ArrayList<>();            // 
class, path
+  private final List<ClassnameAndPath> extendableBuiltins = new ArrayList<>(); 
 // class, path
+  private final List<ClassnameAndPath> nonExtendableBuiltins = new 
ArrayList<>();  // class, path
+  private final List<ClassnameAndPath> nonIdenticalDuplicates = new 
ArrayList<>();  // class, path
+  private final List<ClassnameAndPath> identicalDuplicates = new 
ArrayList<>();  // class, path
+  private final List<PathAndReason> deletedCheckModified = new ArrayList<>();  
// path, deleted check string
+  private final List<String1AndString2> pathWorkaround = new ArrayList<>(); // 
original, workaround
+  private final List<PathAndReason> manualInspection = new ArrayList<>(); // 
path, reason
+//  private final List<PathAndPath> embeddedJars = new ArrayList<>(); // 
source, temp   
   
   private boolean v2;  // false at start of migrate, set to true if a v2 class 
candidate is discovered
   private boolean v3;  // true at start of migrate, set to false if no 
conversion done
@@ -204,6 +303,8 @@ private String outDirLog;
   private boolean hasV2Constructors;
   private boolean hasV3Constructors;
 
+  private boolean isSkipTypeCheck = false;
+
 
   public MigrateJCas() {
   }
@@ -241,11 +342,12 @@ private String outDirLog;
     for (String root : roots) {
       System.out.println("Migrating from root: " + root);
       int i = 1;
+      System.out.print("    ");
       for (Path c : getCandidates(clp, root)) {
         System.out.print(".");
-        if ((i % 50) == 0) System.out.print("\n " + i);
+        if ((i % 50) == 0) System.out.format("%n%4d",i);
         candidate = c;
-        String source = getSource();
+        String source = getSource(candidate);
         migrate(source);
         i++;
       }
@@ -257,15 +359,15 @@ private String outDirLog;
     return clp.getParamArgument(kind).split("\\" + File.pathSeparator);
   }
   
-  private String getSource() {
+  private String getSource(Path path) {
     try {
       String s;
       if (isSource) {
-        s = FileUtils.reader2String(Files.newBufferedReader(candidate));
+        s = FileUtils.reader2String(Files.newBufferedReader(path));
 //        System.out.println("debug read " + s.length());
       } else {
         // read in bytes and decompile the class to a string
-        byte[] b = Files.readAllBytes(candidate);
+        byte[] b = Files.readAllBytes(path);
         s = decompile(b);
       }
       return s;
@@ -314,15 +416,18 @@ private String outDirLog;
     StringReader sr = new StringReader(source);
     try {
       cu = JavaParser.parse(sr, true);
-            
+      
       addImport("org.apache.uima.cas.impl.CASImpl");
       addImport("org.apache.uima.cas.impl.TypeImpl");
       addImport("org.apache.uima.cas.impl.TypeSystemImpl");
       
       this.visit(cu, null);      
       new removeEmptyStmts().visit(cu, null);
-
-      if (fi_fields.size() > 0) {
+      if (v3) {
+        removeImport("org.apache.uima.jcas.cas.TOP_Type");
+      }
+      
+      if (v3 && fi_fields.size() > 0) {
         List<BodyDeclaration> classMembers = cu.getTypes().get(0).getMembers();
         int positionOfFirstConstructor = findConstructor(classMembers);
         if (positionOfFirstConstructor < 0) {
@@ -354,21 +459,18 @@ private String outDirLog;
   @Override
   public void visit(AnnotationDeclaration n, Object ignore) {
     updateClassName(n);
-    v3 = false;
     super.visit(n,  ignore);
   }
 
   @Override
   public void visit(EmptyTypeDeclaration n, Object ignore) {
     updateClassName(n);
-    v3 = false;
     super.visit(n,  ignore);
   }
 
   @Override
   public void visit(EnumDeclaration n, Object ignore) {
     updateClassName(n);
-    v3 = false;
     super.visit(n,  ignore);
   }
 
@@ -441,7 +543,6 @@ private String outDirLog;
       return;
     }
     List<Parameter> ps = n.getParameters();
-    Parameter ps0, ps1;
     
     if (ps.size() == 2 && 
         getParmTypeName(ps, 0).equals("int") &&
@@ -468,6 +569,9 @@ private String outDirLog;
     }      
   }
 
+  private final static Pattern refGetter = 
Pattern.compile("(ll_getRef(Array)?Value)|"
+      +                                                    "(ll_getFSForRef)");
+  private final static Pattern word1 = Pattern.compile("\\A(\\w*)");  // word 
chars starting at beginning \\A means beginning
   /*****************************
    * Method Declaration Visitor
    *   Heuristic to determine if a feature getter or setter:
@@ -478,6 +582,8 @@ private String outDirLog;
    *   - for getter: has 0 or 1 arg (1 arg case for indexed getter, arg must 
be int type)
    *   - for setter: has 1 or 2 args
    *   
+   *   Workaround for decompiler - getters which return FSs might be missing 
the cast to the return value type
+   *     
    *****************************/
   @Override
   public void visit(MethodDeclaration n, Object ignore) {
@@ -501,25 +607,46 @@ private String outDirLog;
         }
         
         // get the range-part-name and convert to v3 range ("Ref" changes to 
"Feature")
-        String s = n.getBody().toStringWithoutComments();
-        int i = s.indexOf("jcasType.ll_cas.ll_");
+        String bodyString = n.getBody().toStringWithoutComments();
+        int i = bodyString.indexOf("jcasType.ll_cas.ll_");
         if (i < 0) break; 
-        s = s.substring(i + "jcasType.ll_cas.ll_get".length());
+        String s = bodyString.substring(i + 
"jcasType.ll_cas.ll_get".length()); // also for ...ll_set - same length!
         if (s.startsWith("FSForRef(")) { // then it's the wrapper and the 
wrong instance.
           i = s.indexOf("jcasType.ll_cas.ll_");
-          if (i < 0) throw new RuntimeException();
+          if (i < 0) {
+            reportUnrecognizedV2Code("Found \"jcasType.ll_cas.ll_[set or 
get]...FSForRef(\" but didn't find following \"jcasType.ll_cas_ll_\"\n" + 
n.toString());
+            break;
+          }
           s = s.substring(i + "jcasType.ll_cas.ll_get".length());
         }
         i = s.indexOf("Value");
         if (i < 0) {
-          reportUnrecognizedV2Code(s);
+          reportUnrecognizedV2Code("Found \"jcasType.ll_cas.ll_[set or get]\" 
but didn't find following \"Value\"\n" + n.toString());
           break;  // give up
         }
         s = Character.toUpperCase(s.charAt(0)) + s.substring(1, i);
         rangeNameV2Part = s;
         rangeNamePart = s.equals("Ref") ? "Feature" : s;
 
-        featName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
+        // get feat name following ")jcasType).casFeatCode_xxxxx,
+        i = bodyString.indexOf("jcasType).casFeatCode_");
+        if (i == -1) {
+          reportUnrecognizedV2Code("Didn't find 
\"...jcasType).casFeatCode_\"\n" + n.toString());
+          break;
+        }
+        Matcher m = word1.matcher(bodyString.substring(i + 
"jcasType).casFeatCode_".length() ));
+        if (!m.find()) {
+          reportUnrecognizedV2Code("Found \"...jcasType).casFeatCode_\" but 
didn't find subsequent word\n" + n.toString());
+          break;
+        }
+        featName = m.group(1);
+        String fromMethod = Character.toLowerCase(name.charAt(3)) + 
name.substring(4);
+        if (!featName.equals(fromMethod)) {
+          // don't report if the only difference is the first letter 
captialization
+          if (!(Character.toLowerCase(featName.charAt(0)) + 
featName.substring(1)).equals(fromMethod)) {
+            reportMismatchedFeatureName(String.format("%-25s %s", featName, 
name));
+          }
+        }
         
         List<Expression> args = Collections.singletonList(new 
StringLiteralExpr(featName));
         VariableDeclarator vd = new VariableDeclarator(
@@ -531,6 +658,29 @@ private String outDirLog;
               ASTHelper.INT_TYPE, 
               vd));
         }
+        
+        /**
+         * add missing cast stmt for
+         * return stmts where the value being returned:
+         *   - doesn't have a cast already
+         *   - has the expression be a methodCallExpr with a name which looks 
like:
+         *       ll_getRefValue or 
+         *       ll_getRefArrayValue  
+         */
+        if (isGetter && "Feature".equals(rangeNamePart)) {
+          for (Statement stmt : n.getBody().getStmts()) {
+            if (stmt instanceof ReturnStmt) {
+              Expression e = getUnenclosedExpr(((ReturnStmt)stmt).getExpr());
+              if ((e instanceof MethodCallExpr)) {
+                String methodName = ((MethodCallExpr)e).getName();
+                if (refGetter.matcher(methodName).matches()) { // 
ll_getRefValue or ll_getRefArrayValue
+                  addCastExpr(stmt, n.getType());
+                }
+              }
+            }
+          }
+        }
+
         get_set_method = n; // used as a flag during inner "visits" to signal  
                             // we're inside a likely feature setter/getter
 
@@ -540,20 +690,19 @@ private String outDirLog;
     super.visit(n, ignore);
     get_set_method = null; // after visiting, reset the get_set_method to null
   }
-  
+    
   /**
    * Visitor for if stmts
-   *   - remove feature missing test
+   *   - removes feature missing test
    */
   @Override
   public void visit(IfStmt n, Object ignore) {
     do {
-      if (get_set_method == null) break;
+      // if (get_set_method == null) break;  // sometimes, these occur outside 
of recogn. getters/setters
       
       Expression c = n.getCondition(), e;
-      BinaryExpr be, be2;
+      BinaryExpr be, be2; 
       List<Statement> stmts;
-      
       if ((c instanceof BinaryExpr) &&
           ((be = (BinaryExpr)c).getLeft() instanceof FieldAccessExpr) &&
           ((FieldAccessExpr)be.getLeft()).getField().equals("featOkTst")) {
@@ -563,12 +712,10 @@ private String outDirLog;
         if (! (be.getRight() instanceof BinaryExpr) 
          || ! ((be2 = (BinaryExpr)be.getRight()).getRight() instanceof 
NullLiteralExpr) 
          || ! (be2.getLeft() instanceof FieldAccessExpr)
-         || ! (n.getThenStmt() instanceof BlockStmt) 
-         || ! ((stmts = ((BlockStmt)n.getThenStmt()).getStmts()).size() == 1)
-         || ! (stmts.get(0) instanceof ExpressionStmt)
-         || ! ((e = ((ExpressionStmt)stmts.get(0)).getExpression()) instanceof 
MethodCallExpr)
+
+         || ! ((e = getExpressionFromStmt(n.getThenStmt())) instanceof 
MethodCallExpr)
          || ! (((MethodCallExpr)e).getName()).equals("throwFeatMissing")) {
-          reportDeletedCheckModified("The featOkTst was modified:\n" + 
n.toString());
+          reportDeletedCheckModified("The featOkTst was modified:\n" + 
n.toString() + '\n');
         }
               
         BlockStmt parent = (BlockStmt) n.getParentNode();
@@ -624,6 +771,7 @@ private String outDirLog;
         String s = n.getName().substring(z.length());
         s = s.substring(0,  s.length() - "Value".length()); // s = 
"ShortArray",  etc.
         if (s.equals("RefArray")) s = "FSArray";
+        if (s.equals("IntArray")) s = "IntegerArray";
         EnclosedExpr ee = new EnclosedExpr(
             new CastExpr(new ClassOrInterfaceType(s), n.getArgs().get(0)));
         
@@ -660,14 +808,15 @@ private String outDirLog;
   public void visit(FieldAccessExpr n, Object ignore) {
     Expression e;
     
-    if (get_set_method != null) {
+    if (get_set_method != null) {  
       if (n.getField().startsWith("casFeatCode_") &&
           ((e = getUnenclosedExpr(n.getScope())) instanceof CastExpr) &&
           ("jcasType".equals(getName(((CastExpr)e).getExpr())))) {
-        replaceInParent(n, new NameExpr("_FI_" + featName)); // repl last in 
List<Expression> (args)
+        String featureName = n.getField().substring("casFeatCode_".length());
+        replaceInParent(n, new NameExpr("_FI_" + featureName)); // repl last 
in List<Expression> (args)
         return;
       } else if (n.getField().startsWith("casFeatCode_")) {
-        System.out.println("debug");
+        reportMigrateFailed("Found field casFeatCode_ ... without a previous 
cast expr using jcasType");
       }
     }
     super.visit(n,  ignore);      
@@ -707,7 +856,7 @@ private String outDirLog;
    *
    */
   private void report() {
-    System.out.format("Migration Summary.  Using input from %s files%n", 
isSource ? ".java" : ".class");
+    System.out.format("%n%nMigration Summary.  Using input from %s files%n", 
isSource ? ".java" : ".class");
     System.out.format("Output top directory: %s%n", outputDirectory);
     System.out.format("Date/time: %tc%n", new Date());
     System.out.println("  Roots:");
@@ -719,6 +868,7 @@ private String outDirLog;
     try {
       reportPaths("Workaround Directories", "workaroundDir", pathWorkaround);
       reportPaths("Report of converted files where a deleted check was 
customized", "deletedCheckModified", deletedCheckModified);
+      reportPaths("Report of converted files needing manual inspection", 
"manualInspection", manualInspection);
       reportPaths("Report of files which failed migration", "failed.txt", 
failedMigration);
       reportPaths("Report of non-JCas files", "NonJCasFiles.txt", 
nonJCasFiles);
       reportPaths("Builtin Extendable JCas classes - skipped - need manual 
checking to see if they are modified",
@@ -736,15 +886,15 @@ private String outDirLog;
   }
   
   private void computeDuplicates() {
-    List<Pair<String, String>> toCheck = new ArrayList<>(c2ps);
+    List<ClassnameAndPath> toCheck = new ArrayList<>(c2ps);
     toCheck.addAll(extendableBuiltins);
-    sortPairs(toCheck);
-    Pair<String, String> prevP = new Pair<>("","");
-    List<Pair<String, String>> sameList = new ArrayList<>();
+    sortReport2(toCheck);
+    ClassnameAndPath prevP = new ClassnameAndPath(null, null);
+    List<ClassnameAndPath> sameList = new ArrayList<>();
     boolean areAllEqual = true;
     
-    for (Pair<String, String> p : toCheck) {
-      if (!p.t.equals(prevP.t)) {
+    for (ClassnameAndPath p : toCheck) {
+      if (!p.getFirst().equals(prevP.getFirst())) {
         
         addToIdenticals(sameList, areAllEqual);
         sameList.clear();
@@ -760,7 +910,7 @@ private String outDirLog;
       }
       sameList.add(p);
       if (areAllEqual) {
-        if (filesMiscompare(p.u, prevP.u)) {
+        if (isFilesMiscompare(p.path, prevP.path)) {
           areAllEqual = false;
         }
       }      
@@ -769,19 +919,19 @@ private String outDirLog;
     addToIdenticals(sameList, areAllEqual);    
   }
   
-  private boolean filesMiscompare(String sp1, String sp2) {
-    Path p1 = Paths.get(sp1);
-    Path p2 = Paths.get(sp2);
-    try {
-      String s1 = FileUtils.reader2String(Files.newBufferedReader(p1));
-      String s2 = FileUtils.reader2String(Files.newBufferedReader(p2));
+  /**
+   * Compare two java source or class files
+   * @param p1
+   * @param p2
+   * @return
+   */
+  private boolean isFilesMiscompare(Path p1, Path p2) {
+      String s1 = getSource(p1);
+      String s2 = getSource(p2);
       return !s1.equals(s2);
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
   }
   
-  private void addToIdenticals(List<Pair<String, String>> sameList, boolean 
areAllEqual) {
+  private void addToIdenticals(List<ClassnameAndPath> sameList, boolean 
areAllEqual) {
     if (sameList.size() > 0) {
       if (areAllEqual) {
         identicalDuplicates.addAll(sameList);
@@ -809,7 +959,7 @@ private String outDirLog;
     return p;
   }
   
-  private void reportPaths(String title, String fileName, List<Pair<String, 
String>> items) throws IOException {
+  private void reportPaths(String title, String fileName, List<? extends 
Report2> items) throws IOException {
     if (items.size() == 0) {
       System.out.println("No " + title);
       return;  
@@ -819,17 +969,25 @@ private String outDirLog;
     System.out.println("");
     
     try (BufferedWriter bw = Files.newBufferedWriter(makePath(outDirLog + 
fileName), StandardOpenOption.CREATE)) {
-      List<Pair<String, String>> sorted = new ArrayList<>(items);
+      List<Report2> sorted = new ArrayList<>(items);
 
-      sortPairs(sorted);  
+      sortReport2(sorted);  
       int max = 0;
-      for (Pair<String, String> p : sorted) {
-        max = Math.max(max, p.t.length());
+      for (Report2 p : sorted) {
+        max = Math.max(max, p.getFirstLength());
       }
       
       int i = 1;
-      for (Pair<String, String> p : sorted) {
-        String s = String.format("%5d %-" +max+ "s %s%n", i, p.t, p.u);
+      for (Report2 p : sorted) {
+        Object v = p.getSecond();
+        if (v instanceof Path) {
+          Path path = (Path) v;
+          FileSystem fileSystem = path.getFileSystem();
+          if (fileSystem instanceof com.sun.nio.zipfs.ZipFileSystem) {
+            v = v.toString() + "\t\t " + fileSystem.toString();
+          }
+        }
+        String s = String.format("%5d %-" +max+ "s %s%n", i, p.getFirst(), v);
         bw.write(s);
         System.out.print(s);
         i++;
@@ -837,39 +995,46 @@ private String outDirLog;
       System.out.println("");
     } // end of try-with-resources
   }
-  
-  private void sortPairs(List<Pair<String, String>> items) {
-    Collections.sort(items, new Comparator<Pair<String, String>>() {
-      @Override
-      public int compare(Pair<String, String> o1, Pair<String, String> o2) {
-        int r = o1.t.compareTo(o2.t);
-        if (r == 0) {
-          r = o1.u.compareTo(o2.u);
-        }
-        return r;
-      }
-    });
+    
+  private void sortReport2(List<? extends Report2> items) {
+    Collections.sort(items, 
+        (o1, o2) -> {
+          int r = o1.getFirst().compareTo(o2.getFirst());
+          if (r == 0) {
+            r = o1.getSecond().compareTo(o2.getSecond());
+          }
+          return r;
+        });
   }
-  
+
   /**
    * Walk the directory tree rooted at root
    *   - descend subdirectories
    *   - descend Jar file
+   *     -- descend nested Jar files (!)
+   *        by extracting these to a temp dir, and keeping a back reference to 
where they were extracted from.
+   *   
    * output the paths representing the classes
    *   -- for non-jars:
    *      file:/c:/data/test.class
    *   -- for Jars:
    *      jar:file:/c:/data/test.jar!/path/to/file
    * @param root
-   * @return
+   * @return list of Paths
    * @throws IOException
    */
   private List<Path> getCandidates(CommandLineParser clp, String root) {
-    boolean skip_Type_check = clp.isInArgsList(SKIP_TYPE_CHECK);
+    isSkipTypeCheck  = clp.isInArgsList(SKIP_TYPE_CHECK);
     Path startPath = Paths.get(root);
     candidates = new ArrayList<>();
     
-    getCandidates_processCandidate(startPath);
+    try (Stream<Path> stream = Files.walk(startPath, 
FileVisitOption.FOLLOW_LINKS)) {  // needed to release file handles
+        stream.forEachOrdered(
+          p -> getCandidates_processFile(p));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    
     Collections.sort(candidates, pathComparator);
     
     List<Path> c = new ArrayList<>();  // candidates
@@ -878,7 +1043,7 @@ private String outDirLog;
       return c;
     }
     for (int i = 0; i < nbrOfPaths;) {
-      if (skip_Type_check) {
+      if (isSkipTypeCheck) {
         candidate = candidates.get(i);
         if (candidate.getFileName().endsWith(isSource ? "_Type.java" : 
"_Type.class")) {
           continue;  // skip the _Type files
@@ -886,9 +1051,10 @@ private String outDirLog;
         c.add(candidate);
         i++;
       } else {
-        int next_Type = indexOf_Type(i);
+        // doing _Type check: only include java files if there's an associated 
_Type file
+        int next_Type = indexOf_Type(i);  // look for the next _Type file 
starting at position i
         if (next_Type == -1) {
-          System.out.println("debug getCandidates retuning:");
+//          System.out.println("debug getCandidates returning because doing 
_Type checking and no further _Types");
           for (Path x : c) {
             System.out.println("debug   " + x);
           }
@@ -930,16 +1096,38 @@ private String outDirLog;
     }
   }
     
+  
+  /**
+   * adds all the .java or .class files to the candidates, including _Type if 
not skipping the _Type check
+   * Handling embedded jar files
+   *   - single level Jar (at the top level of the default file system)
+   *     -- handle using an overlayed file system
+   *   - embedded Jars within Jars: 
+   *     - not supported by Zip File System Provider (it only supports one 
level)
+   *     - handle by extracting to a temp dir, and then using the Zip File 
System Provider
+   * @param path
+   */
   private void getCandidates_processFile(Path path) {
 //    System.out.println("debug processing " + path);
     try {
-      URI pathUri = path.toUri();
-      String pathString = pathUri.toString();
-      if (pathString.endsWith(".jar")) {
+//      URI pathUri = path.toUri();
+      String pathString = path.toString();
+      if (pathString.endsWith(".jar")) {  // path.endsWith does mean this !!
+        if (!path.getFileSystem().equals(FileSystems.getDefault())) {        
+          // embedded Jar: extract to temp
+          Path out = getTempOutputPath(path);
+          Files.copy(path, out, StandardCopyOption.REPLACE_EXISTING);
+//          embeddedJars.add(new PathAndPath(path, out));
+          path = out;
+        }
         FileSystem jfs = FileSystems.newFileSystem(path, null);
         Path start = jfs.getPath("/");
-        getCandidates_DescendDirectory(start);
+        try (Stream<Path> stream = Files.walk(start)) {  // needed to release 
file handles
+          stream.forEachOrdered(
+            p -> getCandidates_processFile(p));
+        }
       } else {
+        // is not a .jar file.  see if it's a jcas file
 //        System.out.println("debug path ends with java or class " + 
pathString.endsWith(isSource ? ".java" : ".class") + " " + pathString);
         if (pathString.endsWith(isSource ? ".java" : ".class")) {
           candidates.add(path);
@@ -950,22 +1138,20 @@ private String outDirLog;
     }
   }
   
-  private void getCandidates_DescendDirectory(Path path) {
-    try {
-      Files.list(path).forEach(p -> getCandidates_processCandidate(p));
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+  private Path getTempOutputPath(Path path) throws IOException {
+    Path localTempDir = getTempDir();
+    Path tempFile = Files.createTempFile(localTempDir, 
path.getFileName().toString(),  ".jar");
+    tempFile.toFile().deleteOnExit();
+    return tempFile;
   }
   
-  private void getCandidates_processCandidate(Path p) {
-    if (Files.isDirectory(p)) {
-      getCandidates_DescendDirectory(p);  
-    } else {
-      getCandidates_processFile(p);
+  private Path getTempDir() throws IOException {
+    if (tempDir == null) {
+      tempDir = Files.createTempDirectory("migrateJCas");
+      tempDir.toFile().deleteOnExit();
     }
-  }
-  
+    return tempDir;
+  }  
   
   private int indexOf_Type(int start) {
     int i = start;
@@ -1032,6 +1218,17 @@ private String outDirLog;
   private void addImport(String s) {
     cu.getImports().add(new ImportDeclaration(new NameExpr(s), false, false));
   }
+  
+  private void removeImport(String s) {
+    Iterator<ImportDeclaration> it = cu.getImports().iterator();
+    while (it.hasNext()) { 
+      ImportDeclaration impDcl = it.next();
+      if (impDcl.getName().toString().equals(s)) {
+        it.remove();
+        break;
+      }
+    } 
+  }
 
   /******************
    * AST Utilities
@@ -1173,13 +1370,6 @@ private String outDirLog;
     Misc.internalError(); return null;
   }
   
-  private Expression getUnenclosedExpr(Expression e) {
-    while (e instanceof EnclosedExpr) {
-      e = ((EnclosedExpr)e).getInner();
-    }
-    return e;
-  }
-  
   /**
    * Get the name of a field
    * @param e -
@@ -1208,18 +1398,57 @@ private String outDirLog;
       TypeImpl ti = 
TypeSystemImpl.staticTsi.getType(Misc.javaClassName2UimaTypeName(packageAndClassName));
       if (null != ti) {
         // is a built-in type
-        Pair<String, String> p = new Pair<String, 
String>(packageAndClassNameSlash, candidate.toString());
+        ClassnameAndPath p = new ClassnameAndPath(packageAndClassNameSlash, 
candidate);
         if (!ti.isFeatureFinal()) {
           extendableBuiltins.add(p);
         } else {
           nonExtendableBuiltins.add(p);
         }
-        v3 = false;
-        return; // skip further processing of this class 
+        v3 = false;   // skip further processing of this class
+        return;  
       }
 
-      c2ps.add(new Pair<String, String>(packageAndClassNameSlash, 
candidate.toString()));
+      c2ps.add(new ClassnameAndPath(packageAndClassNameSlash, candidate));
+      return;
+    }
+    return;
+  }
+
+  private Expression getExpressionFromStmt(Statement stmt) {
+    stmt = getStmtFromStmt(stmt);
+    if (stmt instanceof ExpressionStmt) {
+      return getUnenclosedExpr(((ExpressionStmt)stmt).getExpression());
+    }
+    return null;
+  }
+  
+  private Expression getUnenclosedExpr(Expression e) {
+    while (e instanceof EnclosedExpr) {
+      e = ((EnclosedExpr)e).getInner();
     }
+    return e;
+  }
+  
+  /**
+   * Unwrap (possibly nested) 1 statement blocks
+   * @param stmt -
+   * @return unwrapped (non- block) statement
+   */
+  private Statement getStmtFromStmt(Statement stmt) {
+    while (stmt instanceof BlockStmt) {
+      List<Statement> stmts = ((BlockStmt) stmt).getStmts();
+      if (stmts.size() == 1) {
+        stmt = stmts.get(0);
+        continue;
+      }
+      return null;
+    }
+    return stmt;
+  }
+  
+  private void addCastExpr(Statement stmt, Type castType) {
+    ReturnStmt rstmt = (ReturnStmt) stmt;
+    rstmt.setExpr(new CastExpr(castType, rstmt.getExpr()));
   }
   
   /********************
@@ -1235,7 +1464,7 @@ private String outDirLog;
   }
   
   private void migrationFailed(String reason) {
-    failedMigration.add(new Pair<String, String>(candidate.toString(), 
reason));
+    failedMigration.add(new PathAndReason(candidate, reason));
     v3 = false;    
   }
   
@@ -1255,7 +1484,7 @@ private String outDirLog;
   }
   
   private void reportNotJCasClass(String reason) {
-    nonJCasFiles.add(new Pair<String, String>(candidate.toString(), reason));
+    nonJCasFiles.add(new PathAndReason(candidate, reason));
     v3 = false;
   }
   
@@ -1264,11 +1493,15 @@ private String outDirLog;
   }
   
   private void reportDeletedCheckModified(String m) {
-    deletedCheckModified.add(new Pair<>(candidate.toString(), m));
+    deletedCheckModified.add(new PathAndReason(candidate, m));
+  }
+  
+  private void reportMismatchedFeatureName(String m) {
+    manualInspection.add(new PathAndReason(candidate, "This getter/setter name 
doesn't match internal feature name: " + m));
   }
   
   private void reportUnrecognizedV2Code(String m) {
-    migrationFailed("V2 code not recognized: " + m);
+    migrationFailed("V2 code not recognized:\n" + m);
   }
   
   private void reportPathWorkaround(String orig, String modified) {
@@ -1297,17 +1530,24 @@ private String outDirLog;
   
   private void writeV3(String data) throws IOException {
     String base = getBaseOutputPath(true, false);  // adds numeric suffix if 
dupls
+    data = fixImplementsBug(data);
     FileUtils.writeToFile(makePath(base), data);
   }
   
   private void printUsage() {
     System.out.println(
         "Usage: java org.apache.uima.migratev3.jcas.MigrateJCas \n"
-        + "  [-sourcesRoots 
<One-or-more-directories-separated-by-Path-separator>] [-desc 
<XmlDescriptor>]\n"
-        + "  [-classesRoots 
<One-or-more-directories-separated-by-Path-separator>]\n"
+        + "  [-sourcesRoots 
<One-or-more-directories-or-jars-separated-by-Path-separator>]\n"
+        + "  [-classesRoots 
<One-or-more-directories-or-jars-separated-by-Path-separator>]\n"
         + "  [-outputDirectory a-writable-directory-path\n"
+        + "  [-skipTypeCheck if specified, skips validing a found item by 
looking for the corresponding _Type file"
         + "  NOTE: either -sourcesRoots or -classesRoots is required, but only 
one may be specified.\n"
+        + "  NOTE: classesRoots are scanned for JCas classes, which are then 
decompiled, and the results processed like sourcesRoots\n"
         + "  NOTE: -outputDirectory is required\n");
   }
 
+  private static final Pattern implementsEmpty = Pattern.compile("implements  
\\{");
+  private String fixImplementsBug(String data) {
+    return implementsEmpty.matcher(data).replaceAll("{");
+  }
 }


Reply via email to