/*
 * AnalyzerUtilities.java
 *
 * Copyright (c) 2003, Oracle Corporation. All rights reserved. Oracle grants
 * you ("Licensee") a non-exclusive, royalty-free, worldwide license to use,
 * modify and redistribute this software in source and binary code form, provided
 * that (i) this copyright notice and license appear on all copies of the
 * software; (ii) Licensee does not utilize the software in a manner disparaging
 * to Oracle; and (iii) Licensee does not utilize the software in a manner that
 * violates the terms of any Licensee agreement with Oracle.
 */
package samples.javaclient.analyzer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Toolkit;

import java.lang.Double;

import java.util.Hashtable;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;

import oracle.bali.ewt.busyBar.BusyBar;
import oracle.bali.ewt.dialog.JEWTDialog;
import oracle.bali.ewt.util.WindowUtils;

import oracle.dss.calculation.client.calcBuilder.CalcBuilderStepStorage;
import oracle.dss.crosstab.Crosstab;
import oracle.dss.crosstab.CrosstabPrinter;
import oracle.dss.crosstab.UICrosstab;
import oracle.dss.dataSource.client.QueryClient;
import oracle.dss.dataSource.client.QueryManager;
import oracle.dss.dataSource.common.QueryException;
import oracle.dss.dataView.Dataview;
import oracle.dss.dataView.ViewPrinter;
import oracle.dss.dataView.gui.PrintDialog;
import oracle.dss.datautil.QueryAccess;
import oracle.dss.datautil.QueryContext;
import oracle.dss.graph.Graph;
import oracle.dss.graph.GraphPrinter;
import oracle.dss.graph.UIGraph;
import oracle.dss.graph.gui.GraphLayout;
import oracle.dss.gridView.UIGridView;
import oracle.dss.gridView.gui.ExportPanel;
import oracle.dss.metadataManager.client.MetadataManager;
import oracle.dss.metadataManager.common.MDMeasure;
import oracle.dss.metadataManager.common.MDRoot;
import oracle.dss.metadataManager.common.MetadataManagerException;
import oracle.dss.persistence.gui.PersistenceObjectChooser;
import oracle.dss.queryBuilder.QueryBuilder;
import oracle.dss.selection.Selection;
import oracle.dss.selection.SelectionException;
import oracle.dss.selection.step.InvalidStepArgException;
import oracle.dss.selection.step.MemberStep;
import oracle.dss.util.BIException;
import oracle.dss.util.LayerMetadataMap;
import oracle.dss.util.LayoutContext;
import oracle.dss.util.gui.BIExceptionDialog;
import oracle.dss.util.gui.layout.CrosstabLayout;
import oracle.dss.util.gui.layout.Layout;

/**
 * Utility class.  Provides methods that are used across the application.
 * Methods in this class show how to:
 * <ul>
 * <li>Instantiate and run a CalcBuilder: {@link #runCalcBuilder}</li>
 * <li>Instantiate and run a QueryBuilder: {@link #runQueryBuilder}.</li>
 * <li>Add a measure to the measure dimension in a query: {@link #updateMeasures}</li>
 * <li>Use the PersistenceObjectChooser as an Open dialog: {@link #showOpenDialog}</li>
 * <li>Use the PersistenceObjectChooser as a Save As dialog: {@link #showSaveAsDialog}</li>
 * <li>Load a view from the BI Beans Catalog: {@link #openView}</li>
 * <li>Save a view to the BI Beans Catalog: {@link #saveView}</li>
 * </ol>
 * Other methods display other dialogs and manage error messages.
 */
public class AnalyzerUtilities  {
  /**
   * Main application class.
   */
  private Analyzer m_analyzer;    // Main application frame
  /**
   * Connection for the application.
   */
  private AnalyzerConnection m_connection;  
  /**
   * Status message: "Status: Not connected".
   */
  private JLabel m_statusText = new JLabel("Status: Not connected");  // the status 
  /**
   * Error message: "Unable to connect."
   */
  public static final String MSG_BAD_UNKNOWN = "Unable to connect.";
  /**
   * Error message: "Invalid User ID or Password."
   */
  public static final String MSG_BAD_USER_CREDENTIALS = "Invalid User ID or Password.";
  /**
   * Error message: "Invalid host, port or SID."
   */
  public static final String MSG_BAD_CONNECTION = "Invalid host, port or SID.";
  /**
   * Error message: "Invalid Catalog User ID or Password."
   */
  public static final String MSG_BAD_CATALOG_CREDENTIALS =
                       "Invalid Catalog User ID or Password.  Please ensure the user has been authorized to use the Catalog.";
  /**
   * Error message: "The crosstab or graph contains an invalid object(s).
   * Ensure that you have authorized access to the measures, calculations and 
   * saved selections that are used by the view."
   */
  public static final String MSG_INVALID_OBJECT =
         "The crosstab or graph contains an invalid object(s). Ensure that you have authorized access to the measures, calculations and saved selections that are used by the view.";
  /**
   * Error message: "The specified file-system directory does not exist."
   */
  public static final String MSG_INVALID_ROOT = "The specified file-system directory does not exist.";      
  /**
   * Error BIB-16612.
   * @see #getErrorText
   * @see #MSG_BAD_USER_CREDENTIALS
   */
  public static final String ERR_BAD_USER_CREDENTIALS = "16612";
  /**
   * Error BIB-16613.
   * @see #getErrorText
   * @see #MSG_BAD_CONNECTION
   */
  public static final String ERR_BAD_CONNECTION = "16613";
  /**
   * Error BIB-01017.
   * @see #getErrorText
   * @see #MSG_BAD_CATALOG_CREDENTIALS
   */
  public static final String ERR_BAD_CATALOG_CREDENTIALS = "01017";
  /**
   * Error BIB-14736. 
   * @see AnalyzerConnection#getCatalogConnection
   */
  public static final String ERR_INVALID_BIUSER = "14736";
  /**
   * Error BIB-14736. 
   * @see AnalyzerConnection#getCatalogConnection
   */
  public static final String ERR_INVALID_CATALOG_USER = "14122";  
  /**
   * Error BIB-14122.
   * @see #getErrorText
   * @see #MSG_INVALID_CATALOG_USER
   */   
  public static final String ERR_INVALID_OBJECT = "10310";
  /**
   * Error BIB-14820.
   * @see #getErrorText
   * @see #MSG_INVALID_ROOT
   */
  public static final String ERR_INVALID_ROOT = "14820";

  /**
   * Constructor.
   * 
   * @param analyzer The main analyzer application class.
   */
  public AnalyzerUtilities(Analyzer analyzer) {
    m_analyzer = analyzer;

    // Cache the connection
    if (analyzer != null)
      m_connection = analyzer.getConnection();
  }

  /**
   * Runs the CalcBuilder wizard.  
   * If the CalcBuilder runs successfully, then this method inserts the newly 
   * created custom measure into the view that is in focus.
   * 
   * @see #updateMeasures
   */
  public void runCalcBuilder () {
    try {
      // make sure there is an active dataview
      if (m_analyzer.getActiveWorksheet().getActiveDataview() == null || m_connection.getQueryManager() == null)
        return;

      // Create our CalcBuilder
      QueryContext    qc = (QueryContext) m_analyzer.getActiveWorksheet().getActiveDataview().getDataSource();
      MetadataManager mdm = m_connection.getMetadataManager();

      // CalcBuilderStepStorage does everything for creating a new Calc
      // and adding it to the MeatadataManager
      CalcBuilderStepStorage calcBuilderStepStorage =
        new CalcBuilderStepStorage(null, /*component*/                                                         
          null, /*mdObject*/
          qc,   /*queryContext*/
          mdm/*metadataManager*/);
                                                         
      calcBuilderStepStorage.setParentFrame(m_analyzer);

      // Invoke the CalcBuilder -- if the user press the OK button to create
      // a new calc the calcBuliderStepStorage will do all the work for saving
      // and making the calc appear in the Query Builder.
      MDMeasure mdMeasure = (MDMeasure) calcBuilderStepStorage.invoke();

      // add the measure to the view's query
      updateMeasures(mdMeasure);
    } 
    catch (Throwable t) {
      String strMsg = "Unable to display the Calculation Builder.";
      showErrorMessage(strMsg, t);
      
      return;
    }
  }

  /**
   * Adds a new custom measure to the list of measures for the active view.
   * This method is called by the runCalcBuilder method.  
   * 
   * @param MDMeasure The measure to add to a presentation.
   * 
   * @throws InvalidStepArgException If there is a problem in the Step.
   * @throws SelectionException If the selection for the measure dimension
   *                            cannot be created.
   * @throws QueryException  If there is a problem fetching data after the
   *                         measure dimension is updated.
   * @throws MetadataManagerException If there is a problem in the MetadataManager
   * 
   * @see #runCalcBuilder
   */
  private void updateMeasures(MDMeasure mdMeasure)
    throws InvalidStepArgException, SelectionException,
      QueryException, MetadataManagerException {

    // If there is not an active dataview, then return
    if (m_analyzer.getActiveWorksheet().getActiveDataview() == null || mdMeasure == null)
      return;

    // Get the query from the active dataview  
    QueryClient viewQuery = (QueryClient) m_analyzer.getActiveWorksheet().getActiveDataview().getDataSource();
                                 
    // Create a queryAccess in order to update query
    QueryAccess queryAccess = viewQuery.createQueryAccess();

    // Retrieve the name of the measure dimension
    String strMeasureDimension =
      queryAccess.getMeasureDimension(LayerMetadataMap.LAYER_METADATA_NAME);

    // Retrieve the selection associated with the measure dimension
    Selection selection =
      queryAccess.getSelection(strMeasureDimension);

    // Release our Query Access
    if (queryAccess != null)
      queryAccess.release();

      // Verify that the selection is non-null
    if (selection != null) {
    
      // Retrieve the members associated with the step
      // Measure dimensions can only have member steps
      MemberStep step = (MemberStep) selection.getStep(0);
      Vector members = step.getMembers();

      // Only add measure if it currently does not exist
      String strMeasureID = mdMeasure.getUniqueID();

      if ((strMeasureID != null) && !members.contains(strMeasureID)) {
        // Add our new custom measure
        members.addElement(mdMeasure.getUniqueID());
        step.setMembers(members);

        // Set the action to be SELECT to start fresh.
        // This call might throw InvalidStepArgException
        step.setAction(oracle.dss.selection.step.Step.SELECT);

        // Remove all steps from this selection.
        selection.removeAllSteps();

        // Add our newly created step which includes our new custom
        // measure
        selection.addStep(step);
      }

      if (strMeasureID != null)
        // Attempt to update the query with the new measure selection
        // This is the call that might throw all the exceptions
        viewQuery.applySelection(selection);
    }
  }

  /**
   * Analyzes an exception and attempts to find an appropriate message to display
   * to a user.
   *
   * @param t The exception that was thrown. 
   * @return The message text.
   */
  public String getErrorText(Throwable t) {

    String message = null;
    
    if (t instanceof BIException) {
      String fullMessage = t.toString();

      // Attempt to scan the full message text for typical failures
      if (fullMessage.indexOf(ERR_BAD_CONNECTION) > 0)
        message = MSG_BAD_CONNECTION;
      else if (fullMessage.indexOf(ERR_BAD_USER_CREDENTIALS) > 0)
        message = MSG_BAD_USER_CREDENTIALS;
      else if (fullMessage.indexOf(ERR_BAD_CATALOG_CREDENTIALS) > 0)
        message = MSG_BAD_CATALOG_CREDENTIALS;
      else if (fullMessage.indexOf(ERR_INVALID_CATALOG_USER) > 0)
        message = MSG_BAD_CATALOG_CREDENTIALS;
      else if (fullMessage.indexOf(ERR_INVALID_OBJECT) > 0)
        message = MSG_INVALID_OBJECT;
        
    }
    else 
      message = MSG_BAD_UNKNOWN;
      
    return message;
    
  }

  /**
   * Displays an error message dialog.  Shows the stack trace.
   * @param strMsg The message to display in the error dialog.
   * @param t The Exception that was thrown. 
   */
  void showErrorMessage(String strMsg, Throwable t) {
    String strTitle = null;
    if (m_analyzer == null)
      strTitle = "Error";
    else 
      strTitle = m_analyzer.getTitle();
      
    if (t != null) {
      strMsg += "\n\n" + t.getMessage();

      if ( t instanceof BIException ) {
        Throwable cause = ((BIException)t).getBIRootCause();
        strMsg += "\n" + cause.getLocalizedMessage();
      }

      BIExceptionDialog biExceptionDialog = new BIExceptionDialog(m_analyzer,
                                                                  t,
                                                                  strTitle,
                                                                  strMsg,
                                                                  null);

      Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
      biExceptionDialog.setSize((int)(d.width * 0.8), (int)(d.height * 0.4));
      biExceptionDialog.display();
      biExceptionDialog.dispose();
      biExceptionDialog = null;

      t.printStackTrace();
    }
    else {
      JOptionPane.showMessageDialog(m_analyzer, strMsg, strTitle,
                                      JOptionPane.ERROR_MESSAGE);
    }    
  }

  /**
   * Runs the QueryBuilder.  
   * This method:
   * <ol><li>Instantiates a new QueryBuilder. </li>
   * <li>Gets the query from the active view and sets it on the QueryBuilder.</li>
   * <li>Runs the QueryBuilder, which updates the query in the
   * view.</li></ol>
   */
  public void runQueryBuilder() {
    try {
      // Make sure there is an active view
      if (m_analyzer.getActiveWorksheet().getActiveDataview() == null || m_connection.getQueryManager() == null)
        return;

      QueryBuilder queryBuilder = new QueryBuilder(m_analyzer);

      // Find the current query
      Dataview dv = m_analyzer.getActiveWorksheet().getActiveDataview();
      QueryClient query = (QueryClient) dv.getDataSource();

      // Invoke the QueryBuilder in re-entrant mode
      queryBuilder.setMode(QueryBuilder.TABBED);
      queryBuilder.setMetadataManager(m_connection.getMetadataManager());
      queryBuilder.setQuery((QueryContext) query);
      queryBuilder.setVisible(true);

      Layout layout = null;

      // Display the Right type of Layout panel for different
      // view type
      if (dv instanceof UIGraph) {
        // Set Graph into the GraphLayout panel
        layout = new GraphLayout();
        ((GraphLayout) layout).setGraph((Graph) dv);
      } 
      else if (dv instanceof UICrosstab) {
        // Setting the Paging Control Attributes to the layout,
        // so that the paging control of the crosstab will get updated
        layout = new CrosstabLayout();
        ((CrosstabLayout) layout).setPagingControlAttributes(dv);
      } 

      // Setting Layout context to the Layout panel,
      // and set the Layout to QueryBuilder
      if (layout != null) {
        layout.setLayoutContext((LayoutContext) query);
        queryBuilder.setViewLayoutPanel(layout);
      }

      queryBuilder.run();
    } 
    catch (Throwable t) {
      String strMsg = "Unable to display the Query Builder.";
      showErrorMessage(strMsg, t);
    }
  }

  /**
   * Displays the Graph Format dialog.  
   * The Graph Format dialog allows users to set properties of the graph.
   * This method gets the active view and makes sure it is a graph.
   * Then it instantiates a GraphFormatDialog and runs it.
   * <p>
   * The Graph Format dialog contains different panels, depending on the
   * graph type of the graph.
   * 
   * @see GraphFormatDialog
   */
   void showGraphFormatDialog() {
   // Determine the active dataview
    Dataview dv = m_analyzer.getActiveWorksheet().getActiveDataview();

    if (dv == null) {
      String strMsg = "There is no graph selected.";

      showErrorMessage(strMsg, null);
      return;
    }

    // If the view is a graph, display the dialog
    if (dv instanceof Graph) {
      GraphFormatDialog gfd = new GraphFormatDialog((Graph) dv);
      gfd.setCenterOver((Frame) m_analyzer);

      // If the user hits okay, then apply the format to the graph
      if (gfd.runDialog()) {
        gfd.applyFormat();

        // Flag the worksheet as dirty
        m_analyzer.getActiveWorksheet().setDirty(true);
      }

      gfd.dispose();
    }       
   }

  /**
   * Displays the Graph Type dialog.
   * The Graph Type dialog allows users to change the graph type.
   * This method gets the active view and checks to make sure it is a graph.
   * Then this method instantiates a GraphTypeDialog and runs it.
   * <p>
   * The Graph Type dialog is separate from the Graph Format dialog because
   * the Graph Format dialog has different panels, depending on the graph type.
   * 
   * @see GraphTypeDialog
   */
   void showGraphTypeDialog() {
   // Determine the active dataview
    Dataview dv = m_analyzer.getActiveWorksheet().getActiveDataview();

    if (dv == null) {
      String strMsg = "There is no graph selected.";

      showErrorMessage(strMsg, null);
      return;
    }

    // If the view is a graph, display the dialog
    if (dv instanceof Graph) {
      GraphTypeDialog gfd = new GraphTypeDialog((Graph) dv);
      gfd.setCenterOver((Frame) m_analyzer);

      // If the user hits okay, then apply the graph type to the graph
      if (gfd.runDialog()) {
        gfd.applyGraphType();

        // Flag the worksheet as dirty
        m_analyzer.getActiveWorksheet().setDirty(true);

        // update the toolbar
        m_analyzer.getToolBar().setDataView(m_analyzer.getActiveWorksheet().getActiveDataview());
      }

      gfd.dispose();
    }       
   }

   
  /**
   * Displays the Export dialog.
   * The Export dialog allows users to export data to a spreadsheet.
   * <p>
   * This method gets the active view and checks to make sure it is a 
   * crosstab.
   * Then it constructs a dialog that contains the standard BI Beans
   * ExportPanel, as well as OK and Cancel buttons.
   * Then this method shows the dialog.
   * Data from the crosstab is exported to a file in the format that 
   * the user selects in the dialog.
   */
  void showExportDialog() {
    // Determine the active dataview
    Dataview dv = m_analyzer.getActiveWorksheet().getActiveDataview();
    
    if (dv == null) {
      String strMsg = "Cannot Export because there is no active view.";

      showErrorMessage(strMsg, null);
      return;
    }

    // Export is only supported for Table and Crosstab
    if (dv instanceof Crosstab) {

      // JEWTDialog class makes it really easy to show a dialog and
      // display buttons in it. If we used a JDialog we would have to write
      // a lot more code and implement the displaying of buttons ourselves.
      Frame       parent = (Frame) m_analyzer;
      JEWTDialog  jewtDialog = new JEWTDialog(parent,
          "Export",
          JEWTDialog.BUTTON_OK |
          JEWTDialog.BUTTON_CANCEL);
      ExportPanel exportPanel = new ExportPanel(parent,
          (UIGridView) dv);

      jewtDialog.setCenterOver(parent);
      jewtDialog.setContent(exportPanel);

      if (jewtDialog.runDialog()) {
        // user hit ok...so do the actual export
        exportPanel.apply();
      }

      jewtDialog.dispose();
      jewtDialog = null;
    }    
  }

  /**
   * Displays the Print dialog.
   * This method gets the active view.
   * It then constructs a standard BI Beans PrintDialog and displays it.
   * The view is printed according to options that the user sets in the
   * dialog.
   */
  void showPrintDialog() {
    // Get the application's active dataview
    Dataview    dv = m_analyzer.getActiveWorksheet().getActiveDataview();
  
    if (dv == null) {
      String strMsg = "Cannot Print because there is no active view.";

      showErrorMessage(strMsg, null);
      return;
    }

    ViewPrinter printer = null;

    if (dv instanceof Graph) {
      printer = new GraphPrinter((Graph) dv);
    } 
    else if (dv instanceof Crosstab) {
      printer = new CrosstabPrinter((Crosstab) dv);
    }

    if (printer != null) {
      PrintDialog dialog = new PrintDialog(m_analyzer, printer);

      dialog.show();
      dialog.dispose();
      dialog = null;
    }    
  }

  /**
   * Displays the Open dialog.
   * This method:
   * <ol>
   * <li>Gets the root folder from the connection object for this
   * application.</li>
   * <li>Constructs a standard BI Beans PersistenceObjectChooser and uses
   * a BIPresentationFilter to filter the types of objects that appear in
   * the PersistenceObjectChooser.</li>
   * <li>Displays the PersistenceObjectChooser.</li>
   * <li>calls the openView method to load the object that the user specifies in the
   * PersistenceObjectChooser.</li></ol>
   * 
   * @see #openView
   * @see BIPresentationFilter
   */
  public void showOpenDialog() {
    try {
      // Get the root folder for this connection
      MDRoot root = m_connection.getRootFolder();
         
      PersistenceObjectChooser chooser = new PersistenceObjectChooser(root);

      // Apply filter - only show presentations
      BIPresentationFilter       filter = new BIPresentationFilter();
    
      chooser.addPersistenceObjectFilter(filter);
      chooser.removePersistenceObjectFilter(PersistenceObjectChooser.ALL_TYPES);
      chooser.setDefaultPersistenceObjectFilter(filter.getDisplayText());

      int nRetVal = chooser.showOpenDialog(m_analyzer);

      if (nRetVal != PersistenceObjectChooser.OK_OPTION)
        return;

      String strName = chooser.getSelectedObjectPathName();
  
      // getSelectedObjectPathName returns the full path name
      // including the folders. Note that this is a JNDI path and
      // is relative to the root that was specified in the configuration
      // file.

     openView(strName, false, 0,0,400,400);

    } 
    catch (Throwable t) {
    }
  }

  /**
   * Loads a dataview in a separate thread. 
   * This method instantiates an OpenViewWorker object for the thread.
   * Using a separate thread frees the user interface.
   * This method is called from the showOpenDialog method.
   * @param strName Name of the presentation to open, such as "/My Reports/Sales Trends".
   * @param isDirty <code>true</code> if the view has changes that need to be saved,
   *                <code>false</code> if it does not.
   * @param x The horizontal position for the view.
   * @param y The vertical position for the view.
   * @param width The width of the view.
   * @param height The height of the view.
   * 
   * @see #showOpenDialog
   * @see OpenViewWorker
   */
  public void openView(String strName, boolean isDirty,
                       int x, int y, int width, int height) {
    
    // Open the dataview on a separate thread        
    OpenViewWorker worker = new OpenViewWorker(strName, isDirty, x, y, 
                                width, height, 
                                getBusyBar("Opening " + strName + "..."));
    worker.start();

    return;

  }  


  /**
   * Gets the busy bar for this application.
   * The busy bar notifies the user that the application is busy opening up a 
   * dataview.
   * 
   * @param message The message to display in the busy bar.
   * 
   * @return The busy bar.
   */  
  public JWindow getBusyBar(String message) {
    // show status dialog
    JWindow statusDialog = new JWindow(m_analyzer);
    BusyBar busyBar = new BusyBar();
    busyBar.setText(message);

    JPanel panel = new JPanel(new BorderLayout());
    statusDialog.getContentPane().add(panel);
    panel.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
    
    panel.add(busyBar, BorderLayout.CENTER);
    busyBar.start();
    busyBar.setRunning(true);
    statusDialog.pack();

    WindowUtils.centerWindow(statusDialog, m_analyzer.getDesktop());  
    statusDialog.setVisible(true);

    return statusDialog;
  }

  /**
   * Displays the Save As dialog.
   * This method:
   * <ol><li>Gets the active view.</li>
   * <li>Gets the root folder from the connection for this application.</li>
   * <li>Constructs a standard BI Beans PersistenceObjectChooser
   * to use as a Save As dialog.</li>
   * <li>Sets a BIPresentation Filter object, to filter the types of objects that 
   * appear in the PersistenceObjectChooser.</li>
   * <li>Displays the PersistenceObjectChooser as a Save As dialog.</li>
   * <li>After the user closes the dialog, calls the saveView method
   * to save the view.</li>
   * <li>Updates the path for the active view.</li>
   * </ol>
   * 
   * @see #saveView
   */
  void showSaveAsDialog() {
    // Check for an active dataview
    if (m_analyzer.getActiveWorksheet().getActiveDataview() == null) {
      String strMsg = "Cannot save as because there is no active view.";

      showErrorMessage(strMsg, null);
      return;
    }

    try {
      MDRoot mdRoot = m_connection.getRootFolder();

      PersistenceObjectChooser chooser = new PersistenceObjectChooser(mdRoot);
      BIPresentationFilter       filter = new BIPresentationFilter();

      chooser.addPersistenceObjectFilter(filter);
      chooser.removePersistenceObjectFilter(PersistenceObjectChooser.ALL_TYPES);
      chooser.setDefaultPersistenceObjectFilter(filter.getDisplayText());

      int nRetVal = chooser.showSaveDialog(m_analyzer);

      // If the user didn't hit OK, return
      if (nRetVal != PersistenceObjectChooser.OK_OPTION)
        return;

      // Save the worksheet into the user-specified path and object name
      String  strName = chooser.getSelectedObjectPathName();

      saveView(strName, m_analyzer.getActiveWorksheet().getActiveDataview());
      m_analyzer.getActiveWorksheet().setActiveDataviewPath(strName);
    } 
    catch (Throwable t) {
      String strMsg = "Unable to display the Save As dialog.";
      showErrorMessage(strMsg, t);
    }
  }    
  // helpers for opening, saving, and doing a save as

  /**
   * Saves a presentation.
   * This method gets the MetadataManager and the root folder from the 
   * connection for this application.
   * Then it saves the specified Dataview under the specified name.
   * 
   * @param strName The name under which to save the object. Pass the full
   *                path name from the root folder.
   * @param dv    The view to save.
   * 
   * @return <code>true</code> if the save is successful,
   *         <code>false</code> if it is not.
   */
  boolean saveView(String strName, Dataview dv) {
    try {
      MetadataManager mdm = m_connection.getMetadataManager();

      // calling rebind will work even if the object specified by the path
      // name specified in strName wasn't saved before. All the sub-folders
      // must exist though
      MDRoot root = m_connection.getRootFolder();

      root.rebind(strName, dv);
    } 
    catch (Throwable t) {
      String strMsg = "Unable to save view: " + strName;
      showErrorMessage(strMsg, t);

      return false;
    }

    return true;
  }

  /**
   * Inner class for opening a view on a separate thread.
   * The SwingWorker class, which this class extends, is available from the 
   * Sun web site.
   */
  private class OpenViewWorker extends SwingWorker {
    private String m_strName;
    private int m_x, m_y, m_width, m_height;
    private Dataview m_dataview;
    private boolean m_dirty;
    private JWindow m_busyBar;
    
    /**
     * Constructor.
     * 
     * @param objectName  The full path name of the object to open.
     * @param isDirty  <code>true</code> if the object has changed since 
     *                 the last save,
     *                 <code>false</code> if it has not.
     * @param x   The horizontal position for the view.
     * @param y   The vertial position for the view.
     * @param width The width of the view.
     * @param height The height of the view.
     * @param busyBar The busy bar in which to display a message.
     */
    public OpenViewWorker (String objectName, boolean isDirty, 
              int x, int y, int width, int height, JWindow busyBar) {
      m_strName = objectName;
      m_x = x;
      m_y = y;
      m_width = width;
      m_height = height;
      m_dirty = isDirty;
      m_busyBar = busyBar;
      
    }

    /**
     * Gets the view that this class opens.
     * This is the instantiation of the view that is being loaded from the
     * Catalog.
     * 
     * @return The view that is being opened.
     */
    public Dataview getDataview() {
      return m_dataview;
    }

    /**
     * Cleans up after the thread.
     * This method hides the busy bar and adds a new worksheet to the 
     * application.
     * 
     * @see Analyzer#addWorksheet
     */
    public void finished() {
      m_busyBar.hide();
      m_busyBar.dispose();

      if (m_dataview != null)
        m_analyzer.addWorksheet(m_strName, m_dataview, m_dirty, m_x, m_y, m_width, m_height);
    }

    /**
     * Instantiates the Dataview that is being loaded.
     * Objects are stored as XML definitions in the BI Beans Catalog.
     * This method loads the definition from the Catalog and instantiates a 
     * Dataview object from the definition.
     * The Dataview basically is a new Dataview that has the properties from
     * the XML set on it.
     * 
     * @return The object that is created from the loaded definition.
     */
    public Object construct() {
      m_dataview = null;

      try {
        MetadataManager mdm = m_connection.getMetadataManager();

        Hashtable    env = new Hashtable();
        QueryManager qm = m_connection.getQueryManager();

        // qm can be null which implies that we are running with a config
        // file that doesn't specify a connection to an OLAP source. We
        // will use a static data source in that case.

        // Via this mechanism we can tell the Persistence Manager about the
        // Query Manager it should use to evaluate the query in the Presentation
        // being loaded.
        if (qm != null)
          env.put(QueryClient.QUERY_MANAGER, qm);

        // The reason for using the MetadataManager for loading
        // a view is that the MetadataManager will automatically
        // load any calculations that might be referenced by the
        // query. The calculations need to be present before the
        // query can be evaluated.

        MDRoot root = m_connection.getRootFolder();
        m_dataview = (Dataview) root.lookup(m_strName, env);
      } 
      catch (Throwable t) {
        String strMsg = "Unable to open view: " + m_strName + ".  ";
        strMsg = strMsg + getErrorText(t);
        showErrorMessage(strMsg, t);
        return "FALSE";
      }

      return "TRUE";
    }   
  }
}
  
