/*
 * BIMetadata.java
 *
 * Copyright © 1994-2004, Oracle. 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;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;

import java.util.Enumeration;
import java.util.Vector;

import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeSelectionModel;

import oracle.bali.ewt.util.WindowUtils;

import oracle.dss.metadataManager.client.MetadataManager;
import oracle.dss.metadataManager.common.MDAttribute;
import oracle.dss.metadataManager.common.MDDimension;
import oracle.dss.metadataManager.common.MDFolder;
import oracle.dss.metadataManager.common.MDHierarchy;
import oracle.dss.metadataManager.common.MDLevel;
import oracle.dss.metadataManager.common.MDMeasure;
import oracle.dss.metadataManager.common.MDObject;
import oracle.dss.metadataManager.common.MDRoot;
import oracle.dss.metadataManager.common.MetadataManagerException;
import oracle.dss.metadataManager.common.MM;

/**
 * This class demonstrates how to retrieve metadata from MetadataManager.
 * BIMetadata displays the metadata in a JTree where
 * each node represents an MDObject.  This sample also shows how a user can
 * filter the JTree to retrieve a specific type of MDObject, such as MDMeasure,
 * MDDimension and so on.  The sample also displays the MDObject properties.
 */
public class BIMetadata extends JFrame {
  private JRadioButton m_attribButton;         // Show attributes
  private ButtonGroup m_buttonGrp;             // ButtonGroup to filter MDObject
  private JRadioButton m_dimButton;            // Show dimension
  private JRadioButton m_fldButton;            // Show folders
  private JRadioButton m_hierButton;           // Show hierarchy
  private JRadioButton m_levButton;            // Show level
  private JList m_list;                        // List of selected MDObjects
  private DefaultListModel m_listModel;        // List model for JList
  private JRadioButton m_measButton;           // Show measures
  private MetadataManager m_metadataManager;   // Reference to metadataManager
  private JMenuItem m_metadataMenu;            // MetadataManager menu item
  private JRadioButton m_noneButton;           // To deselect the radio buttons
  private JTextArea m_propertyText;            // TextArea to show properties
  private JTree m_tree;                        // JTree for metadataManager
  private BIFrame m_frame;
  private JPanel m_panel;

  private static final String s_newLine = System.getProperty("line.separator");

  /**
   * Constructor
   */
  public BIMetadata(BIFrame parentFrame, String title) {
    super(title);
    m_frame = parentFrame;
    init();
  }

  /**
   * Initialize the GUI
   */
  protected void init() {
    
    // get the menu bar
    JMenuBar menuBar = new JMenuBar();
    
    m_panel = new JPanel();
    m_panel.setLayout(new BorderLayout());
    
    // put all of these into the frame
    setJMenuBar(menuBar);
    getContentPane().removeAll();
    getContentPane().add(m_panel, BorderLayout.CENTER);
  
    // add a listener to listen for the window closing event
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        cleanUp();
      }
    });

    // show the metadata browser    
    createMetadataTree();
    createRadioButtonPanel();
    createSplitPanel();
    
  }

  /**
   * This method cleans up before returning to the main menu
   */
  protected void cleanUp() {
    
    // Free resources associated with the menu items
    if(m_tree != null) {
      m_tree.removeAll();
    }
    m_panel.removeAll();
    dispose();
    }

  /**
   * Set child nodes of the parent node.  This method is called when the tree
   * expansion event is fired.
   */
  public void addChildren(DefaultMutableTreeNode node) {
    // retrieve children of MDObject contained in the node
    Vector childVec = getChildren((MDObject)node.getUserObject());

    // If children are available, set them to the parent node.
    if(!childVec.isEmpty()) {
      Enumeration children = childVec.elements();
      while(children.hasMoreElements()) {
        MDObject child = (MDObject)children.nextElement();
        Vector gChildVec = getChildren(child);
        if(gChildVec.isEmpty()) {
          node.add(new DefaultMutableTreeNode(child, false));
        }
        else {
          DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(child,
            true);
          Enumeration gChildren = gChildVec.elements();
          while(gChildren.hasMoreElements()) {
            childNode.add(new DefaultMutableTreeNode(gChildren.nextElement()));
          }
          node.add(childNode);
        }
      }
    }
  }

  /**
   * Retrieve metadata from MetadataManager and create first two levels of
   * metadataManager JTree.  Also set tree-expansion and tree-selection
   * listeners
   */
  public void createMetadataTree() {
    // Retrieve metadataManager
    m_metadataManager = m_frame.getMetadataManager();
    // Get root folder from metadataManager and create a JTree node from it.
    MDRoot mdRoot = m_metadataManager.getMDRoot();
    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(mdRoot, true);
    // Add the first two levels to the root node.
    addChildren(rootNode);

    // Create a JTree and set the root node to it.
    m_tree = new JTree(rootNode);
    // Set a customized icon renderer to the JTree
    m_tree.setCellRenderer(new IconRenderer());
    m_tree.putClientProperty("JTree.lineStyle", "Angled");
    m_tree.getSelectionModel().setSelectionMode(
      TreeSelectionModel.SINGLE_TREE_SELECTION);
    JScrollPane scrollPane = new JScrollPane(m_tree);

    // Set tree listener
    m_tree.addTreeExpansionListener(new TreeExpansionListener() {
      public void treeExpanded(TreeExpansionEvent e) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.getPath()
          .getLastPathComponent();
        if(node != null) {
          Enumeration enum = node.children();
          while(enum.hasMoreElements()) {
            DefaultMutableTreeNode childNode =
              (DefaultMutableTreeNode)enum.nextElement();
            if(childNode.getChildCount() == 0) {
              addChildren(childNode);
            }
          }
        }
      }
      public void treeCollapsed(TreeExpansionEvent e) {
        // Ignore the treeCollapsed event
      }
    });

    // Set tree selection listener
    m_tree.addTreeSelectionListener(new TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent e) {
        // Clear the JList
        m_listModel.removeAllElements();
        // Clear the properties text area
        m_propertyText.setText("");
        // Clear the ButtonGroup and disable radio buttons
        m_noneButton.setSelected(true);
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.getPath()
          .getLastPathComponent();
        disableRadioButtons(node);
        // Show properties of MDObject
        showProperties((MDObject)node.getUserObject());
      }
    });

    m_panel.add(scrollPane, BorderLayout.CENTER);
  }

  /**
   * Create radio button panel to filter tree nodes
   */
  public void createRadioButtonPanel() {
    JPanel buttonPanel = new JPanel(new GridLayout(6,1));
    m_buttonGrp = new ButtonGroup();

    // Create folder JButton and set listener for it
    m_fldButton = new JRadioButton("Folder");
    m_fldButton.setEnabled(false);
    m_buttonGrp.add(m_fldButton);
    buttonPanel.add(m_fldButton);
    m_fldButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // Create measure JButton and set listener for it
    m_measButton = new JRadioButton("Measure");
    m_measButton.setEnabled(false);
    m_buttonGrp.add(m_measButton);
    buttonPanel.add(m_measButton);
    m_measButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // Create dimension JButton and set listener for it
    m_dimButton = new JRadioButton("Dimension");
    m_dimButton.setEnabled(false);
    m_buttonGrp.add(m_dimButton);
    buttonPanel.add(m_dimButton);
    m_dimButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // Create hierarchy JButton and set listener for it
    m_hierButton = new JRadioButton("Hierarchy");
    m_hierButton.setEnabled(false);
    m_buttonGrp.add(m_hierButton);
    buttonPanel.add(m_hierButton);
    m_hierButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // Create level JButton and set listener for it
    m_levButton = new JRadioButton("Level");
    m_levButton.setEnabled(false);
    m_buttonGrp.add(m_levButton);
    buttonPanel.add(m_levButton);
    m_levButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // Create attribute JButton and set listener for it
    m_attribButton = new JRadioButton("Attribute");
    m_attribButton.setEnabled(false);
    m_buttonGrp.add(m_attribButton);
    buttonPanel.add(m_attribButton);
    m_attribButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        handleRadioButtonEvent(event);
      }
    });

    // To give the appearence that no radio button is selected, create "none"
    // button so that it can be set as selected when no radio button is
    // applicable.
    m_noneButton = new JRadioButton("none");
    m_buttonGrp.add(m_noneButton);

    m_panel.add(buttonPanel, BorderLayout.EAST);
  }

  /**
   * Create the split pane to show the MDObject properties on left side and
   * filtered children for the selected node on right side.  Children are
   * filtered by selecting one of the enabled radio-buttons.
   */
  public void createSplitPanel() {
    // Create a new JList
    m_listModel = new DefaultListModel();
    m_list = new JList(m_listModel);
    JScrollPane listScrollPane = new JScrollPane(m_list);

    // Set listener on JList
    m_list.addListSelectionListener(new ListSelectionListener() {
      public void valueChanged(ListSelectionEvent e) {
        m_propertyText.setText("");
        MDObject mdObj = (MDObject)m_list.getSelectedValue();
        showProperties(mdObj);
      }
    });

    // Create new JTextArea to show MDObject properties
    m_propertyText = new JTextArea();
    JScrollPane textScrollPane = new JScrollPane(m_propertyText);

    // Create a split pane with JTextArea on left and JList on right.
    JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
      textScrollPane, listScrollPane);
    splitPane.setOneTouchExpandable(true);
    splitPane.setDividerLocation(380);
    // Provide minimum sizes for the two components in the split pane
    Dimension minimumSize = new Dimension(100, 50);
    listScrollPane.setMinimumSize(minimumSize);
    textScrollPane.setMinimumSize(minimumSize);

    m_panel.add(splitPane, BorderLayout.SOUTH);
  }

  /**
   * Depending on the MDObject type contained in the selected node, disable
   * radio buttons.  For example, if folder is selected, only folder,
   * measure, and dimension radio buttons should be enabled.
   */
  public void disableRadioButtons(DefaultMutableTreeNode node) {
    // Retrieve the MDObject from the JTree node.
    MDObject mdObject = (MDObject)node.getUserObject();
    String objType = mdObject.getObjectType();

    // Disable the radio buttons
    m_fldButton.setEnabled(false);
    m_measButton.setEnabled(false);
    m_dimButton.setEnabled(false);
    m_hierButton.setEnabled(false);
    m_levButton.setEnabled(false);
    m_attribButton.setEnabled(false);

    // If MDObject type is FOLDER, enable sub-folder, measure and dimension
    // radio buttons.
    if(objType.equals(MM.FOLDER)) {
      m_fldButton.setEnabled(true);
      m_measButton.setEnabled(true);
      m_dimButton.setEnabled(true);
    }
    // If MDObject type is measure, enable folder and dimension radio buttons.
    else if(objType.equals(MM.MEASURE)) {
      m_fldButton.setEnabled(true);
      m_dimButton.setEnabled(true);
    }
    // If MDObject type is dimension, enable measure, hierarchy and
    // attribute radio buttons
    else if(objType.equals(MM.DIMENSION)) {
      m_measButton.setEnabled(true);
      m_hierButton.setEnabled(true);
      m_attribButton.setEnabled(true);
    }
    // If MDObject type is hierarchy, enable level radio button
    else if(objType.equals(MM.HIERARCHY)) {
      m_levButton.setEnabled(true);
    }
    // If MDObject type is level, enable hierarchy and attribute radio buttons
    else if(objType.equals(MM.LEVEL)) {
      m_hierButton.setEnabled(true);
      m_attribButton.setEnabled(true);
    }
    // If MDObject type is attribute, enable dimension and level radio buttons
    else if(objType.equals(MM.ATTRIBUTE)) {
      m_dimButton.setEnabled(true);
      m_levButton.setEnabled(true);
    }
  }

  /**
   * This method retrieves folders, dimensions or hierarchies of the specified
   * mdObject.
   */
  public Vector getChildren(MDObject mdObject) {
    // retrieve objectType of MDObject
    String objectType = mdObject.getObjectType();
    Vector childrenVec = new Vector();

    try {
      if(objectType != null) {
        // If mdObject is a folder then retrieve sub-folders, measures and
        // dimensions
        if(objectType.equals(MM.FOLDER)) {
          MDFolder folder = (MDFolder)mdObject;
          MDObject[] children = folder.getFolders();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
          children = folder.getMeasures();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
          children = folder.getDimensions();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
          // Remove measureDimension
          if(folder instanceof MDRoot) {
            childrenVec.removeElement(m_metadataManager
              .getMeasureDimension(null));
          }
        }
        // If mdObject is a dimension, retrieve hierarchies and attributes
        else if(objectType.equals(MM.DIMENSION)) {
          MDDimension dimension = (MDDimension)mdObject;
          MDObject[] children = dimension.getHierarchies();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
          children = dimension.getAttributes();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
        }
        // If mdObject is a hierarchy, retrieve levels
        else if(objectType.equals(MM.HIERARCHY)) {
          MDHierarchy hierarchy = (MDHierarchy)mdObject;
          MDObject[] children = hierarchy.getLevels();
          for(int i = 0; children != null && i < children.length; i++) {
            childrenVec.addElement(children[i]);
          }
        }
      }
    }
    catch(MetadataManagerException mme) {
      m_frame.showExceptionDialog(this, mme);
    }

    return childrenVec;
  }

  /**
   * Filter the children of the selected tree node and show them in the split
   * pane.
   */
  public void handleRadioButtonEvent(ActionEvent event) {
    // clear Jlist
    m_listModel.removeAllElements();

    // Retrieve selected tree node
    DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_tree
      .getLastSelectedPathComponent();
    // Retrieve selected radio button
    JRadioButton radioButton = (JRadioButton)event.getSource();
    // Retrieve selected button text
    String buttonText = radioButton.getText();
    if(node != null) {
      MDObject mdObject = (MDObject)node.getUserObject();
      MDObject[] mdObjects = null;
      // Depending on the selected node and selected radio button, filter
      // JTree children nodes and show them in JList.
      try {
        if(buttonText.equals("Folder")) {
          if(mdObject.getObjectType().equals(MM.MEASURE)) {
            mdObjects = ((MDMeasure)mdObject).getFolders();
          }
          else if(mdObject.getObjectType().equals(MM.DIMENSION)) {
            mdObjects = ((MDDimension)mdObject).getFolders();
          }
          else if(mdObject.getObjectType().equals(MM.FOLDER)) {
            mdObjects = ((MDFolder)mdObject).getFolders();
          }
        }
        if(buttonText.equals("Measure")) {
          if(mdObject.getObjectType().equals(MM.FOLDER)) {
            mdObjects = ((MDFolder)mdObject).getMeasures();
          }
          else if(mdObject.getObjectType().equals(MM.DIMENSION)) {
            mdObjects = ((MDDimension)mdObject).getMeasures();
          }
        }
        else if(buttonText.equals("Dimension")) {
          if(mdObject.getObjectType().equals(MM.FOLDER)) {
            mdObjects = ((MDFolder)mdObject).getDimensions();
          }
          else if(mdObject.getObjectType().equals(MM.MEASURE)) {
            mdObjects = ((MDMeasure)mdObject).getDimensions();
          }
          else if(mdObject.getObjectType().equals(MM.HIERARCHY)) {
            mdObjects = ((MDHierarchy)mdObject).getDimensions();
          }
        }
        else if(buttonText.equals("Hierarchy")) {
          if(mdObject.getObjectType().equals(MM.DIMENSION)) {
            mdObjects = ((MDDimension)mdObject).getHierarchies();
          }
          if(mdObject.getObjectType().equals(MM.LEVEL)) {
            mdObjects = new MDObject[]{((MDLevel)mdObject).getHierarchy()};
          }
        }
        else if(buttonText.equals("Level")) {
          if(mdObject.getObjectType().equals(MM.HIERARCHY)) {
            mdObjects = ((MDHierarchy)mdObject).getLevels();
          }
          if(mdObject.getObjectType().equals(MM.ATTRIBUTE)) {
            mdObjects = ((MDAttribute)mdObject).getLevels();
          }
        }
        else if(buttonText.equals("Attribute")) {
          if(mdObject.getObjectType().equals(MM.DIMENSION)) {
            mdObjects = ((MDDimension)mdObject).getAttributes();
          }
          if(mdObject.getObjectType().equals(MM.LEVEL)) {
            mdObjects = ((MDLevel)mdObject).getAttributes();
          }
        }
      }
      catch(Exception excep) {
        m_frame.showExceptionDialog(this, excep);
      }
      // Add MDObjects to the JList
      for(int i = 0; mdObjects != null && i < mdObjects.length; i++) {
        m_listModel.addElement(mdObjects[i]);
      }
    }
  }

  /**
   * Show the properties defined on selected mdObject.
   */
  public void showProperties(MDObject mdObject) {
    m_propertyText.append("Name = " + mdObject.getName() + s_newLine);
    m_propertyText.append("UniqueID = " + mdObject.getUniqueID() + s_newLine);
    m_propertyText.append("ObjectType = " + mdObject.getObjectType()
      + s_newLine);
    m_propertyText.append("DriverType = "
      + mdObject.getDriverTypes().toString() + s_newLine);
    m_propertyText.append("Path = " + mdObject.getPath() + s_newLine);
    m_propertyText.append("ShortLabel = " + mdObject.getShortLabel()
      + s_newLine);
    m_propertyText.append("LongLabel = " + mdObject.getLongLabel()
      + s_newLine);
    m_propertyText.append("Description = " + mdObject.getDescription()
      + s_newLine);

    // Scroll to the top-left corner
    m_propertyText.setCaretPosition(0);
  }

  /**
   * Inner class to customize tree node icons.
   */
  class IconRenderer extends DefaultTreeCellRenderer {
    ImageIcon dimensionIcon;     // Icon for the dimension
    ImageIcon hierarchyIcon;     // Icon for the hierarchy
    ImageIcon folderClosedIcon;  // Icon for the closed folder
    ImageIcon folderOpenIcon;    // Icon for the open folder
    ImageIcon measureIcon;       // Icon for the measure

    /**
     * Constructor
     */
    public IconRenderer()
    {
      dimensionIcon = new ImageIcon(BIMetadata.class
        .getResource("images/dimension.gif"));
      hierarchyIcon = new ImageIcon(BIMetadata.class
        .getResource("images/hierarchy.gif"));
      folderClosedIcon = new ImageIcon(BIMetadata.class
        .getResource("images/folder-c.gif"));
      folderOpenIcon = new ImageIcon(BIMetadata.class
        .getResource("images/folder-o.gif"));
      measureIcon = new ImageIcon(BIMetadata.class
        .getResource("images/measure.gif"));
    }

    /**
     * Override the method on DefaultTreeCellRenderer to customize cell icons
     */
    public Component getTreeCellRendererComponent(JTree tree, Object value,
      boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
      // Call super class method
      super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row,
                                         hasFocus);
      MDObject obj = (MDObject)((DefaultMutableTreeNode)value).getUserObject();
      if(obj.getObjectType().equals(MM.FOLDER)) {
        if(expanded) {
          setIcon(folderOpenIcon);
        }
        else {
          setIcon(folderClosedIcon);
        }
      }
      else if(obj.getObjectType().equals(MM.HIERARCHY)) {
        setIcon(hierarchyIcon);
      }
      else if(obj.getObjectType().equals(MM.MEASURE)) {
        setIcon(measureIcon);
      }
      else if(obj.getObjectType().equals(MM.DIMENSION)) {
        setIcon(dimensionIcon);
      }
      return this;
    }
  }
}
