/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © 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 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;

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;




/**
 * 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.
 */

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(){
                        public boolean accept(File f) {
                                return f.isDirectory() || f.getName().toLowerCase(IlvMapLoader.getFileLocale()).endsWith(".shp");
                        }
                        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(){
                        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() {
      public void actionPerformed(ActionEvent e) {
        loadShape();
      }
    });
    saveDbButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        saveToDb();
      }
    });
    loadDbButton.addActionListener(new ActionListener() {
      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() {
      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() {
      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() {
      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() {
      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() {
      public void actionPerformed(ActionEvent e) {
        closeConnection();
      }
    });
    
    demoMenu.addSeparator();
    debugViewCheckbox = new JCheckBoxMenuItem("Show debug view",true);
    demoMenu.add(debugViewCheckbox);
    debugViewCheckbox.addActionListener(new ActionListener() {
      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() {
      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() {
                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 dataList = new JList(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$
                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;

                                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() {
        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() {
        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() {
        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    
                        float width = (float)areaToFit.getWidth() / columnCount;
            float height = (float)areaToFit.getHeight() / rowCount;
            float x = (float)areaToFit.getX() + width * (columnCount / 2);
            float y = (float)areaToFit.getY() + height * (rowCount / 2);
            float margin = 0.2f;
            
                                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 Rogue Wave JViews Maps Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(
        IlvProductUtil.JViews_Maps_Deployment);

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

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