/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.Position;

import ilog.views.IlvManager;
import ilog.views.IlvManagerLayer;
import ilog.views.IlvManagerView;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.accelerator.IlvDeleteSelectionAccelerator;
import ilog.views.accelerator.IlvFitToSizeAccelerator;
import ilog.views.accelerator.IlvIdentityAccelerator;
import ilog.views.accelerator.IlvZoomInAccelerator;
import ilog.views.accelerator.IlvZoomOutAccelerator;
import ilog.views.interactor.IlvManagerMagViewInteractor;
import ilog.views.interactor.IlvPanInteractor;
import ilog.views.maps.ExceptionHandler;
import ilog.views.maps.IlvAttributeInfoProperty;
import ilog.views.maps.IlvCoordinate;
import ilog.views.maps.IlvCoordinateSystemProperty;
import ilog.views.maps.IlvMapFeature;
import ilog.views.maps.IlvMapFeatureIterator;
import ilog.views.maps.IlvMapLayerTreeProperty;
import ilog.views.maps.IlvMapScaleLimiter;
import ilog.views.maps.IlvMapUtil;
import ilog.views.maps.beans.IlvJMapsManagerViewControlBar;
import ilog.views.maps.beans.IlvLayerTreePanel;
import ilog.views.maps.beans.IlvMapLayer;
import ilog.views.maps.beans.IlvMapLayerTreeModel;
import ilog.views.maps.datasource.IlvMapDataSourceModel;
import ilog.views.maps.datasource.IlvMapDataSourceProperty;
import ilog.views.maps.datasource.IlvSDODataSource;
import ilog.views.maps.datasource.IlvShapeDataSource;
import ilog.views.maps.export.IlvMapExportManager;
import ilog.views.maps.export.IlvSDOExporter;
import ilog.views.maps.format.IlvMapLoader;
import ilog.views.maps.format.oracle.IlvSDOConnection;
import ilog.views.maps.format.oracle.IlvSDOUtil;
import ilog.views.maps.format.oracle.objectmodel.IlvObjectSDOUtil;
import ilog.views.maps.format.oracle.objectmodel.IlvObjectSDOWriter;
import ilog.views.maps.srs.coordsys.IlvCoordinateSystem;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformation;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvThreadedActivityMonitor;
import ilog.views.swing.IlvThreadedActivityMonitorPanel;
import ilog.views.swing.IlvThreadedActivityMonitorProperty;
import ilog.views.tiling.IlvTiledLayer;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.data.IlvJDBCQueryUtil;

/**
 * This is the class that uses the <code>IlvObjectSDOFeatureIterator</code>,
 * <code>IlvObjectSDOWriter</code> and all the other classes allow to read a map
 * file, to save it into Oracle Spatial, to read and restore a (SDO) Layer, and
 * to load a Layer on demand.
 */

SuppressWarnings("serial")
public class SdoSample extends JFrame implements ExceptionHandler {
  // Bars
  IlvJMapsManagerViewControlBar controlBar;
  JToolBar demoToolBar;
  IlvThreadedActivityMonitorPanel monitorPanel;
  IlvThreadedActivityMonitor monitor;
  // Dialogs
  JFileChooser loadDialog;
  JFileChooser saveDialog;
  // Text handlers.
  JTextArea textBuffer = new JTextArea("Outputs: \n"); //$NON-NLS-1$
  Position end;
  JScrollPane scroll;
  // The connection to the database.
  IlvSDOConnection connection = null;
  // The Oracle SDO stuff.
  IlvMapFeatureIterator reader = null;
  int columnCount = 1;
  int rowCount = 1;
  // String keyColumnName = null;
  String geometryColumnName = null;
  // boolean lodEnabled = false;
  // boolean writeEnabled = false;
  IlvAttributeInfoProperty info = null;
  boolean saveAttributes = false;
  Long srID = null;
  // The name of the ordinates in the (USER_)SDO_GEOM_METADATA table.
  String xColName = "X"; //$NON-NLS-1$
  String yColName = "Y"; //$NON-NLS-1$
  // The layer name.
  String layerName;
  // The current file which is loaded or will be saved in Oracle.
  String currentFilename = ""; //$NON-NLS-1$
  // The number of objects loaded from a file.
  int fileObjectsCount = 0;
  // The debug view and the frame containing it.
  IlvManagerView debugView;
  // JFrame debugFrame;
  // JViews objects.
  IlvManager manager;
  IlvManagerView view;
  IlvMapLoader mapLoader;
  // The boundaries of the SDO layer.
  IlvCoordinate layerUpperLeftCorner = new IlvCoordinate(-360d, 90d);
  IlvCoordinate layerLowerRightCorner = new IlvCoordinate(360d, -90d);

  IlvLayerTreePanel layerTreePanel;
  JPanel accessoryPanel;
  JCheckBox lodCheckBox;
  JCheckBoxMenuItem debugViewCheckbox;
  private JTextField columnCountField;
  private JTextField rowCountField;
  private JLabel rowCountLabel;
  private JLabel columnCountLabel;

  IlvShapeDataSource shapeDataSource;

  /**
   * Constructs the sample and it's GUI.
   */
  SuppressWarnings("nls")
  public SdoSample() {
    setTitle("Oracle Spatial Sample");
    setIconImage(new ImageIcon(SdoSample.class.getResource("database.gif")).getImage());
    loadDialog = new JFileChooser(".");
    loadDialog.setAcceptAllFileFilterUsed(false);
    loadDialog.addChoosableFileFilter(new FileFilter() {
      Override
      public boolean accept(File f) {
        return f.isDirectory() || f.getName().toLowerCase(IlvMapLoader.getFileLocale()).endsWith(".shp");
      }

      Override
      public String getDescription() {
        return ("ESRI Shape (*.shp) File"); //$NON-NLS-1$
      }
    });
    saveDialog = new JFileChooser(".");
    setSize(900, 600);
    manager = new IlvManager();
    manager.setNamedProperty(new IlvCoordinateSystemProperty(IlvGeographicCoordinateSystem.WGS84));
    view = new IlvManagerView(manager);
    view.setKeepingAspectRatio(true);
    // limit the zoom to correct scales.
    IlvMapScaleLimiter limiter = new IlvMapScaleLimiter(1, (float) (1 / 1E9));
    limiter.setView(view);
    mapLoader = new IlvMapLoader(manager);
    lodCheckBox = new JCheckBox("Use tiling");
    lodCheckBox.setToolTipText("Enable load on demand (tiling) on the required layer");
    columnCountField = new JTextField(3);
    rowCountField = new JTextField(3);
    columnCountLabel = new JLabel("columns");
    rowCountLabel = new JLabel("rows");
    rowCountLabel.setToolTipText("Specify the number of columns of the load on demand grid");
    columnCountLabel.setToolTipText("Specify the number of rows of the load on demand grid");
    columnCountField.setEnabled(false);
    rowCountField.setEnabled(false);
    columnCountLabel.setEnabled(false);
    rowCountLabel.setEnabled(false);
    lodCheckBox.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        if (lodCheckBox.isSelected()) {
          columnCountField.setEnabled(true);
          rowCountField.setEnabled(true);
          columnCountLabel.setEnabled(true);
          rowCountLabel.setEnabled(true);
        } else {
          columnCountField.setEnabled(false);
          rowCountField.setEnabled(false);
          columnCountLabel.setEnabled(false);
          rowCountLabel.setEnabled(false);
        }
      }
    });
    initButtons();
    createMenuBar();
    initAccelerators();
    getContentPane().add(new IlvJScrollManagerView(view), BorderLayout.CENTER);
    textBuffer.setRows(6);
    textBuffer.setEditable(false);
    end = textBuffer.getDocument().getEndPosition();
    scroll = new JScrollPane(textBuffer);
    layerTreePanel = new IlvLayerTreePanel();
    layerTreePanel.setPreferredSize(new Dimension(220, 300));
    layerTreePanel.setView(view);
    accessoryPanel = new JPanel(new BorderLayout());
    accessoryPanel.add(layerTreePanel, BorderLayout.CENTER);
    getContentPane().add(accessoryPanel, BorderLayout.LINE_START);
    getContentPane().add(scroll, BorderLayout.PAGE_END);
  }

  /**
   * Builds the buttons and the bars.
   */
  SuppressWarnings("nls")
  public void initButtons() {
    getContentPane().setLayout(new BorderLayout());
    JPanel p = new JPanel();
    p.setLayout(new FlowLayout(FlowLayout.LEFT));
    JButton loadButton = new JButton(new ImageIcon(SdoSample.class.getResource("load_shape.gif"))); //$NON-NLS-1$
    JButton saveDbButton = new JButton(new ImageIcon(SdoSample.class.getResource("save_db.gif"))); //$NON-NLS-1$
    JButton loadDbButton = new JButton(new ImageIcon(SdoSample.class.getResource("load_db.gif"))); //$NON-NLS-1$
    monitor = IlvThreadedActivityMonitorProperty.GetThreadedActivityMonitor(manager);
    monitorPanel = new IlvThreadedActivityMonitorPanel(monitor);
    monitorPanel.setOpaque(false);
    controlBar = new IlvJMapsManagerViewControlBar();
    controlBar.setFloatable(false);
    controlBar.setBorderPainted(false);
    controlBar.setOpaque(false);
    demoToolBar = new JToolBar();
    demoToolBar.setFloatable(false);
    demoToolBar.setBorderPainted(false);
    demoToolBar.setOpaque(false);
    p.add(demoToolBar);
    p.add(controlBar);
    controlBar.setView(view);
    loadButton.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        loadShape();
      }
    });
    saveDbButton.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        saveToDb();
      }
    });
    loadDbButton.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        loadFromDB();
      }
    });
    p.add(controlBar);
    loadButton.setToolTipText("Open shape file");
    saveDbButton.setToolTipText("Save layer to dataBase");
    loadDbButton.setToolTipText("Load layer from database");
    demoToolBar.add(loadButton);
    demoToolBar.addSeparator();

    demoToolBar.add(loadDbButton);
    demoToolBar.add(saveDbButton);
    controlBar.addSeparator();
    p.add(monitorPanel);

    loadButton.setPreferredSize(new Dimension(26, 26));
    saveDbButton.setPreferredSize(new Dimension(26, 26));
    loadDbButton.setPreferredSize(new Dimension(26, 26));
    ((IlvPanInteractor) controlBar.getPanInteractor()).setOpaqueMove(true);
    getContentPane().add(p, BorderLayout.PAGE_START);
  }

  /**
   * Creates the menu bar.
   */
  SuppressWarnings("nls")
  public void createMenuBar() {
    JMenuItem voidItem;
    JMenu demoMenu = new JMenu("Demo");
    // JMenu dataBase = new JMenu("Data Base");
    // Reset all.
    demoMenu.add(voidItem = new JMenuItem("New map"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("new.gif"))); //$NON-NLS-1$
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        reset();
      }
    });
    demoMenu.addSeparator();

    // Load shape file.
    demoMenu.add(voidItem = new JMenuItem("Open shape file"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("load_shape.gif")));
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        loadShape();
      }
    });
    // Load DB.
    demoMenu.add(voidItem = new JMenuItem("Load layer from dataBase"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("load_db.gif")));
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        loadFromDB();
      }
    });
    // Save DB.
    demoMenu.add(voidItem = new JMenuItem("Save layer to dataBase"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("save_db.gif")));
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        saveToDb();
      }
    });
    demoMenu.addSeparator();
    demoMenu.add(voidItem = new JMenuItem("Disconnect"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("disconnect.gif")));
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        closeConnection();
      }
    });

    demoMenu.addSeparator();
    debugViewCheckbox = new JCheckBoxMenuItem("Show debug view", true);
    demoMenu.add(debugViewCheckbox);
    debugViewCheckbox.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        if (debugView != null) {
          if (debugViewCheckbox.isSelected()) {
            addDebugView();
          } else {
            removeDebugView();
          }
        }
      }
    });
    demoMenu.addSeparator();
    // Quit.
    demoMenu.add(voidItem = new JMenuItem("Quit"));
    voidItem.setIcon(new ImageIcon(SdoSample.class.getResource("exit.gif")));
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        exitSample();
      }
    });

    JMenuBar menubar = new JMenuBar();
    menubar.add(demoMenu);
    // menubar.add(dataBase);
    setJMenuBar(menubar);
  }

  /**
   * Sets the accelerators for the manager.
   */
  public void initAccelerators() {
    manager.addAccelerator(new IlvZoomInAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvZoomOutAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_U, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvIdentityAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_I, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvFitToSizeAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_F, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvDeleteSelectionAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0));
  }

  /**
   * This method handles messaging in the <code>TextArea</code> object.
   * 
   * @param mes
   */
  public void appendMessage(String mes) {
    textBuffer.setCaretPosition(end.getOffset() - 1);
    textBuffer.append(mes + "\n"); //$NON-NLS-1$
    Rectangle tmp = getBounds();
    scroll.paintImmediately(0, 0, tmp.width, tmp.height);
  }

  /**
   * This method handles errors in the <code>TextArea</code> object.
   * 
   * @param error
   */
  public void reportError(String error) {
    appendMessage("** " + error);//$NON-NLS-1$
  }

  /**
   * This method handles exceptions in the <code>TextArea</code> object.
   * 
   * @param e
   */
  public void reportError(Throwable e) {
    if (e.getMessage() != null)
      appendMessage("** " + e);//$NON-NLS-1$
    else
      appendMessage("No message for this exception.");//$NON-NLS-1$
    // e.printStackTrace();
  }

  private void removeDebugView() {
    if (debugView != null) {
      debugView.popInteractor();
      accessoryPanel.remove(debugView);
      accessoryPanel.invalidate();
      getContentPane().validate();
    }
  }

  /**
   * Resets the debug view of the SDO layer and the manager for load on demand.
   */
  public void reset() {
    removeDebugView();
    debugView = null;
    IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
    dataSourceModel.clear();
    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(view.getManager());
    ltm.clear();

    int nb = manager.getLayersCount();
    for (int i = 1; i < nb; i++)
      manager.removeLayer(1, false);
    manager.deleteAll(true);
    view.repaint();
  }

  /**
   * Exists the sample. It closes the connection before.
   */
  public void exitSample() {
    closeConnection();
    System.exit(0);
  }

  /**
   * 
   *
   */
  private void closeConnection() {
    try {
      if (connection != null && connection.getConnection() != null)
        connection.getConnection().close();
      connection = null;
      appendMessage("Connection closed"); //$NON-NLS-1$
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  /**
   * Loads a shape file.
   */
  SuppressWarnings("nls")
  public void loadShape() {
    try {

      loadDialog.setDialogType(JFileChooser.OPEN_DIALOG);
      loadDialog.setDialogTitle("Load a shape file");
      loadDialog.setSelectedFile(null);
      loadDialog.showOpenDialog(this);
      final File fi = loadDialog.getSelectedFile();
      if (fi != null && fi.getName() != null) {
        reset();
        currentFilename = fi.getPath();
        appendMessage("Loading " + currentFilename);
        shapeDataSource = new IlvShapeDataSource(currentFilename);
        shapeDataSource.setCoordinateSystem(IlvGeographicCoordinateSystem.WGS84);
        final IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
        dataSourceModel.insert(shapeDataSource);
        // dataSourceModel.start();
        Thread loader = new Thread() {
          Override
          public void run() {
            dataSourceModel.start();
            IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(view.getManager());
            IlvMapLayer shapeLayer = shapeDataSource.getInsertionLayer();
            ltm.addChild(null, shapeLayer);
            shapeLayer.setName("ESRI layer (" + fi.getName() + ")");
            view.fitTransformerToContent();
            view.repaint();
          }
        };
        loader.start();
      }
    } catch (Exception e) {
      reportError(e);
    } finally {
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      Toolkit.getDefaultToolkit().sync();
    }
    view.fitTransformerToContent();
    manager.reDraw();
  }

  ////////////////////////
  // Data Base Callbacks
  ////////////////////////

  /**
   * Executes the query passed as argument and returns the result set.
   * 
   * @param query
   *          SQL query string.
   * @return the result set
   * @throws SQLException
   */
  private ResultSet executeQuery(String query) throws SQLException {
    // no risk of SQL injection - only used in getAvailableGid
    if (connection == null || connection.getConnection() == null) {
      reportError("No Connection available!"); //$NON-NLS-1$
      return null;
    }
    Statement st = connection.getConnection().createStatement();
    ResultSet res = st.executeQuery(query);
    return res;
  }

  /**
   * Creates and connects the reader and the writer to the DB.
   */
  SuppressWarnings("nls")
  public void connect() {
    try {
      // The connection panel implementing the
      // <code>IlvSDOConnectionFactory</code>.
      ConnectionPanel conpan = new ConnectionPanel(this);
      Rectangle bbox = getBounds();
      Rectangle bbox2 = conpan.getBounds();
      conpan.setLocation((int) ((bbox.x + bbox.width) / 2f - (bbox2.x + bbox2.width) / 2f),
          (int) ((bbox.y + bbox.height) / 2f - (bbox2.y + bbox2.height) / 2f));
      // Changing the default connection factory:
      IlvSDOConnection.SetConnectionFactory(conpan);
      // Creating a new IlvSDOConnection.
      connection = new IlvSDOConnection(null, null, null);
      // Connecting it.
      IlvSDOConnection.GetConnectionFactory().makeConnection(connection);
      if (connection.getConnection() == null) {
        appendMessage("Not connected.");
        return;
      }
      appendMessage("Connected.");
      // Testing the existence of SDO package.
      if (IlvSDOUtil.CheckSdoExistence(connection.getConnection()))
        appendMessage("Found Oracle SDO Package ...");
      else {
        reportError("** No Oracle SDO Package found.**");
        reportError("** Please contact your DB Administrator. **");
        reportError(" Disconnecting ... ");
        closeConnection();
        return;
      }
    } catch (Exception e) {
      reportError(e);
    }
  }

  /**
   * Checks if the user is connected to the DB.
   * 
   * @return true if connection succeeded.
   */
  public boolean checkConnection() {
    if (connection == null || connection.getConnection() == null)
      connect();
    if (connection.getConnection() == null)
      return false;
    return true;
  }

  /**
   * Returns an available SDO_GID which has to be a unique value.
   * 
   * @param sdoLayerName
   * @return gid number.
   */
  public int getAvailableGid(String sdoLayerName) {
    try {
      // check against SQL injection.
      IlvJDBCQueryUtil.validateTableName(sdoLayerName);
      // Extract the greater SDO_GID from the DB.
      ResultSet result = executeQuery("select SDO_GID from " + sdoLayerName + "_SDOGEOM ORDER BY 1 DESC\n"); //$NON-NLS-1$ //$NON-NLS-2$
      result.next();
      int gid = result.getInt(1);
      result.getStatement().close();
      result.close();
      if (!result.wasNull())
        return gid + 1;
    } catch (SQLException e) {
      return 0;
    }
    return 0;
  }

  /**
   * Pops a dialog proposing the available SDO layers. If
   * <code>multipleChoice</code> is set to <code>true</codE>, then the user can
   * choose more than one layer (by using the shift key + mouse click).
   * 
   * @param writingToDB
   * @return whether the user has chosen something or nothing.
   */
  SuppressWarnings("nls")
  public boolean selectLayers(boolean writingToDB) {
    if (!checkConnection())
      return false;
    layerName = null;
    try {
      String[] layersList = null;
      layersList = IlvObjectSDOUtil.GetAllLayers(connection.getConnection(), connection.getUser(), true);
      final JList<String> dataList = new JList<String>(layersList);
      // custom renderer to display icons
      DefaultListCellRenderer customRenderer = new DefaultListCellRenderer() {
        private Icon vectorIcon = new ImageIcon(SdoSample.class.getResource("vector.gif")); //$NON-NLS-1$
        private Icon rasterIcon = new ImageIcon(SdoSample.class.getResource("raster.gif")); //$NON-NLS-1$

        Override
        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
            boolean hasFocus) {
          JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, hasFocus);
          if (value instanceof String && ((String) value).contains("GEORASTER")) { //$NON-NLS-1$
            label.setIcon(rasterIcon);
          } else {
            label.setIcon(vectorIcon);
          }
          return label;
        }
      };
      dataList.setCellRenderer(customRenderer);
      dataList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      dataList.addListSelectionListener(new ListSelectionListener() {
        boolean lodValue = lodCheckBox.isSelected();
        String rrowCount = rowCountField.getText();
        String colCount = columnCountField.getText();
        boolean needToRestore = false;

        Override
        public void valueChanged(ListSelectionEvent e) {
          if (e.getValueIsAdjusting()) {
            return;
          }
          if (dataList.getSelectedValue() != null) {
            String s = dataList.getSelectedValue().toString();
            if (s.contains("GEORASTER")) { //$NON-NLS-1$
              if (!needToRestore) {
                lodValue = lodCheckBox.isSelected();
                rrowCount = rowCountField.getText();
                colCount = columnCountField.getText();
              }
              lodCheckBox.setEnabled(false);
              lodCheckBox.setSelected(false);
              rowCountField.setEnabled(false);
              rowCountLabel.setEnabled(false);
              columnCountField.setEnabled(false);
              columnCountLabel.setEnabled(false);
              needToRestore = true;
            } else {
              lodCheckBox.setEnabled(true);
              rowCountField.setEnabled(lodCheckBox.isSelected());
              rowCountLabel.setEnabled(lodCheckBox.isSelected());
              columnCountLabel.setEnabled(lodCheckBox.isSelected());
              columnCountField.setEnabled(lodCheckBox.isSelected());
              if (needToRestore) {
                needToRestore = false;
                lodCheckBox.setSelected(lodValue);
                rowCountField.setText(rrowCount);
                columnCountField.setText(colCount);
              }

            }
          }
        }

      });
      final JScrollPane scrollPane = new JScrollPane(dataList);
      String title = "Choose the SDO layer";
      final JDialog layerDialog = new JDialog(this, title, true);
      final JPanel pan1 = new JPanel();
      final JPanel pan2 = new JPanel();
      final JPanel pan4 = new JPanel(new BorderLayout());
      final JButton ok = new JButton("OK");
      final JButton cancel = new JButton("Cancel");

      final JTextField sridField = new JTextField(6);
      final JLabel sridLabel = new JLabel("SRID");
      sridLabel.setToolTipText("Specify the coordinate system of the layer to save to Oracle DB");
      final JPanel pan3 = new JPanel();
      pan3.setLayout(new FlowLayout());
      pan3.setBorder(new EtchedBorder());
      if (writingToDB) {
        pan3.add(sridLabel);
        pan3.add(sridField);
      } else {
        pan3.add(lodCheckBox);
        pan3.add(rowCountLabel);
        pan3.add(rowCountField);
        pan3.add(columnCountLabel);
        pan3.add(columnCountField);
      }
      pan1.setLayout(new BorderLayout());
      pan1.add(scrollPane, "Center"); //$NON-NLS-1$
      pan2.add(ok);
      pan2.add(cancel);

      ok.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent e) {
          Object value = dataList.getSelectedValue();
          if (value == null) {
            reportError("Please select a layer !");
            return;
          }
          layerName = value.toString();
          geometryColumnName = GetAfterDotString(value.toString());

          try {
            srID = Long.valueOf(sridField.getText().trim());
          } catch (Exception ee) {
            if (sridField.isEnabled() && sridField.getText().trim().length() > 0) {
              reportError("Please give a correct SRID !");
              return;
            }
          }
          try {
            rowCount = Integer.valueOf(rowCountField.getText().trim()).intValue();
            columnCount = Integer.valueOf(columnCountField.getText().trim()).intValue();
            layerDialog.setVisible(false);
          } catch (Exception ee) {
            rowCount = 1;
            columnCount = 1;
            if (columnCountField.isEnabled()) {
              reportError("Please fill in tiling parameters"); //$NON-NLS-1$
              return;
            }
            layerDialog.setVisible(false);
          }
        }
      });
      cancel.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent e) {
          layerDialog.setVisible(false);
        }
      });
      layerDialog.getContentPane().setLayout(new BorderLayout());
      pan4.add(pan3, BorderLayout.CENTER);
      pan4.add(pan2, BorderLayout.PAGE_END);
      layerDialog.getContentPane().add(pan1, BorderLayout.CENTER);
      layerDialog.getContentPane().add(pan4, BorderLayout.PAGE_END);
      Rectangle bbox = getBounds();
      layerDialog.pack();
      layerDialog.setLocation((int) bbox.getCenterX() - layerDialog.getWidth() / 2,
          (int) bbox.getCenterY() - layerDialog.getHeight() / 2);

      layerDialog.setVisible(true);
    } catch (SQLException e) {
      reportError(e);
    }
    if (layerName != null) {
      return true;
    }
    return false;
  }

  /**
   * Writes the feature iterator passed as argument into the database. Moreover,
   * it updates the progress bar.
   * 
   * @param rreader
   * @param theWriter
   * @return the feature index.
   */
  SuppressWarnings("nls")
  public int writeFeatureIterator(IlvMapFeatureIterator rreader, Object theWriter) {
    int i = 0;
    if (rreader == null)
      return 0;
    try {
      IlvMapFeature feature = rreader.getNextFeature();
      if (feature != null && feature.getAttributeInfo() != null) {
        int res = JOptionPane.showConfirmDialog(this, "Do you want to save the attributes of the map features?",
            "Save attributes ?", JOptionPane.YES_NO_OPTION);
        if (res == JOptionPane.YES_OPTION)
          saveAttributes = true;
        else
          saveAttributes = false;
      }
      // Loop over the features.
      while (feature != null) {
        try {
          ((IlvObjectSDOWriter) theWriter).writeFeature(feature, saveAttributes, srID);
          i++;
        } catch (Exception e) {
          reportError(e);
        }
        int progress = (int) (100 * i / (double) fileObjectsCount);
        monitor.updateActivityProgress(SdoSample.this, progress, "Saving to DB...");
        if (SwingUtilities.isEventDispatchThread()) {
          Rectangle bounds = monitorPanel.getBounds();
          monitorPanel.paintImmediately(0, 0, (int) bounds.getWidth(), (int) bounds.getHeight());
        }
      }
    } catch (Exception e) {
      reportError(e);
    }
    monitor.unregisterThreadedActivity(SdoSample.this);
    // Don't forget to commit the inserte rows.
    try {
      connection.getConnection().commit();
      ((IlvObjectSDOWriter) theWriter).close(0.00001, srID);
    } catch (SQLException e) {
      reportError(e);
    }
    return i;
  }

  /**
   * Saves the current reader into Spatial.
   */
  SuppressWarnings("nls")
  public void saveToDb() {
    if (!checkConnection())
      return;
    if (shapeDataSource == null) {
      // No Map file has been loaded so that no current reader is available.
      JOptionPane.showMessageDialog(this, "No current filename to save!", "Can't save in Data Base",
          JOptionPane.ERROR_MESSAGE);
      return;
    }
    int res = JOptionPane.showConfirmDialog(this,
        "Saving '" + shapeDataSource.getInsertionLayer().getName() + "' layer in the Data Base ?", "Confirm saving",
        JOptionPane.YES_NO_OPTION);
    if (res == JOptionPane.NO_OPTION)
      return;

    IlvMapExportManager exportManager = new IlvMapExportManager();
    IlvSDOExporter sdoExporter = new IlvSDOExporter();
    sdoExporter.setConnection(connection);
    // sdoExporter.
    exportManager.setVectorialExporter(sdoExporter);
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    Toolkit.getDefaultToolkit().sync();
    exportManager.exportMapLayers(new IlvMapLayer[] { shapeDataSource.getInsertionLayer() });
    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    Toolkit.getDefaultToolkit().sync();
  }

  /**
   * Loads a Spatial layer on demand.
   */
  public void loadFromDB() {
    // lodEnabled = true;
    if (!checkConnection())
      return;
    selectLayers(false);
    if (layerName == null)
      return;
    // reset();
    createDataSources(lodCheckBox.isSelected());

    // show debug view (if tiled)
    if (lodCheckBox.isSelected()) {
      debugView = new IlvManagerView(manager);
      debugView.setKeepingAspectRatio(true);
      debugView.setPreferredSize(new Dimension(230, 170));
      addDebugView();
      int layerCount = manager.getLayersCount();
      for (int i = 0; i < layerCount; i++) {
        IlvManagerLayer currentLayer = manager.getManagerLayer(i);
        if (currentLayer instanceof IlvTiledLayer) {
          ((IlvTiledLayer) currentLayer).setDebugView(debugView);
        }
      }
      IlvManagerMagViewInteractor mag = new IlvManagerMagViewInteractor(view, false);
      debugView.pushInteractor(mag);
      mag.setAutoZooming(true);
      mag.setAutoTranslating(true);
    }

  }

  private void addDebugView() {
    if (debugView != null) {
      accessoryPanel.add(debugView, BorderLayout.PAGE_START);
      accessoryPanel.invalidate();
      getContentPane().validate();
    }
  }

  /**
   * 
   * @param isTiled
   * @param rows
   * @param columns
   */
  private void createDataSources(final boolean isTiled) {

    final IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(view.getManager());
    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(view.getManager());

    IlvSDODataSource ds = null;
    IlvRect tempArea = null;
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

    try {
      ds = new IlvSDODataSource(connection, true, layerName);
      IlvRect extent = null;
      if (isTiled && !layerName.contains("GEORASTER")) {//$NON-NLS-1$
        try {
          extent = IlvObjectSDOUtil.GetLayerExtent(connection.getConnection(), GetBeforeDotString(layerName),
              geometryColumnName, xColName, yColName, true);
        } catch (SQLException e) {/* ignore */
        }

        tempArea = extent;
        ds.setTilingParameters(true, rowCount, columnCount);
        ds.setMultiThreaded(true);
      }
      dataSourceModel.insert(ds);
      IlvMapLayer mapLayer = ds.getInsertionLayer();
      mapLayer.setName("Oracle Layer");//$NON-NLS-1$
      ltm.addChild(null, mapLayer);
    } catch (MalformedURLException e) {
      e.printStackTrace();
    }

    final IlvRect areaToFit = tempArea;
    final IlvSDODataSource fds = ds;
    final IlvCoordinateSystem cs = IlvCoordinateSystemProperty.GetCoordinateSystem(manager);

    // start datasource model in a separate thread
    Thread loadThread = new Thread() {
      Override
      public void run() {
        try {
          fds.start();
        } catch (Throwable e) {
          reportError(e);
          tcleanup();
          return;
        }
        if (!isTiled || layerName.contains("GEORASTER")) { //$NON-NLS-1$
          if (dataSourceModel.getDataSources().length == 1) {
            // fit only on first layer loaded
            view.fitTransformerToContent();
          }
        } else {
          // fit area on center tile
          double width = areaToFit.getWidth() / columnCount;
          double height = areaToFit.getHeight() / rowCount;
          double x = areaToFit.getX() + width * (columnCount / 2);
          double y = areaToFit.getY() + height * (rowCount / 2);
          double margin = 0.2d;

          IlvCoordinateSystem SDOSystem = fds.getCoordinateSystem();
          IlvCoordinateTransformation tr = IlvCoordinateTransformation.CreateTransformation(SDOSystem, cs);

          IlvRect projectedRect = IlvMapUtil.computeTransformedBounds(tr.getTransform(), x, y, x + width, y + height,
              true, 10);
          // adjust ratio
          double ratio = view.getWidth() / (double) view.getHeight();
          double newWidth = projectedRect.getWidth();
          double newHeight = projectedRect.getHeight();
          if ((newWidth / newHeight) < ratio) {
            newHeight = newWidth / ratio;
          } else {
            newWidth = newHeight * ratio;
          }

          /*
           * IlvRect adjustedArea = new IlvRect(projectedRect.x +
           * projectedRect.width*margin , -(projectedRect.y +
           * projectedRect.height) + projectedRect.height * margin,
           * projectedRect.width * (1-2*margin), projectedRect.height*
           * (1-2*margin));
           */

          IlvRect adjustedArea2 = new IlvRect();
          adjustedArea2.setFrameFromCenter(projectedRect.getCenterX(), -projectedRect.getCenterY(),
              projectedRect.getCenterX() + newWidth * (1 - margin) / 2,
              -projectedRect.getCenterY() + newHeight * (1 - margin) / 2);

          IlvTransformer t = IlvTransformer.computeTransformer(adjustedArea2,
              new IlvRect(view.getX(), view.getY(), view.getWidth(), view.getHeight()), null);
          view.setTransformer(t);
        }
        tcleanup();
      }

      private void tcleanup() {
        view.repaint();
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        Toolkit.getDefaultToolkit().sync();
      }
    };

    if (!isTiled) {
      loadThread.start();
    } else {
      loadThread.run();
    }
  }

  private static final String GetBeforeDotString(String str) {
    int dotPosition = str.indexOf('.');
    if (dotPosition < 1)
      return str;
    return str.substring(0, dotPosition);
  }

  private static final String GetAfterDotString(String str) {
    int dotPosition = str.indexOf('.');
    if (dotPosition < 0)
      return null;
    return str.substring(dotPosition + 1);
  }

  /**
   * Instantiates the sample.
   * 
   * @param args
   */
  public static void main(String args[]) {
    // This sample uses JViews Maps features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Maps Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Maps_Deployment);

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        final SdoSample demo = new SdoSample();
        demo.addWindowListener(new WindowAdapter() {
          Override
          public void windowClosing(WindowEvent e) {
            demo.exitSample();
          }
        });
        demo.setVisible(true);
      }
    });
  }

  Override
  public void handle(Exception e) throws Exception {
    reportError(e);
  }
}