arcgissamples\geodatabase\RelatedObjectsRunner.java—ArcObjects 10.4 Help for Java | ArcGIS for Desktop
Related Object Event Handler custom Java class extension
arcgissamples\geodatabase\RelatedObjectsRunner.java
/* Copyright 2015 ESRI
* 
* All rights reserved under the copyright laws of the United States
* and applicable international laws, treaties, and conventions.
* 
* You may freely redistribute and use this sample code, with or
* without modification, provided you include the original copyright
* notice and use restrictions.
* 
* See the use restrictions at <your ArcGIS install location>/DeveloperKit10.4/userestrictions.txt.
* 
*/
package arcgissamples.geodatabase;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import com.esri.arcgis.datasourcesGDB.FileGDBWorkspaceFactory;
import com.esri.arcgis.geodatabase.Feature;
import com.esri.arcgis.geodatabase.FeatureClass;
import com.esri.arcgis.geodatabase.FeatureCursor;
import com.esri.arcgis.geodatabase.Field;
import com.esri.arcgis.geodatabase.Fields;
import com.esri.arcgis.geodatabase.GeometryDef;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFeatureType;
import com.esri.arcgis.geodatabase.esriFieldType;
import com.esri.arcgis.geodatabase.esriRelCardinality;
import com.esri.arcgis.geodatabase.esriRelNotification;
import com.esri.arcgis.geodatabase.esriSchemaLock;
import com.esri.arcgis.geometry.GeographicCoordinateSystem;
import com.esri.arcgis.geometry.ISpatialReference;
import com.esri.arcgis.geometry.Line;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.SpatialReferenceEnvironment;
import com.esri.arcgis.geometry.esriGeometryType;
import com.esri.arcgis.geometry.esriSRGeoCSType;
import com.esri.arcgis.interop.extn.ArcGISExtension;
import com.esri.arcgis.interop.extn.Bootstrapper;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.Set;
import com.esri.arcgis.system.UID;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

public class RelatedObjectsRunner
{
    private static String outputPath = null;
    private Class ceClass = null;
    
  public static void main(String[] args)
  {
    try
    {
      //Get the ArcGIS Desktop runtime, if it is available
        String arcObjectsHome = System.getenv("AGSENGINEJAVA");
        
        //If the ArcGIS Desktop runtime is not available, then report appropriate error to developer
        if(arcObjectsHome == null){
            System.err.println("The RelatedObjectsRunner sample is designed to work with ArcGIS Engine installed.");
            System.err.println("You must install ArcGIS Engine and deploy the RelatedObjectsRunnerExt.jar as instructed " +
                      "in this sample's ReadMe.htm file.");
            System.err.println("Exiting execution of this sample...");
            System.exit(0);
        }

        String extensionJar = arcObjectsHome + "java" + File.separator + "lib" + File.separator + "ext" + File.separator + 
                      "RelatedObjectEventHandlerExt.jar";
        
             RelatedObjectsRunner driver = new RelatedObjectsRunner();
          
            //get output folder
            outputPath = getOutputDir();
                
            if(driver.isJarFileValid(extensionJar)){
              driver.ceClass = driver.getCEClass(extensionJar).get(0);
            }
      
        EngineInitializer.initializeEngine();
        AoInitialize aoInit = new AoInitialize();
          
        driver.initializeArcGISLicenses(aoInit);
          
        driver.driveExtension();

        aoInit.shutdown();
        }
    catch (Exception e){e.printStackTrace();}
  }
  
  /**
   * Convenience method to generate an output directory based on the operating
   * system that the sample is being executed on. 
   * 
   * @return A path to the new directory is return
   */
  private static String getOutputDir() {
    String userDir;
    
    //Get the operating systems user profile or home location depending
    //on which operating system this sample is executed on.
    if(System.getProperty("os.name").toLowerCase().indexOf("win") > -1){
      userDir = System.getenv("UserProfile");
    }else{
      userDir = System.getenv("HOME");
    }
      
    String outputDir = userDir + File.separator + "arcgis_sample_output";
    
    System.out.println("Creating output directory - " + outputDir);
    
    new File(outputDir).mkdir();
    
    return outputDir;
  }
  
  void initializeArcGISLicenses(AoInitialize aoInit) {
    try {
      
      if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngineGeoDB) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeEngineGeoDB);
    } catch (Exception e) {e.printStackTrace();}
  }

  public void driveExtension() throws Exception
  {
    System.out.println("Starting RelatedObjectsRunner...\n");

    System.out.print("Initializing data paths...");
    String fgdbName = "relatedobjects.gdb";
      System.out.println("Done.");

    System.out.print("Creating local File GDB...");
    FileGDBWorkspaceFactory wsFactory = new FileGDBWorkspaceFactory();
    wsFactory.create(outputPath, fgdbName, null, 0);
    System.out.println("Done.");
    System.out.println("Created a File GDB in the following location: " + outputPath + File.separator + fgdbName);

      //open the local workspace and create poles fc and transformer fc
    String polesFCName = "Pole";
        String xformersFCName = "Transformer";
    System.out.print("Creating " + polesFCName + " and " + xformersFCName + " feature classes in local File GDB...");
        Workspace ws = (Workspace) wsFactory.openFromFile(outputPath + File.separator + fgdbName, 0);

        FeatureClass polesFC = createPolesFC(ws, polesFCName);
        FeatureClass xformerFC = createXFormerFC(ws, xformersFCName);
        System.out.println("Done.");

    System.out.print("Creating a composite Relationship Class between " + polesFCName + " and " + xformersFCName + " feature classes...");
    /*
     * We are not using RelationshipClass.createRelationshipClass() method because we need to create a standalone relationship class.
     * IRelationshipClass.createRelationshipClass() is used when creating a relationship class inside a feature dataset.
     */
        ws.createRelationshipClass("POLES_XFORMERS", polesFC, xformerFC, "Transformer", "Pole", esriRelCardinality.esriRelCardinalityOneToMany,
                esriRelNotification.esriRelNotificationForward, false, false, null, "OBJECTID", "", "poleid", "");
        System.out.println("Done.");

    System.out.print("Adding extension to \"Transformer\"...");

    /*
     * Use the RelatedObjectEventHandler CE as class extension to the xformer FC.
     * When a pole will be created the relatedObjectCreated() should be fired.
     */
    if(xformerFC.getEXTCLSID() == null)
    {
      xformerFC.changeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

      //Create a new unique identifier object (UID) and assign the GUID to it.
      UID extUID = new UID();          
      extUID.setValue(ceClass);

      xformerFC.alterClassExtensionCLSID(extUID, null);
      xformerFC.changeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
    else
    {
      System.out.println("Cannot alter EXTCLSID for feature class. One already exists");
    }
    System.out.println("Done.");

        try
        {
            ws.startEditing(false);
            ws.startEditOperation();

            /* Rotate a feature in poles FC. When this feature is rotated, IRelatedObjectClassEvents.relatedObjectSetRotated() in the class
             * extension is fired and that gives you a chance to add custom logic to handle rotation.
             */
            System.out.print("Rotating Pole features ...");
            rotateFeatures(polesFC);
            System.out.println("Done.");

            /*
             * Move a pole feature. When this feature is moved, IRelatedObjectClassEvents.relatedObjectSetMoved() in the class
             * extension is fired and that gives you a chance to add custom logic to handle the move operation.
             */
            System.out.print("Moving features ...");
            moveFeatures(polesFC);
            System.out.println("Done.");

            ws.stopEditOperation();
            ws.stopEditing(true);
        }
        catch(Exception e)
        {
            if(ws != null && ws.isBeingEdited())
            {
                ws.stopEditing(false);
            }

            e.printStackTrace();
        }

    System.out.println("\nRelated Objects Runner Done.");
  }

  /**
   * rotates certain features in specified feature class
   * @param featureClass
   * @throws Exception
   */
  private void rotateFeatures(FeatureClass featureClass) throws Exception
  {
      //Create a set of features to rotate.
      int[] oidsToRotate = {1, 2};
      Set featureSet = new Set();
      for(int i = 0; i < oidsToRotate.length; i++)
      {
          Feature featureToRotate = (Feature) featureClass.getFeature(oidsToRotate[i]);
          featureSet.add(featureToRotate);
      }

      /*
       * Get a single feature and rotate it using rotationPoint and an angle
       */
      //Create a rotation point.
      Point rotationPoint = new Point();
      rotationPoint.setX(-117.4450);
      rotationPoint.setY(49.0166);

      //retrieve a feature
      Feature feature = (Feature) featureClass.getFeature(2);

      //rotate feature
      //no changes happen to feature. only features in feature set get rotated.
      feature.rotateSet(featureSet, rotationPoint, Math.PI);
  }

  /**
   * moves certain features in specified feature class
   * @param featureClass
   * @throws Exception
   */
  private void moveFeatures(FeatureClass featureClass) throws Exception
  {
        //Create a set of features to move.
        int[] oidsToRotate = {1, 2, 3}; //4, 5, 6};
        Set featureSet = new Set();
        for(int i = 0; i < oidsToRotate.length; i++)
        {
            Feature featureToRotate = (Feature) featureClass.getFeature(oidsToRotate[i]);
            featureSet.add(featureToRotate);
        }

        /*
         * Create a move vector and move features along the vector
         */
        //Create a move vector
        Point fromPoint = new Point();
        fromPoint.setX(0);
        fromPoint.setY(0);

        Point toPoint = new Point();
        toPoint.setX(0.1);
        toPoint.setY(0.1);

      Line moveVector = new Line();
      moveVector.setFromPoint(fromPoint);
      moveVector.setToPoint(toPoint);

      // Get a single feature to access the IFeatureEdit interface.
      Feature feature = (Feature) featureClass.getFeature(2);
      feature.moveSet(featureSet, moveVector);
  }

    /**
     * Creates the XFormer FC
     * @param ws
     * @param polesFCName
     * @return
     * @throws Exception
     */
    private FeatureClass createXFormerFC(Workspace ws, String xformerFCName) throws Exception
    {
        Fields fields = new Fields();

        //OBJECTID field
        Field field = new Field();
        field.setName("OBJECTID");
        field.setType(esriFieldType.esriFieldTypeOID);
        fields.addField(field);
        field = null;

        //shape field
        field = new Field();

        GeometryDef gdef = new GeometryDef();
        gdef.setGeometryType(esriGeometryType.esriGeometryPoint);
        gdef.setHasM(false);
        gdef.setHasZ(false);

        SpatialReferenceEnvironment sre = new SpatialReferenceEnvironment();
        GeographicCoordinateSystem gcs = (GeographicCoordinateSystem) sre.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_NAD1983);
        gcs.setDomain(-400, 8099972.71208311, -400, 8099972.71208311);

        gdef.setSpatialReferenceByRef(gcs);

        field.setName("shape");
        field.setType(esriFieldType.esriFieldTypeGeometry);
        field.setGeometryDefByRef(gdef);
        fields.addField(field);
        field = null;

        //pole id field
        field = new Field();
        field.setName("poleid");
        field.setType(esriFieldType.esriFieldTypeInteger);
        field.setEditable(true);
        fields.addField(field);
        field = null;

        //create and return feature class
        FeatureClass xformerFC = new FeatureClass(ws.createFeatureClass(xformerFCName, fields, null, null, esriFeatureType.esriFTSimple, "shape", "default"));

        System.out.println("Num features in polesFC before edit: " + xformerFC.featureCount(null));
        ISpatialReference sr = xformerFC.getSpatialReference();
        FeatureCursor featureCursor = new FeatureCursor(xformerFC.IFeatureClass_insert(true));

        //add feature 1
        Feature feature1 = (Feature) xformerFC.createFeature();

        Point point1 = new Point();
        point1.setX(-78.300683);
        point1.setY(34.099027);
        point1.setSpatialReferenceByRef(sr);

        feature1.setShapeByRef(point1);
        feature1.setValue(2, 1);
        feature1.store();

        //add feature 2
        Feature feature2 = (Feature) xformerFC.createFeature();

        Point point2 = new Point();
        point2.setX(-76.837546);
        point2.setY(29.709614);
        point2.setSpatialReferenceByRef(sr);

        feature2.setShapeByRef(point2);
        feature2.setValue(2, 1);
        feature2.store();

        //add feature 3
        Feature feature3 = (Feature) xformerFC.createFeature();

        Point point3 = new Point();
        point3.setX(-20.573254);
        point3.setY(75.465917);
        point3.setSpatialReferenceByRef(sr);

        feature3.setShapeByRef(point3);
        feature3.setValue(2, 2);
        feature3.store();

        //add feature 4
        Feature feature4 = (Feature) xformerFC.createFeature();

        Point point4 = new Point();
        point4.setX(4.965148);
        point4.setY(46.462838);
        point4.setSpatialReferenceByRef(sr);

        feature4.setShapeByRef(point4);
        feature4.setValue(2, 3);
        feature4.store();

        //add feature 5
        Feature feature5 = (Feature) xformerFC.createFeature();

        Point point5 = new Point();
        point5.setX(2.570923);
        point5.setY(28.778526);
        point5.setSpatialReferenceByRef(sr);

        feature5.setShapeByRef(point5);
        feature5.setValue(2, 3);
        feature5.store();

        //add feature 6
        Feature feature6 = (Feature) xformerFC.createFeature();

        Point point6 = new Point();
        point6.setX(9.354561);
        point6.setY(24.921164);
        point6.setSpatialReferenceByRef(sr);

        feature6.setShapeByRef(point6);
        feature6.setValue(2, 3);
        feature6.store();

        featureCursor.flush();
        featureCursor.release();

        System.out.println("Num features in xformerFC after edit: " + xformerFC.featureCount(null));

        return xformerFC;
    }

  /**
   * Creates the Pole FC
   * @param ws
   * @param polesFCName
   * @return
   * @throws Exception
   */
  private FeatureClass createPolesFC(Workspace ws, String polesFCName) throws Exception
  {
        Fields fields = new Fields();

        //OBJECTID field
        Field field = new Field();
        field.setName("OBJECTID");
        field.setType(esriFieldType.esriFieldTypeOID);
        fields.addField(field);
        field = null;

        //shape field
        field = new Field();

        GeometryDef gdef = new GeometryDef();
        gdef.setGeometryType(esriGeometryType.esriGeometryPoint);
        gdef.setHasM(false);
        gdef.setHasZ(false);

        SpatialReferenceEnvironment sre = new SpatialReferenceEnvironment();
        GeographicCoordinateSystem gcs = (GeographicCoordinateSystem) sre.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_NAD1983);
        gcs.setDomain(-400, 8099972.71208311, -400, 8099972.71208311);

        gdef.setSpatialReferenceByRef(gcs);

        field.setName("shape");
        field.setType(esriFieldType.esriFieldTypeGeometry);
        field.setGeometryDefByRef(gdef);
        fields.addField(field);
        field = null;

        //create and return feature class
        FeatureClass polesFC = new FeatureClass(ws.createFeatureClass(polesFCName, fields, null, null, esriFeatureType.esriFTSimple, "shape", "default"));
        System.out.println("Num features in polesFC before edit: " + polesFC.featureCount(null));
        ISpatialReference sr = polesFC.getSpatialReference();
        FeatureCursor featureCursor = new FeatureCursor(polesFC.IFeatureClass_insert(true));

        //add feature 1
        Feature feature1 = (Feature) polesFC.createFeature();

        Point point1 = new Point();
        point1.setX(-77.236583);
        point1.setY(30.773714);
        point1.setSpatialReferenceByRef(sr);

        feature1.setShapeByRef(point1);
        feature1.store();

        //add feature 2
        Feature feature2 = (Feature) polesFC.createFeature();

        Point point2 = new Point();
        point2.setX(-18.711079);
        point2.setY(73.470370);
        point2.setSpatialReferenceByRef(sr);

        feature2.setShapeByRef(point2);
        feature2.store();

        //add feature 3
        Feature feature3 = (Feature) polesFC.createFeature();

        Point point3 = new Point();
        point3.setX(5.364185);
        point3.setY(24.123089);
        point3.setSpatialReferenceByRef(sr);

        feature3.setShapeByRef(point3);
        feature3.store();

        //add feature 4
        Feature feature4 = (Feature) polesFC.createFeature();

        Point point4 = new Point();
        point4.setX(-65.7057);
        point4.setY(68.0339);
        point4.setSpatialReferenceByRef(sr);

        feature4.setShapeByRef(point4);
        feature4.store();

        featureCursor.flush();
        featureCursor.release();

        System.out.println("Num features in polesFC after edit: " + polesFC.featureCount(null));

        return polesFC;
  }

  /*
   * Returns valid jar file path
   * @param jarFileName
   * @return
   */
  private boolean isJarFileValid(String jarFilePath) throws FileNotFoundException
  {
    if(jarFilePath.length() == 0 || jarFilePath == null)
    {
      System.err.println("Error: Jar file path is empty, null or it contains invalid/unsupported characters.");
      System.exit(-1);
    }
    else
    {      
      File jarFile = new File(jarFilePath);
      if(jarFile.exists())
      {
        if(!jarFile.isFile() && !jarFilePath.endsWith(".jar"))
        {
          throw new FileNotFoundException("Jar file " + jarFile + " not found. Please verify if name of jar file containing Class Extension is correct.");
        }
      }
    }
    
    return true;
  }
  
  /*
   * Retrieve list of valid SOE classes in specified jar file
   */
  private ArrayList<Class> getCEClass(String jarFilePath)
  {
    ArrayList<Class> ceClassList = new ArrayList<Class>(5);
    
    //validate input path
    if(System.getProperty("os.name").toLowerCase().startsWith("windows"))
    {
      if(jarFilePath.contains("Program Files"))
      {
        jarFilePath = jarFilePath.replace("Program Files", "Progra~1");
      }
      else if(jarFilePath.contains("Program Files (x86)"))
      {
        jarFilePath = jarFilePath.replace("Program Files (x86)", "Progra~2");
      } 
    }    
        
    //validate file indicated by jar file name. 
    try
    {        
      //load classes present inside jar file into memory
      Bootstrapper.loadExtensionJar(jarFilePath);        
      
      //retrieve individual contents of jar file
      JarFile jarFile = new JarFile(jarFilePath);
      Enumeration<JarEntry> jarContents = jarFile.entries();
      while (jarContents.hasMoreElements())
      {
        ZipEntry zipEntry = (ZipEntry) jarContents.nextElement();
        if(!zipEntry.isDirectory())
        {
          //ignore current file if it does not end with .class
          String fileName = zipEntry.getName();
          if(fileName.endsWith(".class"))
          {
            //retrieve the Class object for current class file
            String ceFullyQualifiedName = fileName.replace('/', '.').substring(0, fileName.length() -  ".class".length());
            Class classDef = Class.forName(ceFullyQualifiedName);
            
            //consider class only if its not an interface and its annotated with ArcGISExtension
            if(!classDef.isInterface() && classDef.isAnnotationPresent(ArcGISExtension.class))
            {
              //add the class to a list
              ceClassList.add(classDef);
            }
          }
        }
      }
      
      //trim the list
      ceClassList.trimToSize();
      
      //if list is of size 0
      if(ceClassList.size() <= 0)
      {
        System.err.println("\nError: No .class files or .class files containing Class Extension found in " + jarFilePath);
      }        
      
      //return it
      return ceClassList;
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    catch(ClassNotFoundException ce)
    {
      ce.printStackTrace();
    }
    

    return null;
  }    
  
  /**
   * Creates a new directory
   * @param pathName String
   */
  public void createFolder(String pathName)
  {
    File f = new File(pathName);
    if (!f.exists())
    {
      f.mkdir();
    }
  }

  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void emptyFolder(String dirName, boolean includeSubFolder) throws Exception
  {
    File src = new File(dirName);
    if (src.isDirectory() && src.exists())
    {
      File list[] = src.listFiles();
      for (int i = 0; i < list.length; i++)
      {
        if (includeSubFolder && list[i].isDirectory())
        {
          emptyFolder(list[i].getPath(), includeSubFolder);
          list[i].delete();
        }
        else
        {
          list[i].delete();
        }
      }
    }
    else
    {
      throw new Exception("MatrixUtil.emptyFolder() - " + dirName + " is not a directory or does not exist.");
    }
  }

  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void deleteFolder(String dirName) throws Exception
  {
    File src = new File(dirName);
    if (src.isDirectory() && src.exists())
    {
      File list[] = src.listFiles();
      for (int i = 0; i < list.length; i++)
      {
        if (list[i].isDirectory())
        {
          emptyFolder(list[i].getPath(), true);
          list[i].delete();
        }
        else
        {
          list[i].delete();
        }
      }
      src.delete();
    }
    else
    {
      src.delete();
    }
  }

  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void deleteFile(String fullyQualifiedFileName) throws Exception
  {
    File file = new File(fullyQualifiedFileName);
    if(!file.isDirectory() && file.exists())
    {
      file.delete();
    }
    else
    {
      throw new Exception("MatrixUtil.deleteFile() - File " + fullyQualifiedFileName + " does not exist or is a directory.");
    }
  }
}