You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2016/03/18 20:35:03 UTC

svn commit: r1735666 - in /uima/uimaj/uimaj-v3migration-jcas: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/uima/ src/main/java/org/apache/uima/jcas/ src/main/java/org/apache/uima/jcas/cas/ src/m...

Author: schor
Date: Fri Mar 18 19:35:03 2016
New Revision: 1735666

URL: http://svn.apache.org/viewvc?rev=1735666&view=rev
Log:
[UIMA-4518] add initial try at migration tooling for JCas classes from v2 to v3 form

Added:
    uima/uimaj/uimaj-v3migration-jcas/pom.xml
    uima/uimaj/uimaj-v3migration-jcas/src/
    uima/uimaj/uimaj-v3migration-jcas/src/main/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/AnnotationBase.java
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP.java
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP_Type.java
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/Annotation.java
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/
    uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java
    uima/uimaj/uimaj-v3migration-jcas/src/main/resources/
    uima/uimaj/uimaj-v3migration-jcas/src/test/
    uima/uimaj/uimaj-v3migration-jcas/src/test/java/
Modified:
    uima/uimaj/uimaj-v3migration-jcas/   (props changed)

Propchange: uima/uimaj/uimaj-v3migration-jcas/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Fri Mar 18 19:35:03 2016
@@ -0,0 +1,3 @@
+.settings
+.classpath
+.project

Added: uima/uimaj/uimaj-v3migration-jcas/pom.xml
URL: http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/pom.xml?rev=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/pom.xml (added)
+++ uima/uimaj/uimaj-v3migration-jcas/pom.xml Fri Mar 18 19:35:03 2016
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   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.    
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>org.apache.uima</groupId>
+    <artifactId>uimaj-parent</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
+    <relativePath>../uimaj-parent/pom.xml</relativePath>
+  </parent>
+
+	<artifactId>uimaj-v3migration-jcas</artifactId>
+	<packaging>jar</packaging>
+	<name>Apache UIMA Base: ${project.artifactId}</name>
+  <description>Tools for migrating to V3 for JCas</description>
+  <url>${uimaWebsiteUrl}</url>
+  
+  <scm>
+    <connection>
+      scm:svn:http://svn.apache.org/repos/asf/uima/uimaj/trunk/uimaj-jcas
+    </connection>
+    <developerConnection>
+      scm:svn:https://svn.apache.org/repos/asf/uima/uimaj/trunk/uimaj-jcas
+    </developerConnection>
+    <url>
+      http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-jcas
+    </url>
+  </scm>
+  
+  <properties>
+    <uimaScmProject>uimaj-jcas</uimaScmProject>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.uima</groupId>
+      <artifactId>uimaj-core</artifactId>
+      <version>${project.parent.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.uima</groupId>
+      <artifactId>uimaj-test-util</artifactId>
+      <version>${project.parent.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.github.javaparser</groupId>
+      <artifactId>javaparser-core</artifactId>
+      <version>2.3.0</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.bitbucket.mstrobel</groupId>
+      <artifactId>procyon-compilertools</artifactId>
+      <version>0.5.28</version>
+    </dependency>
+    
+    <!-- for reading / transforming / generating JCas cover classes -->
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>5.0.4</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-tree</artifactId>
+      <version>5.0.4</version>
+    </dependency>
+    
+  </dependencies>
+  
+  <build>
+  </build>
+</project>
\ No newline at end of file

Added: uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/AnnotationBase.java
URL: http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/AnnotationBase.java?rev=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/AnnotationBase.java (added)
+++ uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/AnnotationBase.java Fri Mar 18 19:35:03 2016
@@ -0,0 +1,122 @@
+/*
+ * 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.uima.jcas.cas;
+
+import org.apache.uima.cas.AnnotationBaseFS;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.CASRuntimeException;
+import org.apache.uima.cas.impl.CASImpl;
+import org.apache.uima.cas.impl.TypeImpl;
+import org.apache.uima.cas.impl.TypeSystemImpl;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.jcas.JCasRegistry;
+
+/**
+ * the JCas class model for the CAS type uima.cas.AnnotationBase. 
+ * The AnnotationBase type defines one system-used feature which 
+ * specifies for an annotation the subject of analysis (Sofa) to which it refers. 
+ * Various annotation types (including the built-in uima.tcas.Annotation)
+ * may be defined as subtypes of this type.
+ * 
+ * uima.tcas.Annotation is a subtype of this type, appropriate for
+ * Subjects of Analysis which are text strings.  Other (not-built-in)
+ * subtypes may be defined for other kinds of Subjects of Analysis.  For instance
+ * an audio sample Subject of Analysis might define a start and end position as time points 
+ * in the stream.  An image Subject of Analysis might define rectangular coordiantes
+ * describing a sub-area of the image.
+ * 
+ * If you are defining a type which needs a reference to the Subject of Analysis
+ * (which is view-specific),
+ * it should be a subtype of this base type.
+ */
+public class AnnotationBase extends TOP implements AnnotationBaseFS {
+
+  public final static int typeIndexID = JCasRegistry.register(AnnotationBase.class);
+
+  public final static int type = typeIndexID;
+
+  @Override
+  public int getTypeIndexID() {
+    return typeIndexID;
+  }
+  
+  // private final static int _FI_sofa = JCasRegistry.registerFeature();  // only for journal-able or corruptable feature slots
+
+  /* local data */
+  public final static int _FI_sofa = TypeSystemImpl.getAdjustedFeatureOffset("sofa");
+  
+//  private final Sofa _F_sofa;
+  
+  // Never called. Disable default constructor
+  protected AnnotationBase() {
+  }
+
+  // not used, just here to make decompiling of v2 work
+  protected AnnotationBase(int addr, TOP_Type type) {
+    super();
+  }
+ 
+// /* Internal - Constructor used by generator */
+//  public AnnotationBase(int addr, TOP_Type type) {
+//    super(addr, type);
+//  }
+
+  public AnnotationBase(JCas jcas) {
+    super(jcas);
+    if (_casView.isBaseCas()) {
+      throw new CASRuntimeException(CASRuntimeException.DISALLOW_CREATE_ANNOTATION_IN_BASE_CAS, this.getClass().getName());
+    }
+    // no journaling, no index corruption checking
+    _refData[_FI_sofa] = _casView.getSofa();
+  }
+
+  /**
+   * used by generator
+   * Make a new AnnotationBase
+   * @param c -
+   * @param t -
+   */
+
+  public AnnotationBase(TypeImpl t, CASImpl c) {
+    super(t, c);
+    if (_casView.isBaseCas()) {
+      throw new CASRuntimeException(CASRuntimeException.DISALLOW_CREATE_ANNOTATION_IN_BASE_CAS, this.getClass().getName());
+    }
+    // no journaling, no index corruption checking
+    _refData[_FI_sofa] = _casView.getSofa();
+  }
+
+  // *------------------*
+  // * Feature: sofa
+  // * Sofa reference of the annotation
+  /*
+   * getter for sofa - gets Sofaref for annotation
+   */
+  public Sofa getSofa() { return (Sofa) _getFeatureValueNc(_FI_sofa); }
+  
+  // There is no setter for this
+  //   The value is set and is fixed when this is created
+    
+  @Override
+  public CAS getView() {
+    return _casView;
+  }
+   
+}

Added: uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP.java
URL: http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP.java?rev=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP.java (added)
+++ uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP.java Fri Mar 18 19:35:03 2016
@@ -0,0 +1,101 @@
+/*
+ * 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.uima.jcas.cas;
+
+import org.apache.uima.cas.impl.CASImpl;
+import org.apache.uima.cas.impl.FeatureStructureImplC;
+import org.apache.uima.cas.impl.TypeImpl;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.jcas.JCasRegistry;
+import org.apache.uima.jcas.impl.JCasImpl;
+
+// *********************************
+// * Implementation of TOP *
+// *********************************
+/**
+ * The JCas Class model corresponding to the Cas TOP type. This type is the super type of all
+ * JCas feature structures.
+ */
+public class TOP extends FeatureStructureImplC {
+
+	/**
+   * Each cover class when loaded sets an index. Used in the JCas typeArray to go from the cover
+   * class or class instance to the corresponding instance of the _Type class
+   */
+	public final static int typeIndexID = JCasRegistry.register(TOP.class);  
+
+	public final static int type = typeIndexID;
+
+	/**
+   * 
+   * @return the type array index
+   */
+	// can't be factored - refs locally defined field
+	@Override
+	public int getTypeIndexID() {
+		return typeIndexID;
+	}
+
+  /** used to reference the corresponding TOP_Type instance */
+  public TOP_Type jcasType;
+
+  // maybe called to create unique removed marker, but not otherwise used
+  public TOP() {
+  }
+  
+  /**
+   * For use when creating a search key
+   * @param id -
+   */
+  private TOP(int id) {
+    super(id);
+  }
+
+  /**
+   * used by generator
+   * Make a new TOP
+   * @param c -
+   * @param t -
+   */
+
+	public TOP(TypeImpl t, CASImpl c) {
+	  super(t, c);
+	}
+
+	/**
+	 * This version is used by user code new XXX(jcas)
+	 * @param jcas
+	 */
+	public TOP(JCas jcas) {
+	  super((JCasImpl) jcas);	  
+	}
+
+	// not used, just here to make decompiling of v2 work
+	protected TOP(int addr, TOP_Type type) {
+	  super();
+	}
+	
+  public static TOP createSearchKey(int id) {
+    return new TOP(id);
+  }
+
+	final public static TOP singleton = new TOP();
+
+}

Added: uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP_Type.java
URL: http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP_Type.java?rev=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP_Type.java (added)
+++ uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/cas/TOP_Type.java Fri Mar 18 19:35:03 2016
@@ -0,0 +1,29 @@
+/*
+ * 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.uima.jcas.cas;
+
+
+
+// *********************************
+// * Implementation of TOP_Type    *
+// * Only for supporting decompiling of v2 JCas classes
+// *********************************
+
+public class TOP_Type {}

Added: uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/Annotation.java
URL: http://svn.apache.org/viewvc/uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/Annotation.java?rev=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/Annotation.java (added)
+++ uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/jcas/tcas/Annotation.java Fri Mar 18 19:35:03 2016
@@ -0,0 +1,199 @@
+/*
+ * 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.uima.jcas.tcas;
+
+import org.apache.uima.cas.admin.LinearTypeOrder;
+import org.apache.uima.cas.impl.CASImpl;
+import org.apache.uima.cas.impl.TypeImpl;
+import org.apache.uima.cas.impl.TypeSystemImpl;
+import org.apache.uima.cas.text.AnnotationFS;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.jcas.JCasRegistry;
+import org.apache.uima.jcas.cas.AnnotationBase;
+import org.apache.uima.jcas.cas.TOP_Type;
+
+/**
+ * the JCas class model for the CAS type uima.cas.Annotation. It defines two integer valued features
+ * indicating the begin and end of the span being annotated. There is also a method to retrieve the
+ * spanned text as a string.
+ */
+public class Annotation extends AnnotationBase implements AnnotationFS {
+
+  public final static int typeIndexID = JCasRegistry.register(Annotation.class);
+
+  public final static int type = typeIndexID;
+
+  public int getTypeIndexID() {
+    return typeIndexID;
+  }
+  
+  public final static int _FI_begin = TypeSystemImpl.getAdjustedFeatureOffset("begin");
+  public final static int _FI_end   = TypeSystemImpl.getAdjustedFeatureOffset("end");
+  
+//  /* local data */
+//  private int _F_begin;
+//  private int _F_end;
+
+  // Never called. Disable default constructor
+  protected Annotation() {
+  }
+
+  // not used, just here to make decompiling of v2 work
+  protected Annotation(int addr, TOP_Type type) {
+    super();
+  }
+
+  public Annotation(JCas jcas) {
+    super(jcas);
+  }
+  
+  /**
+   * used by generator
+   * Make a new AnnotationBase
+   * @param c -
+   * @param t -
+   */
+
+  public Annotation(TypeImpl t, CASImpl c) {
+    super(t, c);
+  }
+
+
+  // *------------------*
+  // * Feature: begin
+  // * beginning of span of annotation
+  /*
+   * getter for begin - gets beginning of span of annotation
+   */
+//  public int getBegin() { return _F_begin; }
+  public int getBegin() { return _getIntValueNc(_FI_begin); 
+  }
+
+  /*
+   * setter for begin - sets beginning of span of annotation
+   */
+  public void setBegin(int v) { _setIntValueNfcCJ(_getFeatFromAdjOffset(_FI_begin, true), v); }
+  
+  // *------------------*
+  // * Feature: end
+  // * ending of span of annotation
+
+  /*
+   * getter for end - gets ending of span of annotation
+   */
+  public int getEnd() { 
+    return this._getIntValueNc(_FI_end);
+  }
+
+  /*
+   * setter for end - sets ending of span of annotation
+   */
+  public void setEnd(int v) {
+    this._setIntValueNfc(_getFeatFromAdjOffset(_FI_end, true),  v);
+  }
+  
+  /**
+   * Constructor with begin and end passed as arguments
+   * @param jcas JCas
+   * @param begin begin offset
+   * @param end   end offset
+   */
+  public Annotation(JCas jcas, int begin, int end) {
+    super(jcas); // forward to constructor
+    this._setIntValueNcNj(_FI_begin, begin);
+    this._setIntValueNcNj(_FI_end, end);
+  }
+
+  /**
+   * @see org.apache.uima.cas.text.AnnotationFS#getCoveredText()
+   */
+  public String getCoveredText() {
+
+    final String text = _casView.getDocumentText();
+    if (text == null) {
+      return null;
+    }
+    return text.substring(getBegin(), getEnd());
+  }
+
+  /**
+   * @deprecated
+   * @see Annotation#getBegin()
+   * @return the Annotation "begin" feature value
+   */
+  @Deprecated
+  public int getStart() {
+    return getBegin();
+  }
+  
+  /**
+   * Compare two annotations, no type order
+   * @param other
+   * @return
+   */
+  public int compareAnnotation(Annotation other) {
+    int[] o = other._intData;
+    int result = Integer.compare(_intData[_FI_begin], o[_FI_begin]);
+    if (result != 0) return result;
+
+    result = Integer.compare(_intData[_FI_end], o[_FI_end]);
+    return (result == 0) ? 0 : -result;  // reverse compare
+  }
+  
+  /**
+   * Compare two annotations incl type order
+   * @param other
+   * @return
+   */
+  public int compareAnnotation(Annotation other, LinearTypeOrder lto) {
+    int[] o = other._intData;
+    int result = Integer.compare(_intData[_FI_begin], o[_FI_begin]);
+    if (result != 0) return result;
+
+    result = Integer.compare(_intData[_FI_end], o[_FI_end]);
+    if (result != 0) return -result;
+    
+    return lto.compare(this,  other);
+  }
+
+
+  /**
+   * Compare two annotations, with type order, with id compare
+   * @param other
+   * @return
+   */
+  public int compareAnnotationWithId(Annotation other) {
+    int result = compareAnnotation(other);
+    if (result != 0) return result;    
+    return Integer.compare(_id,  other._id);
+  }
+  
+  /**
+   * Compare two annotations, with type order, with id compare
+   * @param other
+   * @return
+   */
+  public int compareAnnotationWithId(Annotation other, LinearTypeOrder lto) {
+    int result = compareAnnotation(other, lto);
+    if (result != 0) return result;    
+    return Integer.compare(_id,  other._id);
+  }
+
+}

Added: 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=1735666&view=auto
==============================================================================
--- uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java (added)
+++ uima/uimaj/uimaj-v3migration-jcas/src/main/java/org/apache/uima/migratev3/jcas/MigrateJCas.java Fri Mar 18 19:35:03 2016
@@ -0,0 +1,1313 @@
+/*
+ * 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.uima.migratev3.jcas;
+
+import java.io.BufferedWriter;
+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.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+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.util.FileUtils;
+import org.apache.uima.util.Misc;
+
+import com.github.javaparser.ASTHelper;
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ParseException;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.PackageDeclaration;
+import com.github.javaparser.ast.body.AnnotationDeclaration;
+import com.github.javaparser.ast.body.BodyDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.ConstructorDeclaration;
+import com.github.javaparser.ast.body.EmptyTypeDeclaration;
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.body.VariableDeclaratorId;
+import com.github.javaparser.ast.expr.AssignExpr;
+import com.github.javaparser.ast.expr.BinaryExpr;
+import com.github.javaparser.ast.expr.CastExpr;
+import com.github.javaparser.ast.expr.EnclosedExpr;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.FieldAccessExpr;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.ast.expr.NullLiteralExpr;
+import com.github.javaparser.ast.expr.ObjectCreationExpr;
+import com.github.javaparser.ast.expr.StringLiteralExpr;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.EmptyStmt;
+import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
+import com.github.javaparser.ast.stmt.ExpressionStmt;
+import com.github.javaparser.ast.stmt.IfStmt;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.ast.type.PrimitiveType;
+import com.github.javaparser.ast.type.ReferenceType;
+import com.github.javaparser.ast.type.Type;
+import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
+
+/**
+ * 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
+ *
+ * Creates summary and detailed reports of its actions.
+ * 
+ * Outputs converted files to an output file tree.
+ * 
+ *   - includes reporting on multiple definitions of the same class
+ *   
+ *   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
+ *     not-converted/
+ *     log/
+ *       converted/
+ *       not-converted/
+ *       duplicates/
+ *         classname-to-sourcepath.csv  // includes Classname.java1 etc.
+ *  
+ */
+public class MigrateJCas extends VoidVisitorAdapter<Object> {
+  
+  private static final String SOURCE_FILE_ROOTS = "-sourcesRoots";
+
+  private static final String CLASS_FILE_ROOTS = "-classesRoots";
+
+  private static final String OUTPUT_DIRECTORY = "-outputDirectory";
+  
+  private static final String SKIP_TYPE_CHECK = "-skip_Type_check";
+    
+  private boolean isSource = false;
+  
+  private Path candidate;
+  private List<Path> candidates;
+  
+  private String packageName;
+  private String className;  // (omitting package)
+  private String packageAndClassNameSlash;  
+  private final Set<String> usedPackageAndClassNames = new HashSet<>();
+  private String currentPackageAndClassNameSlash;
+  
+  /** includes trailing / */
+  private String outputDirectory;
+  /** includes trailing / */
+  private String outDirConverted;
+  /** includes trailing / */
+  private String outDirSkipped;
+  /** includes trailing / */
+private String outDirLog;
+  
+  
+  private String[] roots;
+  
+  private CompilationUnit cu;
+  
+  /************************************
+   * 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 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
+      
+  /************************************ 
+   * Context for visits    
+   ************************************/
+
+  /**
+   * if non-null, we're inside the ast for a likely JCas getter or setter method
+   */
+  private MethodDeclaration get_set_method; 
+  private String featName;
+  private boolean isGetter;
+  private boolean isArraySetter; 
+
+  /**
+   * the range name part for _getXXXValue.. calls
+   */
+  private Object rangeNamePart;
+  
+  /**
+   * the range name part for _getXXXValue.. calls without converting Ref to Feature
+   */
+  private String rangeNameV2Part;
+  
+  /**
+   * temp place to insert static final int feature declarations
+   */
+  private List<BodyDeclaration> fi_fields = new ArrayList<>();
+  private Set<String> featNames = new HashSet<>();
+
+  private boolean hasV2Constructors;
+  private boolean hasV3Constructors;
+
+
+  public MigrateJCas() {
+  }
+
+  public static void main(String[] args) {
+    (new MigrateJCas()).run(args);
+  }
+  
+  void run(String[] args) {
+    CommandLineParser clp = createCmdLineParser();
+    try {
+      clp.parseCmdLine(args);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+    if (!checkCmdLineSyntax(clp)) {
+      printUsage();
+      System.exit(2);
+    }
+    
+    if (clp.isInArgsList(CLASS_FILE_ROOTS)) {
+      roots = getRoots(clp, CLASS_FILE_ROOTS);
+      isSource = false;
+    } else if (clp.isInArgsList(SOURCE_FILE_ROOTS)) {
+      roots = getRoots(clp, SOURCE_FILE_ROOTS);
+      isSource = true;
+    } else {
+      printUsage();
+      System.exit(2);
+    }    
+    
+    // clear output dir
+    FileUtils.deleteRecursive(new File(outputDirectory));
+    
+    for (String root : roots) {
+      System.out.println("Migrating from root: " + root);
+      int i = 1;
+      for (Path c : getCandidates(clp, root)) {
+        System.out.print(".");
+        if ((i % 50) == 0) System.out.print("\n " + i);
+        candidate = c;
+        String source = getSource();
+        migrate(source);
+        i++;
+      }
+    }
+    report();
+  }
+  
+  private String[] getRoots(CommandLineParser clp, String kind) {
+    return clp.getParamArgument(kind).split("\\" + File.pathSeparator);
+  }
+  
+  private String getSource() {
+    try {
+      String s;
+      if (isSource) {
+        s = FileUtils.reader2String(Files.newBufferedReader(candidate));
+//        System.out.println("debug read " + s.length());
+      } else {
+        // read in bytes and decompile the class to a string
+        byte[] b = Files.readAllBytes(candidate);
+        s = decompile(b);
+      }
+      return s;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  /**
+   * Migrate one JCas definition.
+   * 
+   * The source is either direct, or a decompiled version of a .class file (missing comments, etc.).
+   * 
+   * This method only called if heuristics indicate this is a V2 JCas class definition.
+   * 
+   * The goal is to preserve as much as possible of existing customization.
+   * The general approach is to parse the source into an AST, and use visitor methods.
+   *   For getter/setter methods that are for features (heurstic), set up a context for inner visitors
+   *   identifying the getter / setter.
+   *     - reuse method declarator, return value casts, value expressions
+   *     - remove feature checking statement, array bounds checking statement, if present.
+   *     - replace the simpleCore (see Jg), replace the arrayCore
+   *     
+   *   For constructors, replace the 2-arg one that has arguments: 
+   *   addr and TOP_Type with the v3 one using TypeImpl, CasImpl.
+   *   
+   *   Add needed imports.
+   *   Add for each feature the _FI_xxx static field declarator.
+   *   
+   *   Leave other top level things alone
+   *     - additional constructors.
+   *     - other methods not using jcasType refs
+   *   
+   * @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);
+            
+      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) {
+        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);
+    }
+  }
+  
+  /******************
+   *  Visitors
+   ******************/
+  /**
+   * Capture the type name from all top-level types
+   *   AnnotationDeclaration, Empty, and Enum
+   */
+  
+  @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);
+  }
+
+  /**
+   * Check if the top level class looks like a JCas class, and report if not:
+   *   has 0, 1, and 2 element constructors
+   *   has static final field defs for type and typeIndexID
+   *   
+   *   Also check if V2 style: 2 arg constructor arg types
+   *   Report if looks like V3 style due to args of 2 arg constructor
+   *   
+   *   if class doesn't extend anything, not a JCas class.
+   *   if class is enum, not a JCas class
+   * @param n
+   * @param ignore
+   */
+  @Override
+  public void visit(ClassOrInterfaceDeclaration n, Object ignore) {
+    // do checks to see if this is a JCas class; if not report skipped
+   
+    if (n.getParentNode() instanceof CompilationUnit) {
+      updateClassName(n);
+      List<ClassOrInterfaceType> supers = n.getExtends();
+      if (supers == null || supers.size() == 0) {
+        reportNotJCasClass("class doesn't extend a superclass");
+        super.visit(n, ignore);
+        return; 
+      }
+      
+      List<BodyDeclaration> members = n.getMembers();
+      setHasJCasConstructors(members);
+      if (hasV2Constructors && hasTypeFields(members)) {
+        reportV2Class();
+        super.visit(n,  ignore);
+        return;
+      }
+      if (hasV2Constructors) {
+        reportNotJCasClassMissingTypeFields();
+        return;
+      }
+      if (hasV3Constructors) {
+        reportV3Class();
+        return;
+      }
+      reportNotJCasClass("missing v2 constructors");
+      return;
+      
+    } else { // do standard processing for non-outer class
+      super.visit(n,  ignore);
+      return;
+    }
+  }
+  
+  @Override
+  public void visit(PackageDeclaration n, Object ignored) {
+    packageName = n.getName().toString();
+    super.visit(n, ignored);
+  }
+    
+  /***************
+   * Constructors
+   *   - modify the 2 arg constructor - changing the args and the body
+   * @param n - the constructor node
+   * @param ignored -
+   */
+  @Override
+  public void visit(ConstructorDeclaration n, Object ignored) {
+    super.visit(n, ignored);  // processes the params 
+    if (!v3) {  // for enums, annotations
+      return;
+    }
+    List<Parameter> ps = n.getParameters();
+    Parameter ps0, ps1;
+    
+    if (ps.size() == 2 && 
+        getParmTypeName(ps, 0).equals("int") &&
+        getParmTypeName(ps, 1).equals("TOP_Type")) {
+        
+      /** public Foo(TypeImpl type, CASImpl casImpl) {
+       *   super(type, casImpl);
+       *   readObject();
+       */
+      setParameter(ps, 0, "TypeImpl", "type");
+      setParameter(ps, 1, "CASImpl", "casImpl");
+      
+      // Body: change the 1st statement (must be super)
+      List<Statement> stmts = n.getBlock().getStmts();
+      if (!(stmts.get(0) instanceof ExplicitConstructorInvocationStmt)) {
+        recordBadConstructor("missing super call");
+        return;
+      }
+      List<Expression> args = ((ExplicitConstructorInvocationStmt)(stmts.get(0))).getArgs();
+      args.set(0, new NameExpr("type"));
+      args.set(1,  new NameExpr("casImpl"));
+     
+      // leave the rest unchanged.
+    }      
+  }
+
+  /*****************************
+   * Method Declaration Visitor
+   *   Heuristic to determine if a feature getter or setter:
+   *   - name: is 4 or more chars, starting with get or set, with 4th char uppercase
+   *           is not "getTypeIndexID"
+   *   - (optional - if comments are available:) 
+   *       getter for xxx, setter for xxx
+   *   - 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
+   *   
+   *****************************/
+  @Override
+  public void visit(MethodDeclaration n, Object ignore) {
+    String name = n.getName();
+    isGetter = isArraySetter = false;
+    do {  // to provide break exit
+      if (name.length() >= 4 && 
+          ((isGetter = name.startsWith("get")) || name.startsWith("set")) &&
+          Character.isUpperCase(name.charAt(3)) &&
+          !name.equals("getTypeIndexID")) {
+        List<Parameter> ps = n.getParameters();
+        if (isGetter) {
+          if (ps.size() > 1) break;
+        } else { // is setter
+          if (ps.size() > 2 || 
+              ps.size() == 0) break;
+          if (ps.size() == 2) {
+            if (!getParmTypeName(ps, 0).equals("int")) break;
+            isArraySetter = true;
+          } 
+        }
+        
+        // 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_");
+        if (i < 0) break; 
+        s = s.substring(i + "jcasType.ll_cas.ll_get".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();
+          s = s.substring(i + "jcasType.ll_cas.ll_get".length());
+        }
+        i = s.indexOf("Value");
+        if (i < 0) {
+          reportUnrecognizedV2Code(s);
+          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);
+        
+        List<Expression> args = Collections.singletonList(new StringLiteralExpr(featName));
+        VariableDeclarator vd = new VariableDeclarator(
+            new VariableDeclaratorId("_FI_" + featName),
+            new MethodCallExpr(null, "TypeSystemImpl.getAdjustedFeatureOffset", args));
+        if (featNames.add(featName)) {
+          fi_fields.add(new FieldDeclaration(
+              Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL, 
+              ASTHelper.INT_TYPE, 
+              vd));
+        }
+        get_set_method = n; // used as a flag during inner "visits" to signal  
+                            // we're inside a likely feature setter/getter
+
+      } // end of test for getter or setter method
+    } while (false);  // do once, provide break exit
+    
+    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
+   */
+  @Override
+  public void visit(IfStmt n, Object ignore) {
+    do {
+      if (get_set_method == null) break;
+      
+      Expression c = n.getCondition(), e;
+      BinaryExpr be, be2;
+      List<Statement> stmts;
+      
+      if ((c instanceof BinaryExpr) &&
+          ((be = (BinaryExpr)c).getLeft() instanceof FieldAccessExpr) &&
+          ((FieldAccessExpr)be.getLeft()).getField().equals("featOkTst")) {
+        // remove the feature missing if statement
+        
+        // verify the remaining form 
+        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)
+         || ! (((MethodCallExpr)e).getName()).equals("throwFeatMissing")) {
+          reportDeletedCheckModified("The featOkTst was modified:\n" + n.toString());
+        }
+              
+        BlockStmt parent = (BlockStmt) n.getParentNode();
+        stmts = parent.getStmts();
+        stmts.set(stmts.indexOf(n), new EmptyStmt()); //dont remove
+                                            // otherwise iterators fail
+//        parent.getStmts().remove(n);
+        return;
+      }
+    } while (false);
+    
+    super.visit(n,  ignore);
+  }
+  /** 
+   * visitor for method calls
+   */
+  @Override
+  public void visit(MethodCallExpr n, Object ignore) {
+    Node p1, p2, updatedNode = null;
+    List<Expression> args;
+    
+    do {
+      if (get_set_method == null) break;
+
+      /** remove checkArraybounds statement **/
+      if (n.getName().equals("checkArrayBounds") &&
+          ((p1 = n.getParentNode()) instanceof ExpressionStmt) &&
+          ((p2 = p1.getParentNode()) instanceof BlockStmt) &&
+          p2.getParentNode() == get_set_method) {
+        List<Statement> stmts = ((BlockStmt)p2).getStmts();
+        stmts.set(stmts.indexOf(p1), new EmptyStmt());
+        return;
+      }
+           
+      // convert simpleCore expression ll_get/setRangeValue
+      boolean useGetter = isGetter || isArraySetter;
+      if (n.getName().startsWith("ll_" + (useGetter ? "get" : "set") + rangeNameV2Part + "Value")) {
+        args = n.getArgs();
+        if (args.size() != (useGetter ? 2 : 3)) break;
+        String suffix = useGetter ? "Nc" : rangeNamePart.equals("Feature") ? "NcWj" : "Nfc";
+        String methodName = "_" + (useGetter ? "get" : "set") + rangeNamePart + "Value" + suffix; 
+        args.remove(0);    // remove the old addr arg
+        // arg 0 converted when visiting args FieldAccessExpr 
+        n.setScope(null);
+        n.setName(methodName);
+      }
+      
+      // convert array sets/gets
+      String z = "ll_" + (isGetter ? "get" : "set");
+      if (n.getName().startsWith(z) &&
+          n.getName().endsWith("ArrayValue")) {
+        
+        String s = n.getName().substring(z.length());
+        s = s.substring(0,  s.length() - "Value".length()); // s = "ShortArray",  etc.
+        if (s.equals("RefArray")) s = "FSArray";
+        EnclosedExpr ee = new EnclosedExpr(
+            new CastExpr(new ClassOrInterfaceType(s), n.getArgs().get(0)));
+        
+        n.setScope(ee);    // the getter for the array fs
+        n.setName(isGetter ? "get" : "set");
+        n.getArgs().remove(0);
+      }
+      
+      /** remove ll_getFSForRef **/
+      /** remove ll_getFSRef **/
+      if (n.getName().equals("ll_getFSForRef") ||
+          n.getName().equals("ll_getFSRef")) {
+        updatedNode = replaceInParent(n, n.getArgs().get(0));        
+      }
+      
+      
+      
+    } while (false);
+        
+    if (updatedNode != null) {
+      updatedNode.accept(this,  null);
+    } else {
+      super.visit(n, null);
+    }
+  }
+
+  /**
+   * visitor for field access expressions
+   *   - convert ((...type_Type)jcasType).casFeatCode_XXXX to _FI_xxx
+   * @param n -
+   * @param ignore -
+   */
+  @Override
+  public void visit(FieldAccessExpr n, Object ignore) {
+    Expression e;
+    
+    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)
+        return;
+      } else if (n.getField().startsWith("casFeatCode_")) {
+        System.out.println("debug");
+      }
+    }
+    super.visit(n,  ignore);      
+  }
+  
+  private class removeEmptyStmts extends VoidVisitorAdapter<Object> {
+    @Override
+    public void visit(BlockStmt n, Object ignore) {
+      Iterator<Statement> it = n.getStmts().iterator();
+      while (it.hasNext()) {
+        if (it.next() instanceof EmptyStmt) {
+          it.remove();
+        }
+      }
+      super.visit(n,  ignore);
+    }
+    
+//    @Override
+//    public void visit(MethodDeclaration n, Object ignore) {
+//      if (n.getName().equals("getModifiablePrimitiveNodes")) {
+//        System.out.println("debug");
+//      }
+//      super.visit(n,  ignore);
+//      if (n.getName().equals("getModifiablePrimitiveNodes")) {
+//        System.out.println("debug");
+//      }
+//    }
+  }
+    
+  /**
+   * converted files: 
+   *    java name, path  (sorted by java name, v3 name only)
+   * not-converted:
+   *    java name, path  (sorted by java name)
+   * duplicates:
+   *    java name, path  (sorted by java name)
+   *
+   */
+  private void report() {
+    System.out.format("Migration 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:");
+    for (String s : roots) {
+      System.out.format("    %s%n",  s);
+    }
+    System.out.println("\n");
+   
+    try {
+      reportPaths("Workaround Directories", "workaroundDir", pathWorkaround);
+      reportPaths("Report of converted files where a deleted check was customized", "deletedCheckModified", deletedCheckModified);
+      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",
+          "extendableBuiltins.txt", extendableBuiltins);
+      
+      computeDuplicates();
+      reportPaths("Report of duplicates - not identical", "nonIdenticalDuplicates.txt", nonIdenticalDuplicates);
+      reportPaths("Report of duplicates - identical", "identicalDuplicates.txt", identicalDuplicates);
+      
+      reportPaths("Report of processed files", "processed.txt", c2ps);
+      reportPaths("Builtin non-Extendable JCas classes - skipped", "builtinsNotExtendable.txt", nonExtendableBuiltins);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  private void computeDuplicates() {
+    List<Pair<String, String>> toCheck = new ArrayList<>(c2ps);
+    toCheck.addAll(extendableBuiltins);
+    sortPairs(toCheck);
+    Pair<String, String> prevP = new Pair<>("","");
+    List<Pair<String, String>> sameList = new ArrayList<>();
+    boolean areAllEqual = true;
+    
+    for (Pair<String, String> p : toCheck) {
+      if (!p.t.equals(prevP.t)) {
+        
+        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 (filesMiscompare(p.u, prevP.u)) {
+          areAllEqual = false;
+        }
+      }      
+    }
+    
+    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));
+      return !s1.equals(s2);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  private void addToIdenticals(List<Pair<String, String>> 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);
+    Path parent = p.getParent();
+    try {
+      Files.createDirectories(parent);
+    } catch (FileAlreadyExistsException e) {
+      // caused by running on Windows system which ignores "case"
+      // there's a file at /x/y/  named "z", but the path wants to be /x/y/Z/
+      //   Workaround: change "z" to "z_c"  c for capitalization issue
+      String newDir = parent.getFileName().toString() + "_c";
+      Path p2 = Paths.get(parent.getParent().toString(), newDir);
+      Files.createDirectories(p2);
+      reportPathWorkaround(parent.toString(), p2.toString());
+      return Paths.get(p2.toString(), p.getFileName().toString());
+    }
+    return p;
+  }
+  
+  private void reportPaths(String title, String fileName, List<Pair<String, String>> items) throws IOException {
+    if (items.size() == 0) {
+      System.out.println("No " + title);
+      return;  
+    }
+    System.out.println("\n" + title);
+    for (int i = 0; i < title.length(); i++) System.out.print('=');
+    System.out.println("");
+    
+    try (BufferedWriter bw = Files.newBufferedWriter(makePath(outDirLog + fileName), StandardOpenOption.CREATE)) {
+      List<Pair<String, String>> sorted = new ArrayList<>(items);
+
+      sortPairs(sorted);  
+      int max = 0;
+      for (Pair<String, String> p : sorted) {
+        max = Math.max(max, p.t.length());
+      }
+      
+      int i = 1;
+      for (Pair<String, String> p : sorted) {
+        String s = String.format("%5d %-" +max+ "s %s%n", i, p.t, p.u);
+        bw.write(s);
+        System.out.print(s);
+        i++;
+      }
+      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;
+      }
+    });
+  }
+  
+  /**
+   * Walk the directory tree rooted at root
+   *   - descend subdirectories
+   *   - descend Jar file
+   * 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
+   * @throws IOException
+   */
+  private List<Path> getCandidates(CommandLineParser clp, String root) {
+    boolean skip_Type_check = clp.isInArgsList(SKIP_TYPE_CHECK);
+    Path startPath = Paths.get(root);
+    candidates = new ArrayList<>();
+    
+    getCandidates_processCandidate(startPath);
+    Collections.sort(candidates, pathComparator);
+    
+    List<Path> c = new ArrayList<>();  // candidates
+    final int nbrOfPaths = candidates.size();
+    if (nbrOfPaths == 0) {
+      return c;
+    }
+    for (int i = 0; i < nbrOfPaths;) {
+      if (skip_Type_check) {
+        candidate = candidates.get(i);
+        if (candidate.getFileName().endsWith(isSource ? "_Type.java" : "_Type.class")) {
+          continue;  // skip the _Type files
+        }
+        c.add(candidate);
+        i++;
+      } else {
+        int next_Type = indexOf_Type(i);
+        if (next_Type == -1) {
+          System.out.println("debug getCandidates retuning:");
+          for (Path x : c) {
+            System.out.println("debug   " + x);
+          }
+          return c;
+        } 
+        addIfPreviousIsSameName(c, next_Type);
+        i = next_Type + 1;  
+      }
+    }
+    return c;  
+  }
+  
+  private final static Comparator<Path> pathComparator = new Comparator<Path>() {
+    @Override
+    public int compare(Path o1, Path o2) {
+      return o1.toString().compareTo(o2.toString());
+    }
+  };
+  
+  // 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--;
+    }
+  }
+    
+  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")) {
+        FileSystem jfs = FileSystems.newFileSystem(path, null);
+        Path start = jfs.getPath("/");
+        getCandidates_DescendDirectory(start);
+      } else {
+//        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);
+        }
+      }
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  private void getCandidates_DescendDirectory(Path path) {
+    try {
+      Files.list(path).forEach(p -> getCandidates_processCandidate(p));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  private void getCandidates_processCandidate(Path p) {
+    if (Files.isDirectory(p)) {
+      getCandidates_DescendDirectory(p);  
+    } else {
+      getCandidates_processFile(p);
+    }
+  }
+  
+  
+  private int indexOf_Type(int start) {
+    int i = start;
+    if (start >= candidates.size()) {
+      return -1;
+    }
+    for (Path p : candidates.subList(start,  candidates.size())) {
+      String s = p.toString();
+      if (s.endsWith("_Type.java") || 
+          s.endsWith("_Type.class")) {
+        return i;
+      }
+      i++;
+    }
+    return -1;
+  }
+  
+  
+  private static final CommandLineParser createCmdLineParser() {
+    CommandLineParser parser = new CommandLineParser();
+    parser.addParameter(SOURCE_FILE_ROOTS, true);
+    parser.addParameter(CLASS_FILE_ROOTS, true);
+    parser.addParameter(OUTPUT_DIRECTORY, true);
+    parser.addParameter(SKIP_TYPE_CHECK, false);
+    return parser;
+  }
+
+  private final boolean checkCmdLineSyntax(CommandLineParser clp) {
+    if (clp.getRestArgs().length > 0) {
+      System.err.println("Error parsing CVD command line: unknown argument(s):");
+      String[] args = clp.getRestArgs();
+      for (int i = 0; i < args.length; i++) {
+        System.err.print(" ");
+        System.err.print(args[i]);
+      }
+      System.err.println();
+      return false;
+    }
+    if (clp.isInArgsList(SOURCE_FILE_ROOTS) && clp.isInArgsList(CLASS_FILE_ROOTS)) {
+      System.err.println("both sources file roots and classess file roots parameters specified; please specify just one.");
+      return false;
+    }
+    
+    if (clp.isInArgsList(OUTPUT_DIRECTORY)) {
+      outputDirectory = Paths.get(clp.getParamArgument(OUTPUT_DIRECTORY)).toString();
+      if (!outputDirectory.endsWith("/")) {
+        outputDirectory = outputDirectory + "/";
+      }
+      outDirConverted = outputDirectory + "converted/";
+      outDirSkipped = outputDirectory + "not-converted/";
+      outDirLog = outputDirectory + "logs/";
+    } else {
+      System.err.println("-outputDirectory is a required parameter, must be a path to a writable file directory.");
+      return false;
+    }
+    return true;
+  }
+  
+  private String decompile(byte[] b) {
+    UimaDecompiler ud = new UimaDecompiler();
+    return ud.decompile(b);
+  }
+  
+  private void addImport(String s) {
+    cu.getImports().add(new ImportDeclaration(new NameExpr(s), false, false));
+  }
+
+  /******************
+   * AST Utilities
+   ******************/
+  
+  private Node replaceInParent(Node n, Expression v) {
+    Node parent = n.getParentNode();
+    if (parent instanceof EnclosedExpr) {
+      ((EnclosedExpr)parent).setInner(v);
+    } else if (parent instanceof MethodCallExpr) { // args in the arg list
+      List<Expression> args = ((MethodCallExpr)parent).getArgs();
+      args.set(args.indexOf(n), v);
+      v.setParentNode(parent);
+    } else if (parent instanceof ExpressionStmt) { 
+      ((ExpressionStmt)parent).setExpression(v);
+    } else if (parent instanceof CastExpr) { 
+      ((CastExpr)parent).setExpr(v);
+    } else if (parent instanceof ReturnStmt) { 
+      ((ReturnStmt)parent).setExpr(v);
+    } else if (parent instanceof AssignExpr) {
+      ((AssignExpr)parent).setValue(v);
+    } else if (parent instanceof VariableDeclarator) {
+      ((VariableDeclarator)parent).setInit(v);      
+    } else if (parent instanceof ObjectCreationExpr) {
+      List<Expression> args = ((ObjectCreationExpr)parent).getArgs();
+      int i = args.indexOf(n);
+      if (i < 0) throw new RuntimeException();
+      args.set(i, v);      
+   } else {
+      System.out.println(parent.getClass().getName());
+      throw new RuntimeException();
+    }
+    return v;
+  }
+    
+  /**
+   * 
+   * @param p the parameter to modify
+   * @param t the name of class or interface
+   * @param name the name of the variable
+   */
+  private void setParameter(List<Parameter> ps, int i, String t, String name) {
+    Parameter p = ps.get(i);
+    p.setType(new ClassOrInterfaceType(t));
+    p.setId(new VariableDeclaratorId(name));
+  }
+  
+  private int findConstructor(List<BodyDeclaration> classMembers) {
+    int i = 0;
+    for (BodyDeclaration bd : classMembers) {
+      if (bd instanceof ConstructorDeclaration) {
+        return i;
+      }
+      i++;
+    }
+    return -1;
+  }
+  
+  private boolean hasTypeFields(List<BodyDeclaration> members) {
+    boolean hasType = false;
+    boolean hasTypeId = false;
+    for (BodyDeclaration bd : members) {
+      if (bd instanceof FieldDeclaration) {
+        FieldDeclaration f = (FieldDeclaration)bd;
+        int m = f.getModifiers();
+        if (Modifier.isPublic(m) &&
+            Modifier.isStatic(m) &&
+            Modifier.isFinal(m) &&
+            getTypeName(f.getType()).equals("int")) {
+          List<VariableDeclarator> vds = f.getVariables();
+          for (VariableDeclarator vd : vds) {
+            String n = vd.getId().getName();
+            if (n.equals("type")) hasType = true;
+            if (n.equals("typeIndexID")) hasTypeId = true;
+            if (hasTypeId && hasType) break;
+          }
+        }
+      }
+    } // end of for
+    return hasTypeId && hasType;
+  }
+  
+  /**
+   * Heuristic:
+   *   JCas classes have 0, 1, and 2 arg constructors with particular arg types
+   *   0 -
+   *   1 - JCas
+   *   2 - int, TOP_Type  (v2) or TypeImpl, CASImpl (v3)
+   *   
+   * Additional 1 and 2 arg constructors are permitted.
+   * 
+   * Sets fields hasV2Constructors, hasV3Constructors
+   * @param members
+   */
+  private void setHasJCasConstructors(List<BodyDeclaration> members) {
+    boolean has0ArgConstructor = false;
+    boolean has1ArgJCasConstructor = false;
+    boolean has2ArgJCasConstructorV2 = false;
+    boolean has2ArgJCasConstructorV3 = false;
+    
+    for (BodyDeclaration bd : members) {
+      if (bd instanceof ConstructorDeclaration) {
+        List<Parameter> ps = ((ConstructorDeclaration)bd).getParameters();
+        if (ps.size() == 0) has0ArgConstructor = true;
+        if (ps.size() == 1 && getParmTypeName(ps, 0).equals("JCas")) {
+          has1ArgJCasConstructor = true;
+        }
+        if (ps.size() == 2) {
+          if (getParmTypeName(ps, 0).equals("int") &&
+              getParmTypeName(ps, 1).equals("TOP_Type")) {
+            has2ArgJCasConstructorV2 = true;
+          } else if (getParmTypeName(ps, 0).equals("TypeImpl") &&
+                     getParmTypeName(ps, 1).equals("CASImpl")) {
+            has2ArgJCasConstructorV3 = true;
+          }
+        } // end of 2 arg constructor
+      } // end of is-constructor
+    } // end of for loop
+    
+    hasV2Constructors = has0ArgConstructor && has1ArgJCasConstructor && has2ArgJCasConstructorV2;
+    hasV3Constructors = has0ArgConstructor && has1ArgJCasConstructor && has2ArgJCasConstructorV3;
+  }
+  
+  private String getParmTypeName(List<Parameter> p, int i) {
+    return getTypeName(p.get(i).getType());
+  }
+  
+  private String getTypeName(Type t) {
+    if (t instanceof ReferenceType) {
+      t = ((ReferenceType)t).getType();
+    }
+    
+    if (t instanceof PrimitiveType) {
+      return ((PrimitiveType)t).toString(); 
+    }
+    if (t instanceof ClassOrInterfaceType) {
+      return ((ClassOrInterfaceType)t).getName();
+    }
+    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 -
+   * @return the field name or null
+   */
+  private String getName(Expression e) {
+    e = getUnenclosedExpr(e);
+    if (e instanceof NameExpr) {
+      return ((NameExpr)e).getName();
+    }
+    if (e instanceof FieldAccessExpr) {
+      return ((FieldAccessExpr)e).getField();
+    }
+    return null;
+  }
+  
+  private void updateClassName(TypeDeclaration n) {
+    if (n.getParentNode() instanceof CompilationUnit) {
+      className = n.getName();
+      String packageAndClassName = 
+          (className.contains(".")) 
+            ? className 
+            : packageName + '.' + className;
+      packageAndClassNameSlash = packageAndClassName.replace('.', '/');
+      
+      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());
+        if (!ti.isFeatureFinal()) {
+          extendableBuiltins.add(p);
+        } else {
+          nonExtendableBuiltins.add(p);
+        }
+        v3 = false;
+        return; // skip further processing of this class 
+      }
+
+      c2ps.add(new Pair<String, String>(packageAndClassNameSlash, candidate.toString()));
+    }
+  }
+  
+  /********************
+   * Recording results
+   ********************/
+
+  private void recordBadConstructor(String msg) {
+    reportMigrateFailed("Constructor is incorrect, " + msg);
+  }
+      
+  private void reportParseException() {
+    reportMigrateFailed("Unparsable Java");
+  }
+  
+  private void migrationFailed(String reason) {
+    failedMigration.add(new Pair<String, String>(candidate.toString(), reason));
+    v3 = false;    
+  }
+  
+  private void reportMigrateFailed(String m) {
+    System.out.format("Skipping this file due to error: %s, path: %s%n", m, candidate);
+    migrationFailed(m);
+  }
+  
+  private void reportV2Class() {
+    v2JCasFiles.add(candidate);
+    v2 = true;
+  }
+  
+  private void reportV3Class() {
+    v3JCasFiles.add(candidate);
+    v3 = true;
+  }
+  
+  private void reportNotJCasClass(String reason) {
+    nonJCasFiles.add(new Pair<String, String>(candidate.toString(), reason));
+    v3 = false;
+  }
+  
+  private void reportNotJCasClassMissingTypeFields() {
+    reportNotJCasClass("missing required type and/or typeIndexID static fields");
+  }
+  
+  private void reportDeletedCheckModified(String m) {
+    deletedCheckModified.add(new Pair<>(candidate.toString(), m));
+  }
+  
+  private void reportUnrecognizedV2Code(String m) {
+    migrationFailed("V2 code not recognized: " + m);
+  }
+  
+  private void reportPathWorkaround(String orig, String modified) {
+    
+  }
+  
+  /***********************************************/
+    
+  private void getBaseOutputPath() {
+    String s = packageAndClassNameSlash;
+    int i = 1;
+    while (!usedPackageAndClassNames.add(s)) {
+      s = packageAndClassNameSlash + i++;
+    }
+    currentPackageAndClassNameSlash = s;
+  }
+  
+  private String getBaseOutputPath(boolean wasConverted, boolean isV2) {
+    return (wasConverted ? outDirConverted : outDirSkipped) + (isV2 ? "v2/" : "v3/") + currentPackageAndClassNameSlash + ".java";
+  }
+  
+  private void writeV2Orig(String data, boolean wasConverted) throws IOException {
+    String base = getBaseOutputPath(wasConverted, true);  // adds numeric suffix if dupls
+    FileUtils.writeToFile(makePath(base), data);
+  }
+  
+  private void writeV3(String data) throws IOException {
+    String base = getBaseOutputPath(true, false);  // adds numeric suffix if dupls
+    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"
+        + "  [-outputDirectory a-writable-directory-path\n"
+        + "  NOTE: either -sourcesRoots or -classesRoots is required, but only one may be specified.\n"
+        + "  NOTE: -outputDirectory is required\n");
+  }
+
+}