// setHandler creates a Proxy object from the passed OSXAdapter and adds it as an
 // ApplicationListener
 @SuppressWarnings({"rawtypes", "unchecked"})
 public static void setHandler(OSXAdapter adapter) {
   try {
     Class applicationClass = Class.forName("com.apple.eawt.Application");
     if (macOSXApplication == null) {
       macOSXApplication =
           applicationClass.getConstructor((Class[]) null).newInstance((Object[]) null);
     }
     Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
     Method addListenerMethod =
         applicationClass.getDeclaredMethod(
             "addApplicationListener", new Class[] {applicationListenerClass});
     // Create a proxy object around this handler that can be reflectively added as an Apple
     // ApplicationListener
     Object osxAdapterProxy =
         Proxy.newProxyInstance(
             OSXAdapter.class.getClassLoader(), new Class[] {applicationListenerClass}, adapter);
     addListenerMethod.invoke(macOSXApplication, new Object[] {osxAdapterProxy});
   } catch (ClassNotFoundException cnfe) {
     LOG.debug(
         "This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled.");
   } catch (
       Exception
           ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking
                 // eawt.Application methods
     LOG.debug("Failed to access Mac OS X EAWT: " + ex.getMessage());
   }
 }
 @SuppressWarnings({"rawtypes", "unchecked"})
 public static void setDockIconImage(Image image) {
   try {
     Class applicationClass = Class.forName("com.apple.eawt.Application");
     if (macOSXApplication == null) {
       macOSXApplication =
           applicationClass.getConstructor((Class[]) null).newInstance((Object[]) null);
     }
     Method setDockIconImage =
         applicationClass.getDeclaredMethod("setDockIconImage", new Class[] {Image.class});
     setDockIconImage.invoke(macOSXApplication, image);
   } catch (ClassNotFoundException e) {
     LOG.debug(
         "This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled.");
   } catch (Exception e) {
     LOG.debug("Failed to access Mac OS X EAWT: " + e.getMessage());
   }
 }
 // It is important to mark the ApplicationEvent as handled and cancel the default behavior
 // This method checks for a boolean result from the proxy method and sets the event accordingly
 protected void setApplicationEventHandled(Object event, boolean handled) {
   if (event != null) {
     try {
       Method setHandledMethod =
           event.getClass().getDeclaredMethod("setHandled", new Class[] {boolean.class});
       // If the target method returns a boolean, use that as a hint
       setHandledMethod.invoke(event, new Object[] {Boolean.valueOf(handled)});
     } catch (Exception ex) {
       LOG.debug("Failed to handle an Mac OS X EAWT ApplicationEvent: " + ex.getMessage());
     }
   }
 }
 // Pass this method an Object and a Method equipped to display application options
 // They will be called when the Preferences menu item is selected from the application menu
 public static void setPreferencesHandler(Object target, Method prefsHandler) {
   boolean enablePrefsMenu = (target != null && prefsHandler != null);
   if (enablePrefsMenu) {
     setHandler(new OSXAdapter("handlePreferences", target, prefsHandler));
   }
   // If we're setting a handler, enable the Preferences menu item by calling
   // com.apple.eawt.Application reflectively
   try {
     Method enablePrefsMethod =
         macOSXApplication
             .getClass()
             .getDeclaredMethod("setEnabledPreferencesMenu", new Class[] {boolean.class});
     enablePrefsMethod.invoke(macOSXApplication, new Object[] {Boolean.valueOf(enablePrefsMenu)});
   } catch (Exception ex) {
     LOG.debug("Failed to enable the Preferences entry in the Mac OS X application menu.");
   }
 }
public class OSXAdapter implements InvocationHandler {
  public static final Logger LOG = Logger.getInstance();
  public static final boolean IS_MAC_OS_X =
      System.getProperty("os.name").toLowerCase().startsWith("mac os x");

  protected Object targetObject;
  protected Method targetMethod;
  protected String proxySignature;

  static Object macOSXApplication;

  // Pass this method an Object and Method equipped to perform application shutdown logic
  // The method passed should return a boolean stating whether or not the quit should occur
  public static void setQuitHandler(Object target, Method quitHandler) {
    setHandler(new OSXAdapter("handleQuit", target, quitHandler));
  }

  // Pass this method an Object and Method equipped to display application info
  // They will be called when the About menu item is selected from the application menu
  public static void setAboutHandler(Object target, Method aboutHandler) {
    boolean enableAboutMenu = (target != null && aboutHandler != null);
    if (enableAboutMenu) {
      setHandler(new OSXAdapter("handleAbout", target, aboutHandler));
    }
    // If we're setting a handler, enable the About menu item by calling
    // com.apple.eawt.Application reflectively
    try {
      Method enableAboutMethod =
          macOSXApplication
              .getClass()
              .getDeclaredMethod("setEnabledAboutMenu", new Class[] {boolean.class});
      enableAboutMethod.invoke(macOSXApplication, new Object[] {Boolean.valueOf(enableAboutMenu)});
    } catch (Exception ex) {
      LOG.debug("Failed to activate the About entry in the Mac OS X application menu.");
    }
  }

  // Pass this method an Object and a Method equipped to display application options
  // They will be called when the Preferences menu item is selected from the application menu
  public static void setPreferencesHandler(Object target, Method prefsHandler) {
    boolean enablePrefsMenu = (target != null && prefsHandler != null);
    if (enablePrefsMenu) {
      setHandler(new OSXAdapter("handlePreferences", target, prefsHandler));
    }
    // If we're setting a handler, enable the Preferences menu item by calling
    // com.apple.eawt.Application reflectively
    try {
      Method enablePrefsMethod =
          macOSXApplication
              .getClass()
              .getDeclaredMethod("setEnabledPreferencesMenu", new Class[] {boolean.class});
      enablePrefsMethod.invoke(macOSXApplication, new Object[] {Boolean.valueOf(enablePrefsMenu)});
    } catch (Exception ex) {
      LOG.debug("Failed to enable the Preferences entry in the Mac OS X application menu.");
    }
  }

  // Pass this method an Object and a Method equipped to handle document events from the Finder
  // Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the
  // application bundle's Info.plist
  public static void setFileHandler(Object target, Method fileHandler) {
    setHandler(
        new OSXAdapter("handleOpenFile", target, fileHandler) {
          // Override OSXAdapter.callTarget to send information on the
          // file to be opened
          public boolean callTarget(Object appleEvent) {
            if (appleEvent != null) {
              try {
                Method getFilenameMethod =
                    appleEvent.getClass().getDeclaredMethod("getFilename", (Class[]) null);
                String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[]) null);
                this.targetMethod.invoke(this.targetObject, new Object[] {filename});
              } catch (Exception ex) {

              }
            }
            return true;
          }
        });
  }

  @SuppressWarnings({"rawtypes", "unchecked"})
  public static void setDockIconImage(Image image) {
    try {
      Class applicationClass = Class.forName("com.apple.eawt.Application");
      if (macOSXApplication == null) {
        macOSXApplication =
            applicationClass.getConstructor((Class[]) null).newInstance((Object[]) null);
      }
      Method setDockIconImage =
          applicationClass.getDeclaredMethod("setDockIconImage", new Class[] {Image.class});
      setDockIconImage.invoke(macOSXApplication, image);
    } catch (ClassNotFoundException e) {
      LOG.debug(
          "This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled.");
    } catch (Exception e) {
      LOG.debug("Failed to access Mac OS X EAWT: " + e.getMessage());
    }
  }

  // setHandler creates a Proxy object from the passed OSXAdapter and adds it as an
  // ApplicationListener
  @SuppressWarnings({"rawtypes", "unchecked"})
  public static void setHandler(OSXAdapter adapter) {
    try {
      Class applicationClass = Class.forName("com.apple.eawt.Application");
      if (macOSXApplication == null) {
        macOSXApplication =
            applicationClass.getConstructor((Class[]) null).newInstance((Object[]) null);
      }
      Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
      Method addListenerMethod =
          applicationClass.getDeclaredMethod(
              "addApplicationListener", new Class[] {applicationListenerClass});
      // Create a proxy object around this handler that can be reflectively added as an Apple
      // ApplicationListener
      Object osxAdapterProxy =
          Proxy.newProxyInstance(
              OSXAdapter.class.getClassLoader(), new Class[] {applicationListenerClass}, adapter);
      addListenerMethod.invoke(macOSXApplication, new Object[] {osxAdapterProxy});
    } catch (ClassNotFoundException cnfe) {
      LOG.debug(
          "This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled.");
    } catch (
        Exception
            ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking
                  // eawt.Application methods
      LOG.debug("Failed to access Mac OS X EAWT: " + ex.getMessage());
    }
  }

  // Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for
  // example),
  // the Object that will ultimately perform the task, and the Method to be called on that Object
  protected OSXAdapter(String proxySignature, Object target, Method handler) {
    this.proxySignature = proxySignature;
    this.targetObject = target;
    this.targetMethod = handler;
  }

  // Override this method to perform any operations on the event
  // that comes with the various callbacks
  // See setFileHandler above for an example
  public boolean callTarget(Object appleEvent)
      throws InvocationTargetException, IllegalAccessException {
    Object result = targetMethod.invoke(targetObject, (Object[]) null);
    if (result == null) {
      return true;
    }
    return Boolean.valueOf(result.toString()).booleanValue();
  }

  // InvocationHandler implementation
  // This is the entry point for our proxy object; it is called every time an ApplicationListener
  // method is invoked
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (isCorrectMethod(method, args)) {
      boolean handled = callTarget(args[0]);
      setApplicationEventHandled(args[0], handled);
    }
    // All of the ApplicationListener methods are void; return null regardless of what happens
    return null;
  }

  // Compare the method that was called to the intended method when the OSXAdapter instance was
  // created
  // (e.g. handleAbout, handleQuit, handleOpenFile, etc.)
  protected boolean isCorrectMethod(Method method, Object[] args) {
    return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1);
  }

  // It is important to mark the ApplicationEvent as handled and cancel the default behavior
  // This method checks for a boolean result from the proxy method and sets the event accordingly
  protected void setApplicationEventHandled(Object event, boolean handled) {
    if (event != null) {
      try {
        Method setHandledMethod =
            event.getClass().getDeclaredMethod("setHandled", new Class[] {boolean.class});
        // If the target method returns a boolean, use that as a hint
        setHandledMethod.invoke(event, new Object[] {Boolean.valueOf(handled)});
      } catch (Exception ex) {
        LOG.debug("Failed to handle an Mac OS X EAWT ApplicationEvent: " + ex.getMessage());
      }
    }
  }
}
public class DBAddress implements DBImporter {
  private final Logger LOG = Logger.getInstance();
  private final Connection batchConn;
  private final Config config;
  private final DBImporterManager dbImporterManager;

  private PreparedStatement psAddress;
  private DBAddressToBuilding addressToBuildingImporter;
  private DBStGeometry stGeometry;
  private int batchCounter;

  private boolean importXalSource;

  public DBAddress(Connection batchConn, Config config, DBImporterManager dbImporterManager)
      throws SQLException {
    this.batchConn = batchConn;
    this.config = config;
    this.dbImporterManager = dbImporterManager;

    init();
  }

  private void init() throws SQLException {
    importXalSource = config.getProject().getImporter().getAddress().isSetImportXAL();

    psAddress =
        batchConn.prepareStatement(
            "insert into ADDRESS (ID, STREET, HOUSE_NUMBER, PO_BOX, ZIP_CODE, CITY, COUNTRY, MULTI_POINT, XAL_SOURCE) values "
                + "(?, ?, ?, ?, ?, ?, ?, ?, ?)");

    addressToBuildingImporter =
        (DBAddressToBuilding) dbImporterManager.getDBImporter(DBImporterEnum.ADDRESS_TO_BUILDING);
    stGeometry = (DBStGeometry) dbImporterManager.getDBImporter(DBImporterEnum.ST_GEOMETRY);
  }

  public long insert(Address address) throws SQLException {
    if (!address.isSetXalAddress() || !address.getXalAddress().isSetAddressDetails()) return 0;

    XalAddressProperty xalAddressProperty = address.getXalAddress();
    AddressDetails addressDetails = xalAddressProperty.getAddressDetails();

    // ok, let's start
    long addressId = dbImporterManager.getDBId(DBSequencerEnum.ADDRESS_ID_SEQ);
    if (addressId == 0) return 0;

    boolean success = false;
    String streetAttr, houseNoAttr, poBoxAttr, zipCodeAttr, cityAttr, countryAttr, xalSource;
    streetAttr = houseNoAttr = poBoxAttr = zipCodeAttr = cityAttr = countryAttr = xalSource = null;
    PGgeometry multiPoint = null;

    // try and interpret <country> child element
    if (addressDetails.isSetCountry()) {
      Country country = addressDetails.getCountry();

      // country name
      if (country.isSetCountryName()) {
        List<String> countryName = new ArrayList<String>();
        for (CountryName name : country.getCountryName()) countryName.add(name.getContent());

        countryAttr = Util.collection2string(countryName, ",");
      }

      // locality
      if (country.isSetLocality()) {
        Locality locality = country.getLocality();

        // check whether we deal with a city or a town
        if (locality.isSetType()
            && (locality.getType().toUpperCase().equals("CITY")
                || locality.getType().toUpperCase().equals("TOWN"))) {

          // city name
          if (locality.isSetLocalityName()) {
            List<String> localityName = new ArrayList<String>();
            for (LocalityName name : locality.getLocalityName())
              localityName.add(name.getContent());

            cityAttr = Util.collection2string(localityName, ",");
          }

          // thoroughfare - just streets are supported
          if (locality.isSetThoroughfare()) {
            Thoroughfare thoroughfare = locality.getThoroughfare();

            // check whether we deal with a street
            if (thoroughfare.isSetType()
                && (thoroughfare.getType().toUpperCase().equals("STREET")
                    || thoroughfare.getType().toUpperCase().equals("ROAD"))) {

              // street name
              if (thoroughfare.isSetThoroughfareName()) {
                List<String> fareName = new ArrayList<String>();
                for (ThoroughfareName name : thoroughfare.getThoroughfareName())
                  fareName.add(name.getContent());

                streetAttr = Util.collection2string(fareName, ",");
              }

              // house number - we do not support number ranges so far...
              if (thoroughfare.isSetThoroughfareNumberOrThoroughfareNumberRange()) {
                List<String> houseNumber = new ArrayList<String>();
                for (ThoroughfareNumberOrRange number :
                    thoroughfare.getThoroughfareNumberOrThoroughfareNumberRange()) {
                  if (number.isSetThoroughfareNumber())
                    houseNumber.add(number.getThoroughfareNumber().getContent());
                }

                houseNoAttr = Util.collection2string(houseNumber, ",");
              }
            }
          }

          // dependent locality
          if (streetAttr == null && houseNoAttr == null && locality.isSetDependentLocality()) {
            DependentLocality dependentLocality = locality.getDependentLocality();

            if (dependentLocality.isSetType()
                && dependentLocality.getType().toUpperCase().equals("DISTRICT")) {

              if (dependentLocality.isSetThoroughfare()) {
                Thoroughfare thoroughfare = dependentLocality.getThoroughfare();

                // street name
                if (streetAttr == null && thoroughfare.isSetThoroughfareName()) {
                  List<String> fareName = new ArrayList<String>();
                  for (ThoroughfareName name : thoroughfare.getThoroughfareName())
                    fareName.add(name.getContent());

                  streetAttr = Util.collection2string(fareName, ",");
                }

                // house number - we do not support number ranges so far...
                if (houseNoAttr == null
                    && thoroughfare.isSetThoroughfareNumberOrThoroughfareNumberRange()) {
                  List<String> houseNumber = new ArrayList<String>();
                  for (ThoroughfareNumberOrRange number :
                      thoroughfare.getThoroughfareNumberOrThoroughfareNumberRange()) {
                    if (number.isSetThoroughfareNumber())
                      houseNumber.add(number.getThoroughfareNumber().getContent());
                  }

                  houseNoAttr = Util.collection2string(houseNumber, ",");
                }
              }
            }
          }

          // postal code
          if (locality.isSetPostalCode()) {
            PostalCode postalCode = locality.getPostalCode();

            // get postal code number
            if (postalCode.isSetPostalCodeNumber()) {
              List<String> zipCode = new ArrayList<String>();
              for (PostalCodeNumber number : postalCode.getPostalCodeNumber())
                zipCode.add(number.getContent());

              zipCodeAttr = Util.collection2string(zipCode, ",");
            }
          }

          // post box
          if (locality.isSetPostBox()) {
            PostBox postBox = locality.getPostBox();

            // get post box nummber
            if (postBox.isSetPostBoxNumber()) poBoxAttr = postBox.getPostBoxNumber().getContent();
          }
        }
      }

      // multiPoint geometry
      if (address.isSetMultiPoint()) multiPoint = stGeometry.getMultiPoint(address.getMultiPoint());

      success = true;
    } else {
      StringBuilder msg =
          new StringBuilder(Util.getFeatureSignature(address.getCityGMLClass(), address.getId()));
      msg.append(": Failed to interpret xAL address element.");
      LOG.error(msg.toString());
    }

    // get XML representation of <xal:AddressDetails>
    if (importXalSource) {
      xalSource = dbImporterManager.marshal(addressDetails, XALModuleType.CORE);
      if (xalSource != null && xalSource.length() > 0) success = true;
    }

    if (success) {
      psAddress.setLong(1, addressId);
      psAddress.setString(2, streetAttr);
      psAddress.setString(3, houseNoAttr);
      psAddress.setString(4, poBoxAttr);
      psAddress.setString(5, zipCodeAttr);
      psAddress.setString(6, cityAttr);
      psAddress.setString(7, countryAttr);

      if (multiPoint != null) {
        psAddress.setObject(8, multiPoint);
      } else psAddress.setNull(8, Types.OTHER, "ST_GEOMETRY");

      if (xalSource != null) psAddress.setString(9, xalSource);
      else psAddress.setNull(9, Types.CLOB);

      psAddress.addBatch();
      if (++batchCounter == Internal.POSTGRESQL_MAX_BATCH_SIZE)
        dbImporterManager.executeBatch(DBImporterEnum.ADDRESS);

      // enable xlinks
      if (address.isSetId())
        dbImporterManager.putGmlId(address.getId(), addressId, address.getCityGMLClass());
    } else addressId = 0;

    return addressId;
  }

  public long insert(Address address, long parentId) throws SQLException {
    long addressId = insert(address);

    if (addressId != 0) addressToBuildingImporter.insert(addressId, parentId);

    return addressId;
  }

  @Override
  public void executeBatch() throws SQLException {
    psAddress.executeBatch();
    batchCounter = 0;
  }

  @Override
  public void close() throws SQLException {
    psAddress.close();
  }

  @Override
  public DBImporterEnum getDBImporterType() {
    return DBImporterEnum.ADDRESS;
  }
}
  public long insert(Address address) throws SQLException {
    if (!address.isSetXalAddress() || !address.getXalAddress().isSetAddressDetails()) return 0;

    XalAddressProperty xalAddressProperty = address.getXalAddress();
    AddressDetails addressDetails = xalAddressProperty.getAddressDetails();

    // ok, let's start
    long addressId = dbImporterManager.getDBId(DBSequencerEnum.ADDRESS_ID_SEQ);
    if (addressId == 0) return 0;

    boolean success = false;
    String streetAttr, houseNoAttr, poBoxAttr, zipCodeAttr, cityAttr, countryAttr, xalSource;
    streetAttr = houseNoAttr = poBoxAttr = zipCodeAttr = cityAttr = countryAttr = xalSource = null;
    PGgeometry multiPoint = null;

    // try and interpret <country> child element
    if (addressDetails.isSetCountry()) {
      Country country = addressDetails.getCountry();

      // country name
      if (country.isSetCountryName()) {
        List<String> countryName = new ArrayList<String>();
        for (CountryName name : country.getCountryName()) countryName.add(name.getContent());

        countryAttr = Util.collection2string(countryName, ",");
      }

      // locality
      if (country.isSetLocality()) {
        Locality locality = country.getLocality();

        // check whether we deal with a city or a town
        if (locality.isSetType()
            && (locality.getType().toUpperCase().equals("CITY")
                || locality.getType().toUpperCase().equals("TOWN"))) {

          // city name
          if (locality.isSetLocalityName()) {
            List<String> localityName = new ArrayList<String>();
            for (LocalityName name : locality.getLocalityName())
              localityName.add(name.getContent());

            cityAttr = Util.collection2string(localityName, ",");
          }

          // thoroughfare - just streets are supported
          if (locality.isSetThoroughfare()) {
            Thoroughfare thoroughfare = locality.getThoroughfare();

            // check whether we deal with a street
            if (thoroughfare.isSetType()
                && (thoroughfare.getType().toUpperCase().equals("STREET")
                    || thoroughfare.getType().toUpperCase().equals("ROAD"))) {

              // street name
              if (thoroughfare.isSetThoroughfareName()) {
                List<String> fareName = new ArrayList<String>();
                for (ThoroughfareName name : thoroughfare.getThoroughfareName())
                  fareName.add(name.getContent());

                streetAttr = Util.collection2string(fareName, ",");
              }

              // house number - we do not support number ranges so far...
              if (thoroughfare.isSetThoroughfareNumberOrThoroughfareNumberRange()) {
                List<String> houseNumber = new ArrayList<String>();
                for (ThoroughfareNumberOrRange number :
                    thoroughfare.getThoroughfareNumberOrThoroughfareNumberRange()) {
                  if (number.isSetThoroughfareNumber())
                    houseNumber.add(number.getThoroughfareNumber().getContent());
                }

                houseNoAttr = Util.collection2string(houseNumber, ",");
              }
            }
          }

          // dependent locality
          if (streetAttr == null && houseNoAttr == null && locality.isSetDependentLocality()) {
            DependentLocality dependentLocality = locality.getDependentLocality();

            if (dependentLocality.isSetType()
                && dependentLocality.getType().toUpperCase().equals("DISTRICT")) {

              if (dependentLocality.isSetThoroughfare()) {
                Thoroughfare thoroughfare = dependentLocality.getThoroughfare();

                // street name
                if (streetAttr == null && thoroughfare.isSetThoroughfareName()) {
                  List<String> fareName = new ArrayList<String>();
                  for (ThoroughfareName name : thoroughfare.getThoroughfareName())
                    fareName.add(name.getContent());

                  streetAttr = Util.collection2string(fareName, ",");
                }

                // house number - we do not support number ranges so far...
                if (houseNoAttr == null
                    && thoroughfare.isSetThoroughfareNumberOrThoroughfareNumberRange()) {
                  List<String> houseNumber = new ArrayList<String>();
                  for (ThoroughfareNumberOrRange number :
                      thoroughfare.getThoroughfareNumberOrThoroughfareNumberRange()) {
                    if (number.isSetThoroughfareNumber())
                      houseNumber.add(number.getThoroughfareNumber().getContent());
                  }

                  houseNoAttr = Util.collection2string(houseNumber, ",");
                }
              }
            }
          }

          // postal code
          if (locality.isSetPostalCode()) {
            PostalCode postalCode = locality.getPostalCode();

            // get postal code number
            if (postalCode.isSetPostalCodeNumber()) {
              List<String> zipCode = new ArrayList<String>();
              for (PostalCodeNumber number : postalCode.getPostalCodeNumber())
                zipCode.add(number.getContent());

              zipCodeAttr = Util.collection2string(zipCode, ",");
            }
          }

          // post box
          if (locality.isSetPostBox()) {
            PostBox postBox = locality.getPostBox();

            // get post box nummber
            if (postBox.isSetPostBoxNumber()) poBoxAttr = postBox.getPostBoxNumber().getContent();
          }
        }
      }

      // multiPoint geometry
      if (address.isSetMultiPoint()) multiPoint = stGeometry.getMultiPoint(address.getMultiPoint());

      success = true;
    } else {
      StringBuilder msg =
          new StringBuilder(Util.getFeatureSignature(address.getCityGMLClass(), address.getId()));
      msg.append(": Failed to interpret xAL address element.");
      LOG.error(msg.toString());
    }

    // get XML representation of <xal:AddressDetails>
    if (importXalSource) {
      xalSource = dbImporterManager.marshal(addressDetails, XALModuleType.CORE);
      if (xalSource != null && xalSource.length() > 0) success = true;
    }

    if (success) {
      psAddress.setLong(1, addressId);
      psAddress.setString(2, streetAttr);
      psAddress.setString(3, houseNoAttr);
      psAddress.setString(4, poBoxAttr);
      psAddress.setString(5, zipCodeAttr);
      psAddress.setString(6, cityAttr);
      psAddress.setString(7, countryAttr);

      if (multiPoint != null) {
        psAddress.setObject(8, multiPoint);
      } else psAddress.setNull(8, Types.OTHER, "ST_GEOMETRY");

      if (xalSource != null) psAddress.setString(9, xalSource);
      else psAddress.setNull(9, Types.CLOB);

      psAddress.addBatch();
      if (++batchCounter == Internal.POSTGRESQL_MAX_BATCH_SIZE)
        dbImporterManager.executeBatch(DBImporterEnum.ADDRESS);

      // enable xlinks
      if (address.isSetId())
        dbImporterManager.putGmlId(address.getId(), addressId, address.getCityGMLClass());
    } else addressId = 0;

    return addressId;
  }
public class XlinkTexCoordList implements DBXlinkResolver {
  private final Logger LOG = Logger.getInstance();

  private final Connection batchConn;
  private final HeapCacheTable textureParamHeapTable;
  private final HeapCacheTable linearRingHeapTable;
  private final DBXlinkResolverManager resolverManager;

  private PreparedStatement psTexCoordList;
  private PreparedStatement psSelectLinearRing;
  private PreparedStatement psSelectInteriorLinearRing;
  private PreparedStatement psSelectTexCoord;

  private int batchCounter;

  public XlinkTexCoordList(
      Connection batchConn,
      HeapCacheTable textureParamHeapTable,
      HeapCacheTable linearRingHeapTable,
      DBXlinkResolverManager resolverManager)
      throws SQLException {
    this.batchConn = batchConn;
    this.textureParamHeapTable = textureParamHeapTable;
    this.linearRingHeapTable = linearRingHeapTable;
    this.resolverManager = resolverManager;

    init();
  }

  private void init() throws SQLException {
    psTexCoordList =
        batchConn.prepareStatement(
            "insert into TEXTUREPARAM (SURFACE_GEOMETRY_ID, IS_TEXTURE_PARAMETRIZATION, WORLD_TO_TEXTURE , TEXTURE_COORDINATES, SURFACE_DATA_ID) values "
                + "(?, 1, null, ?, ?)");

    Connection linearRingConn = linearRingHeapTable.getConnection();
    String linearRingTableName = linearRingHeapTable.getTableName();

    psSelectLinearRing =
        linearRingConn.prepareStatement(
            "select RING_NO, PARENT_GMLID from " + linearRingTableName + " where GMLID=?");
    psSelectInteriorLinearRing =
        linearRingConn.prepareStatement(
            "select GMLID, RING_NO from "
                + linearRingTableName
                + " where PARENT_GMLID=? and RING_NO<>0");
    psSelectTexCoord =
        textureParamHeapTable
            .getConnection()
            .prepareStatement(
                "select GMLID, TEXTURE_COORDINATES from "
                    + textureParamHeapTable.getTableName()
                    + " where TEXCOORDLIST_ID=? and not GMLID=?");
  }

  public boolean insert(DBXlinkTextureParam xlink) throws SQLException {
    String gmlId = xlink.getGmlId();
    ResultSet rs = null;

    // check whether we deal with a local gml:id
    // remote gml:ids are not supported so far...
    if (Util.isRemoteXlink(gmlId)) return false;

    // replace leading #
    gmlId = gmlId.replaceAll("^#", "");

    try {
      // step 1: get the exterior linear ring element
      psSelectLinearRing.setString(1, gmlId);
      rs = psSelectLinearRing.executeQuery();

      if (!rs.next()) return false;

      // if an interior ring is returned we silently discard it
      int exteriorRing = rs.getInt("RING_NO");
      if (exteriorRing != 0) return true;

      String parentGmlId = rs.getString("PARENT_GMLID");
      rs.close();

      // step 2: check whether parent geometry exists... we need to do this
      // since we require the database key for referencing
      GmlIdEntry surfaceGeometryEntry =
          resolverManager.getDBId(parentGmlId, CityGMLClass.ABSTRACT_GML_GEOMETRY);
      if (surfaceGeometryEntry == null || surfaceGeometryEntry.getId() == -1) {
        StringBuilder msg =
            new StringBuilder(Util.getGeometrySignature(GMLClass.LINEAR_RING, gmlId));
        msg.append(": The element could not be assigned to an existing geometry object.");

        LOG.error(msg.toString());
        return false;
      }

      // step 3: find all corresponding interior rings
      psSelectInteriorLinearRing.setString(1, parentGmlId);
      rs = psSelectInteriorLinearRing.executeQuery();

      HashMap<String, Integer> innerRingMap = new HashMap<String, Integer>();
      int maxRingNo = 0;
      while (rs.next()) {
        String innerGmlId = rs.getString("GMLID");
        if (innerGmlId == null) innerGmlId = parentGmlId;

        int ringNo = rs.getInt("RING_NO");

        innerRingMap.put(innerGmlId, ringNo);
        if (ringNo > maxRingNo) maxRingNo = ringNo;
      }

      rs.close();

      // step 4: find corresponding texture coordinates
      List<String> texCoordList = new ArrayList<String>();
      String textureCoordinates = xlink.getTextureCoord();

      // reverse order of texture coordinates if necessary
      if (surfaceGeometryEntry.isReverse())
        textureCoordinates = reverseTextureCoordinates(textureCoordinates);

      texCoordList.add(0, textureCoordinates);
      for (int i = 0; i < maxRingNo; i++) texCoordList.add("");

      psSelectTexCoord.setString(1, xlink.getTexCoordListId());
      psSelectTexCoord.setString(2, xlink.getGmlId());
      rs = psSelectTexCoord.executeQuery();

      while (rs.next()) {
        String innerGmlId = rs.getString("GMLID");
        textureCoordinates = rs.getString("TEXTURE_COORDINATES");

        if (Util.isRemoteXlink(innerGmlId)) continue;

        // reverse order of texture coordinates if necessary
        if (surfaceGeometryEntry.isReverse())
          textureCoordinates = reverseTextureCoordinates(textureCoordinates);

        // replace leading #
        innerGmlId = innerGmlId.replaceAll("^#", "");
        if (innerRingMap.containsKey(innerGmlId))
          texCoordList.set(innerRingMap.get(innerGmlId), textureCoordinates);
      }

      // step 5: sanity check
      String texCoord = Util.collection2string(texCoordList, ";");
      if (texCoord.length() > 4000) {
        LOG.error(
            "Texture coordinates exceed 4000 characters for target geometry object '"
                + parentGmlId
                + "'.");
        return false;
      }

      if (texCoord.contains(";;") || texCoord.endsWith(";"))
        LOG.warn("Missing texture coordinates for target geometry object '" + parentGmlId + "'.");

      psTexCoordList.setLong(1, surfaceGeometryEntry.getId());
      psTexCoordList.setString(2, texCoord);
      psTexCoordList.setLong(3, xlink.getId());

      psTexCoordList.addBatch();
      if (++batchCounter == Internal.POSTGRESQL_MAX_BATCH_SIZE) executeBatch();

      if (xlink.getTexParamGmlId() != null) {
        // propagate xlink...
        resolverManager.propagateXlink(
            new DBXlinkTextureAssociation(
                xlink.getId(), surfaceGeometryEntry.getId(), xlink.getTexParamGmlId()));
      }

      return true;
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException sqlEx) {
          //
        }

        rs = null;
      }
    }
  }

  private String reverseTextureCoordinates(String textureCoordinates) {
    String[] coords = textureCoordinates.split("\\s+");

    for (int lower = 0, upper = coords.length - 2; lower < upper; lower += 2, upper -= 2) {
      String x = coords[lower];
      String y = coords[lower + 1];

      coords[lower] = coords[upper];
      coords[lower + 1] = coords[upper + 1];

      coords[upper] = x;
      coords[upper + 1] = y;
    }

    return Util.collection2string(Arrays.asList(coords), " ");
  }

  @Override
  public void executeBatch() throws SQLException {
    psTexCoordList.executeBatch();
    batchCounter = 0;
  }

  @Override
  public void close() throws SQLException {
    psTexCoordList.close();
    psSelectLinearRing.close();
    psSelectInteriorLinearRing.close();
    psSelectTexCoord.close();
  }

  @Override
  public DBXlinkResolverEnum getDBXlinkResolverType() {
    return DBXlinkResolverEnum.TEXCOORDLIST;
  }
}
  public boolean insert(DBXlinkTextureParam xlink) throws SQLException {
    String gmlId = xlink.getGmlId();
    ResultSet rs = null;

    // check whether we deal with a local gml:id
    // remote gml:ids are not supported so far...
    if (Util.isRemoteXlink(gmlId)) return false;

    // replace leading #
    gmlId = gmlId.replaceAll("^#", "");

    try {
      // step 1: get the exterior linear ring element
      psSelectLinearRing.setString(1, gmlId);
      rs = psSelectLinearRing.executeQuery();

      if (!rs.next()) return false;

      // if an interior ring is returned we silently discard it
      int exteriorRing = rs.getInt("RING_NO");
      if (exteriorRing != 0) return true;

      String parentGmlId = rs.getString("PARENT_GMLID");
      rs.close();

      // step 2: check whether parent geometry exists... we need to do this
      // since we require the database key for referencing
      GmlIdEntry surfaceGeometryEntry =
          resolverManager.getDBId(parentGmlId, CityGMLClass.ABSTRACT_GML_GEOMETRY);
      if (surfaceGeometryEntry == null || surfaceGeometryEntry.getId() == -1) {
        StringBuilder msg =
            new StringBuilder(Util.getGeometrySignature(GMLClass.LINEAR_RING, gmlId));
        msg.append(": The element could not be assigned to an existing geometry object.");

        LOG.error(msg.toString());
        return false;
      }

      // step 3: find all corresponding interior rings
      psSelectInteriorLinearRing.setString(1, parentGmlId);
      rs = psSelectInteriorLinearRing.executeQuery();

      HashMap<String, Integer> innerRingMap = new HashMap<String, Integer>();
      int maxRingNo = 0;
      while (rs.next()) {
        String innerGmlId = rs.getString("GMLID");
        if (innerGmlId == null) innerGmlId = parentGmlId;

        int ringNo = rs.getInt("RING_NO");

        innerRingMap.put(innerGmlId, ringNo);
        if (ringNo > maxRingNo) maxRingNo = ringNo;
      }

      rs.close();

      // step 4: find corresponding texture coordinates
      List<String> texCoordList = new ArrayList<String>();
      String textureCoordinates = xlink.getTextureCoord();

      // reverse order of texture coordinates if necessary
      if (surfaceGeometryEntry.isReverse())
        textureCoordinates = reverseTextureCoordinates(textureCoordinates);

      texCoordList.add(0, textureCoordinates);
      for (int i = 0; i < maxRingNo; i++) texCoordList.add("");

      psSelectTexCoord.setString(1, xlink.getTexCoordListId());
      psSelectTexCoord.setString(2, xlink.getGmlId());
      rs = psSelectTexCoord.executeQuery();

      while (rs.next()) {
        String innerGmlId = rs.getString("GMLID");
        textureCoordinates = rs.getString("TEXTURE_COORDINATES");

        if (Util.isRemoteXlink(innerGmlId)) continue;

        // reverse order of texture coordinates if necessary
        if (surfaceGeometryEntry.isReverse())
          textureCoordinates = reverseTextureCoordinates(textureCoordinates);

        // replace leading #
        innerGmlId = innerGmlId.replaceAll("^#", "");
        if (innerRingMap.containsKey(innerGmlId))
          texCoordList.set(innerRingMap.get(innerGmlId), textureCoordinates);
      }

      // step 5: sanity check
      String texCoord = Util.collection2string(texCoordList, ";");
      if (texCoord.length() > 4000) {
        LOG.error(
            "Texture coordinates exceed 4000 characters for target geometry object '"
                + parentGmlId
                + "'.");
        return false;
      }

      if (texCoord.contains(";;") || texCoord.endsWith(";"))
        LOG.warn("Missing texture coordinates for target geometry object '" + parentGmlId + "'.");

      psTexCoordList.setLong(1, surfaceGeometryEntry.getId());
      psTexCoordList.setString(2, texCoord);
      psTexCoordList.setLong(3, xlink.getId());

      psTexCoordList.addBatch();
      if (++batchCounter == Internal.POSTGRESQL_MAX_BATCH_SIZE) executeBatch();

      if (xlink.getTexParamGmlId() != null) {
        // propagate xlink...
        resolverManager.propagateXlink(
            new DBXlinkTextureAssociation(
                xlink.getId(), surfaceGeometryEntry.getId(), xlink.getTexParamGmlId()));
      }

      return true;
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException sqlEx) {
          //
        }

        rs = null;
      }
    }
  }