Author: schor
Date: Thu Jun 16 17:29:38 2016
New Revision: 1748765

URL: http://svn.apache.org/viewvc?rev=1748765&view=rev
Log:
[UIMA-4518] improve finding candidates, improve duplicate handling, avoid 
recomputing identical inputs, support individual class migration, improve use 
of and clarity around classpath

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=1748765&r1=1748764&r2=1748765&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
 Thu Jun 16 17:29:38 2016
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
 import java.lang.reflect.Modifier;
+import java.net.URLClassLoader;
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
@@ -34,13 +35,18 @@ import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
@@ -96,13 +102,14 @@ import com.github.javaparser.ast.type.Ty
 import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
 
 /**
- * A driver that scans given roots for source and/or class Java files that 
contain
- * JCas classes
+ * A driver that scans given roots for source and/or class Java files that 
contain JCas classes
  * 
  *   - identifies which ones appear to be JCas classes (heuristic)
  *     -- identifies which ones appear to be v2
  *       --- converts these to v3
  *
+ *   - also can receive a list of individual class names
+ *   
  * Creates summary and detailed reports of its actions.
  * 
  * Outputs converted files to an output file tree.
@@ -111,17 +118,41 @@ import com.github.javaparser.ast.visitor
  *   
  *   Directory structure, starting at -outputDirectory
  *     converted/
- *       x/y/z/javapath/.../Classname.java
- *       x/y/z/javapath/.../Classname.javav2Orig
- *       x/y/z/javapath/.../Classname.java1         // for duplicates
- *       x/y/z/javapath/.../Classname.java1v2Orig
+ *       v2/
+ *         x/y/z/javapath/.../Classname.java
+ *         x/y/z/javapath/.../Classname.java
+ *         ...
+ *       v3/
+ *         x/y/z/javapath/.../Classname.java
+ *         x/y/z/javapath/.../Classname.java
+ *         ...
+ *         1/                                   << for duplicates, each set is 
for identical dups, different sets for non-identical
+ *           x/y/z/javapath/.../Classname.java  << for duplicates, each set is 
for identical dups, different sets for non-identical  
+ *           x/y/z/javapath/.../Classname.java  << for duplicates, each set is 
for identical dups, different sets for non-identical
+ *           ...                                    
+ *         2/                                   << for duplicates, each set is 
for identical dups, different sets for non-identical  
+ *           x/y/z/javapath/.../Classname.java  << for duplicates, each set is 
for identical dups, different sets for non-identical
+ *           x/y/z/javapath/.../Classname.java  << for duplicates, each set is 
for identical dups, different sets for non-identical
+ *           ...
+ *         
  *     not-converted/
- *     log/
- *       converted/
- *       not-converted/
- *       duplicates/
- *         classname-to-sourcepath.csv  // includes Classname.java1 etc.
+ *     logs/
+ *       processed.txt
+ *       builtinsNotExtended.txt
+ *       ...
  *  
+ * Operates in one of two modes:
+ *   Mode 1: Given classes-roots and/or individual class names, and a 
classpath, 
+ *     scans the classes-routes looking for classes candidates, or iterate 
through the individual class names
+ *       - determines the class name,
+ *       - looks up the right "version" in the provided classpath, and 
decompiles that
+ *       - migrates that decompiled source.
+ *       -- duplicates are also processed. If different they are put into 
v3/nnn/etc.   
+ *       
+ *   Mode 2: Given sources-roots 
+ *     Duplicates are migrated, results are put into a v3/nnn/ 
rest-of-path-identical
+ * 
+ * Note: Each run clears the output directory before starting the migration.
  */
 public class MigrateJCas extends VoidVisitorAdapter<Object> {
   
@@ -133,6 +164,10 @@ public class MigrateJCas extends VoidVis
   
   private static final String SKIP_TYPE_CHECK = "-skipTypeCheck";
   
+  private static final String MIGRATE_CLASSPATH = "-migrateClasspath"; 
+  
+  private static final String CLASSES = "-classes"; // individual classes to 
migrate, get from supplied classpath
+  
   private Path tempDir = null;
     
   private boolean isSource = false;
@@ -144,7 +179,7 @@ public class MigrateJCas extends VoidVis
   private String className;  // (omitting package)
   private String packageAndClassNameSlash;  
   private final Set<String> usedPackageAndClassNames = new HashSet<>();
-  private String currentPackageAndClassNameSlash;
+  private int packageAndClassNameSlash_i;
   
   /** includes trailing / */
   private String outputDirectory;
@@ -153,104 +188,44 @@ public class MigrateJCas extends VoidVis
   /** includes trailing / */
   private String outDirSkipped;
   /** includes trailing / */
-private String outDirLog;
+  private String outDirLog;
   
   
   private String[] roots;
   
   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 ClassLoader migrateClassLoader = null;
   
-  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 String migrateClasspath = null;
   
-  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;
+  private String individualClasses = null;  // to decompile
+  
+  private class ConvertedSource {
+    String rOrigSource;  // remembered original source
+    byte[] rOrigBytes;   // remembered original bytes
+    List<Path> paths;
+    ConvertedSource(String origSource, byte[] origBytes, Path path) {
+      this.rOrigSource = origSource;
+      this.rOrigBytes = origBytes;
+      paths = new ArrayList<>();
+      add(path);
     }
-    @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;
+    
+    void add(Path path) {
+      paths.add(path);
     }
   }
   
-  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(); }
-  }
+  /**
+   * A map from classnames (fully qualified, with slashes) to a list of 
converted sources
+   *   one per non-duplicated source
+   */
+  private Map<String, List<ConvertedSource>> classname2multiSources = new 
TreeMap<>();
   
+  private Map<byte[], String> origBytesToClassName = new HashMap<>();
+  private Map<String, String> origSourceToClassName = new HashMap<>();
+    
   /************************************
    * Reporting
    ************************************/
@@ -262,8 +237,8 @@ private String outDirLog;
   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<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
@@ -305,6 +280,10 @@ private String outDirLog;
 
   private boolean isSkipTypeCheck = false;
 
+  
+  private byte[] origBytes;   // set by getSource()
+  private String origSource;  // set by getSource()
+  private String alreadyDone; // the slashifiedClassName
 
   public MigrateJCas() {
   }
@@ -334,7 +313,7 @@ private String outDirLog;
     } else {
       printUsage();
       System.exit(2);
-    }    
+    }
     
     // clear output dir
     FileUtils.deleteRecursive(new File(outputDirectory));
@@ -344,14 +323,28 @@ private String outDirLog;
       int i = 1;
       System.out.print("    ");
       for (Path c : getCandidates(clp, root)) {
-        System.out.print(".");
-        if ((i % 50) == 0) System.out.format("%n%4d",i);
-        candidate = c;
-        String source = getSource(candidate);
+        String source = prepare(c);
+        migrate(source);
+        if ((i % 50) == 0) System.out.format("%n%4d", Integer.valueOf(i));
+        i++;
+      }
+      System.out.println("");
+    }
+    
+    if (individualClasses != null) {
+      String[] cn = individualClasses.split(File.pathSeparator);
+      System.out.println("Migrating individual classes: " );
+      int i = 1;
+      System.out.print("    ");
+      for (String classname : cn) {
+        String source = prepareIndividual(classname);
         migrate(source);
+        if ((i % 50) == 0) System.out.format("%n%4d", Integer.valueOf(i));
         i++;
       }
+      System.out.println("");
     }
+    
     report();
   }
   
@@ -359,18 +352,35 @@ private String outDirLog;
     return clp.getParamArgument(kind).split("\\" + File.pathSeparator);
   }
   
+  /**
+   * If working with .class files:
+   *   - read the byte array
+   *   - see if it has already been decompiled; if so, return false
+   *   
+   * Side effect, set origSource and origBytes and alreadyDone
+   * @param path
+   * @return the source to maybe migrate
+   */
   private String getSource(Path path) {
     try {
-      String s;
+      origSource = null;
+      origBytes = null;
       if (isSource) {
-        s = FileUtils.reader2String(Files.newBufferedReader(path));
+        origSource = FileUtils.reader2String(Files.newBufferedReader(path));
+        alreadyDone = origSourceToClassName.get(origSource);
 //        System.out.println("debug read " + s.length());
       } else {
-        // read in bytes and decompile the class to a string
-        byte[] b = Files.readAllBytes(path);
-        s = decompile(b);
+        origBytes = Files.readAllBytes(path);
+        alreadyDone = origBytesToClassName.get(origBytes);
+        if (null == alreadyDone) {
+          origSource = decompile(origBytes);
+        }
       }
-      return s;
+      if (alreadyDone != null) {
+        return null;
+      }
+            
+      return origSource;
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -404,49 +414,110 @@ private String outDirLog;
    * @param source - the source, either directly from a .java file, or a 
decompiled .class file
    */
   private void migrate(String source) {
-    v3 = true;  // preinit
-    v2 = false;
-    featNames.clear();
-    fi_fields.clear();
-    
-//    System.out.println("Migrating source before migration:\n");
-//    System.out.println(source);
-//    System.out.println("\n\n\n");
 
-    StringReader sr = new StringReader(source);
-    try {
-      cu = JavaParser.parse(sr, true);
+    if (alreadyDone == null) {
+      v3 = true;  // preinit
+      v2 = false;
+      featNames.clear();
+      fi_fields.clear();
       
-      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 (v3) {
-        removeImport("org.apache.uima.jcas.cas.TOP_Type");
+  //    System.out.println("Migrating source before migration:\n");
+  //    System.out.println(source);
+  //    System.out.println("\n\n\n");
+  
+      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 (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) {
+            throw new RuntimeException();
+          }
+          classMembers.addAll(positionOfFirstConstructor, fi_fields);
+        }      
+                
+        if (isSource) {
+          origSourceToClassName.put(origSource, packageAndClassNameSlash);
+        } else {
+          origBytesToClassName.put(origBytes, packageAndClassNameSlash);
+        }
+
+        boolean identicalFound = collectInfoForReports();
+
+        if (!identicalFound) {  // don't write out identicals
+          getBaseOutputPath();
+          if (v2) { 
+            writeV2Orig(source, v3);
+          }
+          if (v3) {
+            String s = cu.toString();
+            writeV3(s);  
+          }
+          System.out.println(".");
+        } else {
+          System.out.println("d");
+        }
+      } catch (ParseException e) {
+        reportParseException();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
       }
-      
-      if (v3 && fi_fields.size() > 0) {
-        List<BodyDeclaration> classMembers = cu.getTypes().get(0).getMembers();
-        int positionOfFirstConstructor = findConstructor(classMembers);
-        if (positionOfFirstConstructor < 0) {
-          throw new RuntimeException();
-        }
-        classMembers.addAll(positionOfFirstConstructor, fi_fields);
-      }      
-      // Reporting
-      getBaseOutputPath();
-      if (v2) 
-        writeV2Orig(source, v3);
-      if (v3)
-        writeV3(cu.toString());       
-    } catch (ParseException e) {
-      reportParseException();
-    } catch (IOException e) {
-      throw new RuntimeException(e);
+    } else {
+      collectInfoForReports();
+      System.out.println("d");
     }
   }
+
+  /**
+   * Add this instance to the tracking information for multiple versions 
(identical or not) of a class
+   * @return true if this is an identical duplicate of one already done
+   */
+  
+  private boolean collectInfoForReports() {
+    String fqcn = (alreadyDone == null) 
+                    ? packageAndClassNameSlash
+                    : alreadyDone;
+    // for a given fully qualified class name (slashified), 
+    //   find the list of ConvertedSources - one per each different version
+    //     create it if null
+    List<ConvertedSource> convertedSources = classname2multiSources.get(fqcn);
+    if (convertedSources == null) {
+      convertedSources = new ArrayList<>();
+      classname2multiSources.put(fqcn, convertedSources);
+    }
+    
+    // search to see if this instance already in the set
+    //   if so, add the path to the set of identicals
+    boolean found = false;
+    for (ConvertedSource cs : convertedSources) {
+      if ((isSource && cs.rOrigSource.equals(origSource)) ||
+          (!isSource && Arrays.equals(cs.rOrigBytes, origBytes))) {
+        cs.add(candidate);
+        found = true;
+        break;
+      }
+    }
+
+    
+    if (!found) {
+      ConvertedSource cs = new ConvertedSource(origSource, origBytes, 
candidate);
+      convertedSources.add(cs);
+    }    
+    
+    return found;
+  }
   
   /******************
    *  Visitors
@@ -874,9 +945,10 @@ private String outDirLog;
       reportPaths("Builtin Extendable JCas classes - skipped - need manual 
checking to see if they are modified",
           "extendableBuiltins.txt", extendableBuiltins);
       
-      computeDuplicates();
-      reportPaths("Report of duplicates - not identical", 
"nonIdenticalDuplicates.txt", nonIdenticalDuplicates);
-      reportPaths("Report of duplicates - identical", 
"identicalDuplicates.txt", identicalDuplicates);
+//      computeDuplicates();
+//      reportPaths("Report of duplicates - not identical", 
"nonIdenticalDuplicates.txt", nonIdenticalDuplicates);
+//      reportPaths("Report of duplicates - identical", 
"identicalDuplicates.txt", identicalDuplicates);
+      reportDuplicates();
       
       reportPaths("Report of processed files", "processed.txt", c2ps);
       reportPaths("Builtin non-Extendable JCas classes - skipped", 
"builtinsNotExtendable.txt", nonExtendableBuiltins);
@@ -885,61 +957,61 @@ private String outDirLog;
     }
   }
   
-  private void computeDuplicates() {
-    List<ClassnameAndPath> toCheck = new ArrayList<>(c2ps);
-    toCheck.addAll(extendableBuiltins);
-    sortReport2(toCheck);
-    ClassnameAndPath prevP = new ClassnameAndPath(null, null);
-    List<ClassnameAndPath> sameList = new ArrayList<>();
-    boolean areAllEqual = true;
-    
-    for (ClassnameAndPath p : toCheck) {
-      if (!p.getFirst().equals(prevP.getFirst())) {
-        
-        addToIdenticals(sameList, areAllEqual);
-        sameList.clear();
-        areAllEqual = true;
-        
-        prevP = p;
-        continue;
-      }
-      
-      // have 2nd or subsequent same class
-      if (sameList.size() == 0) {
-        sameList.add(prevP);
-      }
-      sameList.add(p);
-      if (areAllEqual) {
-        if (isFilesMiscompare(p.path, prevP.path)) {
-          areAllEqual = false;
-        }
-      }      
-    }
-    
-    addToIdenticals(sameList, areAllEqual);    
-  }
-  
-  /**
-   * 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);
-  }
-  
-  private void addToIdenticals(List<ClassnameAndPath> sameList, boolean 
areAllEqual) {
-    if (sameList.size() > 0) {
-      if (areAllEqual) {
-        identicalDuplicates.addAll(sameList);
-      } else {
-        nonIdenticalDuplicates.addAll(sameList);
-      }
-    }
-  }
+//  private void computeDuplicates() {
+//    List<ClassnameAndPath> toCheck = new ArrayList<>(c2ps);
+//    toCheck.addAll(extendableBuiltins);
+//    sortReport2(toCheck);
+//    ClassnameAndPath prevP = new ClassnameAndPath(null, null);
+//    List<ClassnameAndPath> sameList = new ArrayList<>();
+//    boolean areAllEqual = true;
+//    
+//    for (ClassnameAndPath p : toCheck) {
+//      if (!p.getFirst().equals(prevP.getFirst())) {
+//        
+//        addToIdenticals(sameList, areAllEqual);
+//        sameList.clear();
+//        areAllEqual = true;
+//        
+//        prevP = p;
+//        continue;
+//      }
+//      
+//      // have 2nd or subsequent same class
+//      if (sameList.size() == 0) {
+//        sameList.add(prevP);
+//      }
+//      sameList.add(p);
+//      if (areAllEqual) {
+//        if (isFilesMiscompare(p.path, prevP.path)) {
+//          areAllEqual = false;
+//        }
+//      }      
+//    }
+//    
+//    addToIdenticals(sameList, areAllEqual);    
+//  }
+  
+//  /**
+//   * 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);
+//  }
+  
+//  private void addToIdenticals(List<ClassnameAndPath> sameList, boolean 
areAllEqual) {
+//    if (sameList.size() > 0) {
+//      if (areAllEqual) {
+//        identicalDuplicates.addAll(sameList);
+//      } else {
+//        nonIdenticalDuplicates.addAll(sameList);
+//      }
+//    }
+//  }
  
   private Path makePath(String name) throws IOException {
     Path p = Paths.get(name);
@@ -959,6 +1031,51 @@ private String outDirLog;
     return p;
   }
   
+  private void reportDuplicates() {
+    List<Entry<String, List<ConvertedSource>>> nonIdenticals = new 
ArrayList<>();
+    List<ConvertedSource> onlyIdenticals = new ArrayList<>();
+    for (Entry<String, List<ConvertedSource>> e : 
classname2multiSources.entrySet()) {
+      List<ConvertedSource> convertedSourcesFor1class = e.getValue();
+      if (convertedSourcesFor1class.size() > 1) {
+        // have multiple non-identical sources for one class
+        nonIdenticals.add(e);
+      } else {
+        ConvertedSource cs = convertedSourcesFor1class.get(0);
+        if (cs.paths.size() > 1) {
+          // have multiple (only) identical sources for one class
+          onlyIdenticals.add(cs);
+        }
+      }
+    }
+    
+    if (nonIdenticals.size() == 0) {
+      if (onlyIdenticals.size() == 0) {
+        System.out.println("No duplicates found.");
+      } else {
+        // report identical duplicates
+        System.out.println("Identical duplicates (only):");
+        for (ConvertedSource cs : onlyIdenticals) {
+          for (Path path : cs.paths) {
+            System.out.println("  " + vWithFileSys(path));
+          }
+        }
+      }
+    } else {
+      System.out.println("Report of non-identical duplicates");
+      for (Entry<String, List<ConvertedSource>> nonIdentical : nonIdenticals) {
+        System.out.println("  classname: " + nonIdentical.getKey());
+        int i = 1;
+        for (ConvertedSource cs : nonIdentical.getValue()) {
+          System.out.println("    version " + i);
+          for (Path path : cs.paths) {
+            System.out.println("      " + vWithFileSys(path));
+          }
+          i++;
+        }
+      }
+    }
+  }
+  
   private void reportPaths(String title, String fileName, List<? extends 
Report2> items) throws IOException {
     if (items.size() == 0) {
       System.out.println("No " + title);
@@ -980,14 +1097,7 @@ private String outDirLog;
       int i = 1;
       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);
+        String s = String.format("%5d %-" +max+ "s %s%n", Integer.valueOf(i), 
p.getFirst(), vWithFileSys(v));
         bw.write(s);
         System.out.print(s);
         i++;
@@ -995,7 +1105,18 @@ private String outDirLog;
       System.out.println("");
     } // end of try-with-resources
   }
-    
+  
+  private String vWithFileSys(Object v) {
+    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();
+      }
+    }
+    return v.toString();
+  }
+  
   private void sortReport2(List<? extends Report2> items) {
     Collections.sort(items, 
         (o1, o2) -> {
@@ -1015,12 +1136,9 @@ private String outDirLog;
    *        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
+   *   - the path includes the "file system".
    * @param root
-   * @return list of Paths
+   * @return list of Paths to walk
    * @throws IOException
    */
   private List<Path> getCandidates(CommandLineParser clp, String root) {
@@ -1036,32 +1154,39 @@ private String outDirLog;
     }
     
     Collections.sort(candidates, pathComparator);
+   
+    // the collection potentially includes inner class files
     
     List<Path> c = new ArrayList<>();  // candidates
     final int nbrOfPaths = candidates.size();
     if (nbrOfPaths == 0) {
       return c;
     }
-    for (int i = 0; i < nbrOfPaths;) {
+    for (int i = 0; i < nbrOfPaths; i++) {
+      
+      // skip files that end with _Type or 
+      //   appear to be inner files: have names with a "$" char
+      candidate = candidates.get(i);
+      String lastPartOfPath = candidate.getFileName().toString();
+      if (lastPartOfPath.endsWith(isSource ? "_Type.java" : "_Type.class")) {
+        continue;  // skip the _Type files
+      }
+      
+      if (lastPartOfPath.contains("$")) {
+        continue;  // inner class
+      }
+      
       if (isSkipTypeCheck) {
-        candidate = candidates.get(i);
-        if (candidate.getFileName().endsWith(isSource ? "_Type.java" : 
"_Type.class")) {
-          continue;  // skip the _Type files
-        }
         c.add(candidate);
-        i++;
       } else {
         // 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 returning because doing 
_Type checking and no further _Types");
-          for (Path x : c) {
-            System.out.println("debug   " + x);
-          }
-          return c;
-        } 
-        addIfPreviousIsSameName(c, next_Type);
-        i = next_Type + 1;  
+        //   in the sort order, these follow the file without the _Type suffix
+        //   but perhaps there are other names inbetween
+        boolean has_Type = has_Type(candidate, i + 1);  // look for the next 
_Type file starting at position i + 1
+        if (!has_Type) {
+          continue;  // not a JCas class
+        }
+        c.add(candidate);  
       }
     }
     return c;  
@@ -1074,27 +1199,27 @@ private String outDirLog;
     }
   };
   
-  // there may be several same-name roots not quite right
-  //   xxx_Type$1.class
-  
-  private void addIfPreviousIsSameName(List<Path> c, int i) {
-    if (i == 0) return;
-    String _Type = candidates.get(i).toString();
-//    String prev = r.get(i-1).getPath();
-    String prefix = _Type.substring(0, _Type.length() - ("_Type." + (isSource 
? "java" : "class")).length());
-    i--;
-    while (i >= 0) {
-      String s = candidates.get(i).toString();
-      if ( ! s.startsWith(prefix)) {
-        break;
-      }
-      if (s.substring(prefix.length()).equals((isSource ? ".java" : 
".class"))) {
-        c.add(candidates.get(i));
-        break;
-      }
-      i--;
-    }
-  }
+//  // there may be several same-name roots not quite right
+//  //   xxx_Type$1.class
+//  
+//  private void addIfPreviousIsSameName(List<Path> c, int i) {
+//    if (i == 0) return;
+//    String _Type = candidates.get(i).toString();
+////    String prev = r.get(i-1).getPath();
+//    String prefix = _Type.substring(0, _Type.length() - ("_Type." + 
(isSource ? "java" : "class")).length());
+//    i--;
+//    while (i >= 0) {
+//      String s = candidates.get(i).toString();
+//      if ( ! s.startsWith(prefix)) {
+//        break;
+//      }
+//      if (s.substring(prefix.length()).equals((isSource ? ".java" : 
".class"))) {
+//        c.add(candidates.get(i));
+//        break;
+//      }
+//      i--;
+//    }
+//  }
     
   
   /**
@@ -1112,7 +1237,7 @@ private String outDirLog;
     try {
 //      URI pathUri = path.toUri();
       String pathString = path.toString();
-      if (pathString.endsWith(".jar")) {  // path.endsWith does mean this !!
+      if (pathString.endsWith(".jar")) {  // path.endsWith does not mean this 
!!
         if (!path.getFileSystem().equals(FileSystems.getDefault())) {        
           // embedded Jar: extract to temp
           Path out = getTempOutputPath(path);
@@ -1120,7 +1245,8 @@ private String outDirLog;
 //          embeddedJars.add(new PathAndPath(path, out));
           path = out;
         }
-        FileSystem jfs = FileSystems.newFileSystem(path, null);
+        // experiment - see if this makes a copy
+        FileSystem jfs = FileSystems.newFileSystem(Paths.get(path.toUri()), 
null);
         Path start = jfs.getPath("/");
         try (Stream<Path> stream = Files.walk(start)) {  // needed to release 
file handles
           stream.forEachOrdered(
@@ -1153,20 +1279,26 @@ private String outDirLog;
     return tempDir;
   }  
   
-  private int indexOf_Type(int start) {
-    int i = start;
+  private boolean has_Type(Path cand, int start) {
     if (start >= candidates.size()) {
-      return -1;
+      return false;
     }
+
+    String sc = cand.toString();
+    String sc_minus_suffix = sc.substring(0,  sc.length() - ( isSource ? 
".java".length() : ".class".length())); 
+    String sc_Type = sc_minus_suffix + ( isSource ? "_Type.java" : 
"_Type.class");
+    // a string which sorts beyond the candidate + a suffix of "_"
+    String s_end = sc_minus_suffix + (char) (((int)'_') + 1);
     for (Path p : candidates.subList(start,  candidates.size())) {
       String s = p.toString();
-      if (s.endsWith("_Type.java") || 
-          s.endsWith("_Type.class")) {
-        return i;
+      if (s_end.compareTo(s) < 0) {
+        return false;  // not found, we're already beyond where it would be 
found
+      }
+      if (s.equals(sc_Type)) {
+        return true;
       }
-      i++;
     }
-    return -1;
+    return false;
   }
   
   
@@ -1176,6 +1308,8 @@ private String outDirLog;
     parser.addParameter(CLASS_FILE_ROOTS, true);
     parser.addParameter(OUTPUT_DIRECTORY, true);
     parser.addParameter(SKIP_TYPE_CHECK, false);
+    parser.addParameter(MIGRATE_CLASSPATH, true);
+    parser.addParameter(CLASSES, true);
     return parser;
   }
 
@@ -1207,14 +1341,44 @@ private String outDirLog;
       System.err.println("-outputDirectory is a required parameter, must be a 
path to a writable file directory.");
       return false;
     }
+    
+    if (clp.isInArgsList(MIGRATE_CLASSPATH)) {
+      migrateClasspath = clp.getParamArgument(MIGRATE_CLASSPATH);
+    }
+    
+    if (clp.isInArgsList(CLASSES)) {
+      individualClasses = clp.getParamArgument(CLASSES);
+    }
     return true;
   }
   
   private String decompile(byte[] b) {
-    UimaDecompiler ud = new UimaDecompiler();
+    ClassLoader cl = getClassLoader();
+    UimaDecompiler ud = new UimaDecompiler(cl, null);
     return ud.decompile(b);
   }
   
+  private String decompile(String classname) {
+    ClassLoader cl = getClassLoader();
+    UimaDecompiler ud = new UimaDecompiler(cl, null);
+    return ud.decompileToString(classname);
+  }
+  
+//  private String getFullyQualifiedClassNameWithSlashes(byte[] b) {
+//    ClassLoader cl = getClassLoader();
+//    UimaDecompiler ud = new UimaDecompiler(cl, null);
+//    return ud.extractClassNameSlashes(b);    
+//  }
+  
+  private ClassLoader getClassLoader() {
+    if (null == migrateClassLoader) {
+      migrateClassLoader = (migrateClasspath == null) 
+                      ? this.getClass().getClassLoader()
+                      : new 
URLClassLoader(Misc.classpath2urls(migrateClasspath));
+    }
+    return migrateClassLoader;
+  }
+  
   private void addImport(String s) {
     cu.getImports().add(new ImportDeclaration(new NameExpr(s), false, false));
   }
@@ -1505,22 +1669,28 @@ private String outDirLog;
   }
   
   private void reportPathWorkaround(String orig, String modified) {
-    
+    pathWorkaround.add(new String1AndString2(orig, modified));
   }
   
   /***********************************************/
     
   private void getBaseOutputPath() {
     String s = packageAndClassNameSlash;
-    int i = 1;
+    int i = 0;
     while (!usedPackageAndClassNames.add(s)) {
-      s = packageAndClassNameSlash + i++;
+      i = i + 1;
+      s = packageAndClassNameSlash + "_dupid_" + i;
     }
-    currentPackageAndClassNameSlash = s;
+    packageAndClassNameSlash_i = i;
   }
   
   private String getBaseOutputPath(boolean wasConverted, boolean isV2) {
-    return (wasConverted ? outDirConverted : outDirSkipped) + (isV2 ? "v2/" : 
"v3/") + currentPackageAndClassNameSlash + ".java";
+    return (wasConverted ? outDirConverted : outDirSkipped) + (isV2 ? "v2/" : 
"v3/") 
+        + ((packageAndClassNameSlash_i > 0) 
+             ? (Integer.toString(packageAndClassNameSlash_i) + "/") 
+             : "")
+        + packageAndClassNameSlash 
+        + ".java";
   }
   
   private void writeV2Orig(String data, boolean wasConverted) throws 
IOException {
@@ -1539,10 +1709,14 @@ private String outDirLog;
         "Usage: java org.apache.uima.migratev3.jcas.MigrateJCas \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"
+        + "  [-outputDirectory a-writable-directory-path (required)\n"
+        + "  [-migrateClasspath a-class-path to use in decompiling, required 
if -classesRoots is specified, or \n"
+        + "                        to provide disambiguation information when 
-sourcesRoots is specified\n"
+        + "                        and more than one instance of a class is 
found in the roots]\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"
+        + "        The decompiling requires that the classes being scanned be 
on the migrateClasspath when this is invoked.\n"
         + "  NOTE: -outputDirectory is required\n");
   }
 
@@ -1550,4 +1724,125 @@ private String outDirLog;
   private String fixImplementsBug(String data) {
     return implementsEmpty.matcher(data).replaceAll("{");
   }
+  
+//  /**
+//   * Called after class is migrated
+//   * Given a path to a class (source or class file), 
+//   * return the URL to the class as found in the classpath.
+//   *   This returns the "first" one found in the classpath, in the case of 
duplicates.
+//   * @param path
+//   * @return the location of the class in the class path
+//   */
+//  private URL getPathForClass(Path path) {
+//    return (null == packageAndClassNameSlash) 
+//             ? null 
+//             : migrateClassLoader.getResource(packageAndClassNameSlash + 
".class");
+//  }
+  
+  /**
+   * prepare to migrate one class
+   */
+  private String  prepare(Path c) {
+    candidate = c;
+    packageName = null;
+    className = null;
+    packageAndClassNameSlash = null;
+    cu = null;
+    return getSource(c);
+  }
+  
+  private String prepareIndividual(String classname) {
+    candidate = Paths.get(classname); // a pseudo path
+    packageName = null;
+    className = null;
+    packageAndClassNameSlash = null;
+    cu = null;
+    return decompile(classname); // always look up in classpath
+                                 // to decompile individual source - put in 
sourcesRoots
+  }
+  
+  /*********************************************************************
+   * Reporting classes
+   *********************************************************************/
+  
+  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;
+    String1AndString2(String s1, String s2) {
+      this.s1 = s1;
+      this.s2 = s2;
+    }
+    @Override
+    public Comparable<String> getFirst() { return s1; }
+    @Override
+    public Comparable<String> getSecond() { return s2; }
+    @Override
+    int getFirstLength() { return s1.toString().length(); }
+  }
+
 }



Reply via email to