/*
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2002-2005 Julian Hyde
// Copyright (C) 2005-2015 Pentaho and others
// Copyright (C) 2006-2007 CINCOM SYSTEMS, INC.
// All Rights Reserved.
*/
package mondrian.gui;

import mondrian.gui.MondrianGuiDef.Hierarchy;
import mondrian.olap.Id;
import mondrian.olap.MondrianProperties;
import mondrian.olap.Util;

import org.eigenbase.xom.NodeDef;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.text.JTextComponent;

/**
 * @author sean
 */
public class SchemaPropertyCellEditor
    implements javax.swing.table.TableCellEditor
{
    Workbench workbench;

    final List<CellEditorListener> listeners =
        new ArrayList<CellEditorListener>();

    JTextField stringEditor;
    JTextArea cdataTextArea;
    JScrollPane jScrollPaneCDATA;
    // JEditorPane jEditorPaneCDATA;
    JCheckBox booleanEditor;
    JTextField integerEditor;
    JTable tableEditor;
    Component activeEditor;
    JComboBox listEditor;
    JTable relationTable;
    JPanel relationRenderer;

    JdbcMetaData jdbcMetaData;
    ComboBoxModel allOptions, selOptions;
    String listEditorValue;
    MouseListener ml;
    ItemListener il;
    ActionListener al;

    String noSelect = "-- No Selection --";
    FocusAdapter editorFocus;

    Object originalValue;

    public SchemaPropertyCellEditor(
        Workbench workbench)
    {
        this(workbench, null);
    }

    /**
     * Creates a new instance of SchemaPropertyCellEditor
     */
    public SchemaPropertyCellEditor(
        Workbench workbench,
        JdbcMetaData jdbcMetaData)
    {
        this.workbench = workbench;
        this.jdbcMetaData = jdbcMetaData;

        noSelect = getResourceConverter().getString(
            "schemaPropertyCellEditor.noSelection", noSelect);

        stringEditor = new JTextField();
        stringEditor.setFont(Font.decode("Dialog"));
        stringEditor.setBorder(null);

        // cdata multi-line
        cdataTextArea = new JTextArea();
        cdataTextArea.setLineWrap(true);
        cdataTextArea.setWrapStyleWord(true);
        cdataTextArea.setLayout(new java.awt.BorderLayout());
        cdataTextArea.setEditable(true);
        cdataTextArea.setPreferredSize(new java.awt.Dimension(100, 300));
        cdataTextArea.setMinimumSize(new java.awt.Dimension(100, 100));

        jScrollPaneCDATA = new JScrollPane(cdataTextArea);
        jScrollPaneCDATA.setMaximumSize(cdataTextArea.getPreferredSize());

        booleanEditor = new JCheckBox();
        booleanEditor.setBackground(Color.white);

        integerEditor = new JTextField();
        integerEditor.setBorder(null);
        integerEditor.setHorizontalAlignment(JTextField.RIGHT);
        integerEditor.setFont(Font.decode("Courier"));

        tableEditor = new JTable();

        listEditor = new JComboBox();
        listEditor.setEditable(true);
        listEditor.setMaximumSize(stringEditor.getMaximumSize());
        listEditor.setFont(Font.decode("Dialog"));
        listEditor.setBackground(Color.white);
        listEditor.setBorder(
            new EmptyBorder(
                0, 0, 0, 0)); //super.noFocusBorder);

        al = new ActionListener() {
            boolean all = true;

            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("comboBoxChanged")
                    && listEditor.getSelectedIndex() == 0)
                {   // 0 index refers to less or more options
                    if (all) {
                        listEditor.setModel(allOptions);
                    } else {
                        listEditor.setModel(selOptions);
                    }
                    listEditor.setSelectedIndex(-1);
                    all = !all;
                }
                // Must invoke later on the GUI thread since trying
                // now will fail. The component is already marked
                // as 'dirty'.
                SwingUtilities.invokeLater(
                    new Runnable() {
                        public void run() {
                            if (listEditor.isDisplayable()) {
                                listEditor.setPopupVisible(true);
                            }
                        }
                    });
            }
        };

        JTextComponent editor =
            (JTextComponent) listEditor.getEditor().getEditorComponent();

        editor.addMouseListener(
            new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    if (listEditor.isDisplayable()) {
                        listEditor.setPopupVisible(true);
                    }
                }
            });

        editor.addKeyListener(
            new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
                    if (listEditor.isDisplayable()) {
                        listEditor.setPopupVisible(true);
                    }
                }

                public void keyReleased(KeyEvent e) {
                    // listEditor.setSelectedItem(
                    //   ((JTextComponent) e.getSource()).getText());
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        listEditor.setSelectedItem(listEditorValue);
                        listEditor.getEditor().setItem(listEditorValue);
                    }
                }
            });

        // Not used

//        relationRenderer = new JPanel();
//
//        relationList = new JComboBox(
//            new String[] {
//                getResourceConverter().getString(
//                    "schemaPropertyCellEditor.join","Join"),
//                 getResourceConverter().getString(
//                     "schemaPropertyCellEditor.table","Table")});
//        relationList.setMaximumSize(stringEditor.getMaximumSize());
//        relationTable = new JTable();
//        relationRenderer.add(relationList);
//        relationRenderer.add(relationTable);
    }


    public MondrianGuiDef.RelationOrJoin getRelation(
        final JTable table,
        final int row,
        final int column)
    {
        PropertyTableModel tableModel = (PropertyTableModel) table.getModel();
        Object value = tableModel.getValue();
        Class<?> targetClassz = tableModel.target.getClass();
        Object parent = this.getParentObject();

        MondrianGuiDef.RelationOrJoin relation = null;

        if (targetClassz == MondrianGuiDef.Table.class) {
            relation = (MondrianGuiDef.Table) value;
        } else if (targetClassz == MondrianGuiDef.View.class) {
            relation = (MondrianGuiDef.View) value;
        } else if (targetClassz == MondrianGuiDef.Join.class) {
            relation = (MondrianGuiDef.Join) value;
        } else if (targetClassz == MondrianGuiDef.Hierarchy.class) {
            MondrianGuiDef.Hierarchy hProps = (MondrianGuiDef.Hierarchy) value;
            relation = hProps.relation;
        } else if (targetClassz == MondrianGuiDef.Level.class) {
            MondrianGuiDef.Hierarchy hProps = (MondrianGuiDef.Hierarchy) parent;
            relation = hProps.relation;
        } else if (targetClassz == MondrianGuiDef.Cube.class) {
            MondrianGuiDef.Cube hProps = (MondrianGuiDef.Cube) value;
            relation = hProps.fact;
        }

        return relation;
    }

    public Component getTableCellEditorComponent(
        final JTable table,
        Object value,
        boolean isSelected,
        final int row,
        final int column)
    {
        PropertyTableModel tableModel = (PropertyTableModel) table.getModel();
        Class<?> parentClassz = null;
        if (tableModel.getParentTarget() != null) {
            parentClassz = tableModel.getParentTarget().getClass();
        }
        Class<?> targetClassz = tableModel.target.getClass();
        String propertyName = tableModel.getRowName(row);
        String selectedFactTable = tableModel.getFactTable();
        String selectedFactTableSchema = tableModel.getFactTableSchema();
        listEditorValue = null;  // reset value of combo-box
        Object parent = this.getParentObject();

        MondrianGuiDef.RelationOrJoin currentRelation =
            getRelation(table, row, column);

        boolean nonTableRelation =
            currentRelation != null
            && !(currentRelation instanceof MondrianGuiDef.Table
                 || currentRelation instanceof MondrianGuiDef.Join);

        if (targetClassz == MondrianGuiDef.UserDefinedFunction.class
            && propertyName.equals("className"))
        {
            List<String> udfs = getUdfs();
            ComboBoxModel cAlludfs =
                new DefaultComboBoxModel(new Vector<String>(udfs));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAlludfs);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
        } else if (targetClassz == MondrianGuiDef.Measure.class
                   && propertyName.equals("formatString"))
        {
            List<String> formatStrs = getFormatStrings();
            ComboBoxModel cAllformatStrs =
                new DefaultComboBoxModel(new Vector<String>(formatStrs));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllformatStrs);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
        } else if (targetClassz == MondrianGuiDef.Measure.class
                   && propertyName.equals("aggregator"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Measure._aggregator_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Measure.class
                   && propertyName.equals("datatype"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Measure._datatype_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Parameter.class
                   && propertyName.equals("parameter"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Parameter._type_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.SQL.class
                   && propertyName.equals("dialect"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(MondrianGuiDef.SQL._dialect_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Level.class
                   && propertyName.equals("hideMemberIf"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Level._hideMemberIf_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Level.class
                   && propertyName.equals("levelType"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Level._levelType_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Level.class
                   && propertyName.equals("type"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(MondrianGuiDef.Level._type_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Level.class
            && propertyName.equals("internalType"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Level._internalType_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Dimension.class
                   && propertyName.equals("type"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(
                    MondrianGuiDef.Dimension._type_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.DimensionUsage.class
                   && propertyName.equals("source"))
        {
            List<String> source = getSource();
            ComboBoxModel cAllsource =
                new DefaultComboBoxModel(new Vector<String>(source));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllsource);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if ((tableModel.target instanceof MondrianGuiDef.Grant
            || tableModel.target instanceof MondrianGuiDef.MemberGrant)
            && propertyName.equals("access"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            ComboBoxModel cAccess =
                new DefaultComboBoxModel(
                    new String[]{"all", "none"});

            if (targetClassz == MondrianGuiDef.SchemaGrant.class) {
                cAccess = new DefaultComboBoxModel(
                    new String[]{
                        "all", "custom", "none", "all_dimensions"
                    });
            } else if (targetClassz == MondrianGuiDef.CubeGrant.class
                       || targetClassz == MondrianGuiDef.DimensionGrant.class
                       || targetClassz == MondrianGuiDef.MemberGrant.class)
            {
                cAccess =
                    new DefaultComboBoxModel(
                        new String[]{"all", "custom", "none"});

            } else if (targetClassz == MondrianGuiDef.HierarchyGrant.class
                || targetClassz == MondrianGuiDef.DimensionGrant.class)
            {
                cAccess = new DefaultComboBoxModel(
                    new String[]{
                        "all", "custom", "none"
                    });
            }
            listEditor.setModel(cAccess);

            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.HierarchyGrant.class
            && propertyName.equals("rollupPolicy"))
        {
            ComboBoxModel cRollupPolicy =
                new DefaultComboBoxModel(
                    new String[]{"full", "partial", "hidden"});
            listEditor.setModel(cRollupPolicy);
            listEditor.setSelectedItem(value);
            activeEditor = listEditor;
        } else if (targetClassz == MondrianGuiDef.DimensionGrant.class
                    && propertyName.equals("dimension"))
        {
            List<String> source = getDimensions();
            ComboBoxModel cAllsource =
                new DefaultComboBoxModel(new Vector<String>(source));

            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllsource);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.HierarchyGrant.class
                       && propertyName.equals("hierarchy"))
        {
            List<String> source = getHierarchies();
            ComboBoxModel cAllsource =
                new DefaultComboBoxModel(new Vector<String>(source));

            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllsource);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if ((targetClassz == MondrianGuiDef.HierarchyGrant.class
                    && (propertyName.equals("topLevel")
                        || propertyName.equals("bottomLevel"))))
        {
            List<String> source = getLevels(
                ((MondrianGuiDef.HierarchyGrant) tableModel.target).hierarchy);
            ComboBoxModel cAllsource =
                new DefaultComboBoxModel(new Vector<String>(source));

            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllsource);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if (((targetClassz == MondrianGuiDef.VirtualCubeDimension.class
                     || targetClassz == MondrianGuiDef.VirtualCubeMeasure.class)
                    && propertyName.equals("cubeName"))
                   || (targetClassz == MondrianGuiDef.CubeGrant.class
                       && propertyName.equals("cube")))
        {
            List<String> source = getCubes();
            ComboBoxModel cAllsource =
                new DefaultComboBoxModel(new Vector<String>(source));

            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllsource);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
        } else if ((targetClassz == MondrianGuiDef.Dimension.class
                    && propertyName.equals("foreignKey"))
                   || (targetClassz == MondrianGuiDef.DimensionUsage.class
                       && propertyName.equals("foreignKey"))
                   || (targetClassz == MondrianGuiDef.Measure.class
                       && propertyName.equals("column")))
        {
            Vector<String> fks = new Vector<String>(
                jdbcMetaData.getFactTableFKs(
                    selectedFactTableSchema, selectedFactTable));
            fks.add(
                0, getResourceConverter().getString(
                    "schemaPropertyCellEditor.allColumns",
                    "<< All Columns >>"));
            Vector<String> allcols = new Vector<String>(
                jdbcMetaData.getAllColumns(
                    selectedFactTableSchema, selectedFactTable));
            ComboBoxModel cFks = new DefaultComboBoxModel(fks);

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            if ((fks.size() > 1) && propertyName.equals("foreignKey")) {
                allcols.add(
                    0,
                    getResourceConverter().getString(
                        "schemaPropertyCellEditor.foreignKeys",
                        "<< Foreign keys >>"));
                ComboBoxModel cAllcols = new DefaultComboBoxModel(allcols);
                listEditor.setModel(cFks);
                selOptions = cFks;
                allOptions = cAllcols;
                listEditor.addActionListener(al);
            } else {
                ComboBoxModel cAllcols = new DefaultComboBoxModel(allcols);
                listEditor.setModel(cAllcols);
            }

            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
        } else if (targetClassz == MondrianGuiDef.Hierarchy.class
                   && propertyName.equals("primaryKey"))
        {
            MondrianGuiDef.Hierarchy hProps =
                (MondrianGuiDef.Hierarchy) tableModel.getValue();
            String pkTable = hProps.primaryKeyTable;

            String schemaName = null;

            String pk = "";
            List<String> allcols;

            MondrianGuiDef.RelationOrJoin relation = hProps.relation;
            if (relation instanceof MondrianGuiDef.Table) {
                pkTable = ((MondrianGuiDef.Table) relation).name;
                schemaName = ((MondrianGuiDef.Table) relation).schema;
                pk = jdbcMetaData.getTablePK(schemaName, pkTable);
            } else if (relation instanceof MondrianGuiDef.Join) {
                String[] schemaAndTable =
                    SchemaExplorer.getTableNameForAlias(
                        hProps.relation, pkTable);
                schemaName = schemaAndTable[0];
                pkTable = schemaAndTable[1];
            }

            if (relation instanceof MondrianGuiDef.Table
                || relation instanceof MondrianGuiDef.Join)
            {
                allcols = jdbcMetaData.getAllColumns(schemaName, pkTable);
                pk = jdbcMetaData.getTablePK(schemaName, pkTable);
            } else {
                allcols = Collections.emptyList();
            }

            ComboBoxModel cAllcols =
                new DefaultComboBoxModel(new Vector<String>(allcols));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllcols);
            if (value == null || ((String) value).equals("")) {
                listEditor.setSelectedItem(pk);
            } else {
                listEditor.setSelectedItem((String) value);
                listEditorValue = (String) value;
            }
            activeEditor = listEditor;
        } else if ((targetClassz == MondrianGuiDef.Level.class
                    && propertyName.equals("column"))
                   || (targetClassz == MondrianGuiDef.Level.class
                       && propertyName.equals("nameColumn"))
                   || (targetClassz == MondrianGuiDef.Level.class
                       && propertyName.equals("parentColumn"))
                   || (targetClassz == MondrianGuiDef.Level.class
                       && propertyName.equals("ordinalColumn"))
                   || (targetClassz == MondrianGuiDef.Level.class
                       && propertyName.equals("captionColumn"))
                   || (targetClassz == MondrianGuiDef.Closure.class
                       && propertyName.equals("parentColumn"))
                   || (targetClassz == MondrianGuiDef.Closure.class
                       && propertyName.equals("childColumn"))
                   || (targetClassz == MondrianGuiDef.Property.class
                       && propertyName.equals("column")))
        {
            MondrianGuiDef.Level lProps;
            if (targetClassz == MondrianGuiDef.Level.class) {
                lProps = (MondrianGuiDef.Level) tableModel.getValue();
            } else {
                lProps = (MondrianGuiDef.Level) this.getParentObject();
            }

            String schemaName = null;
            String lTable = lProps.table;
            List<String> allcols;

            // Sets the corresponding columns on the selection dropdown for the
            // specified table.
            if (targetClassz == MondrianGuiDef.Level.class && parent != null) {
                if (parent instanceof MondrianGuiDef.Hierarchy) {
                    MondrianGuiDef.RelationOrJoin relation =
                        ((MondrianGuiDef.Hierarchy) parent).relation;
                    if (relation instanceof MondrianGuiDef.Table) {
                        lTable = ((MondrianGuiDef.Table) relation).name;
                        schemaName = ((MondrianGuiDef.Table) relation).schema;
                    } else if (relation instanceof MondrianGuiDef.Join) {
                        String[] schemaAndTable =
                            SchemaExplorer.getTableNameForAlias(
                                relation, lTable);
                        schemaName = schemaAndTable[0];
                        lTable = schemaAndTable[1];
                    }
                }
            }
            if (lTable != null) {
                allcols = jdbcMetaData.getAllColumns(schemaName, lTable);
            } else {
                allcols = Collections.emptyList();
            }
            ComboBoxModel cAllcols =
                new DefaultComboBoxModel(new Vector<String>(allcols));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllcols);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Property.class
                   && propertyName.equals("type"))
        {
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(
                new DefaultComboBoxModel(MondrianGuiDef.Property._type_values));
            listEditor.setSelectedItem((String) value);
            activeEditor = listEditor;

        } else if ((targetClassz == MondrianGuiDef.AggFactCount.class
            && propertyName.equals("column"))
            || (targetClassz == MondrianGuiDef.AggIgnoreColumn.class
            && propertyName.equals("column"))
            || (targetClassz == MondrianGuiDef.AggLevelProperty.class
            && propertyName.equals("column"))
            || (targetClassz == MondrianGuiDef.AggLevel.class
            && Arrays.asList(
                "column", "ordinalColumn",
                "captionColumn", "nameColumn").contains(propertyName))
            || (targetClassz == MondrianGuiDef.AggMeasure.class
            && propertyName.equals("column"))
            || (targetClassz == MondrianGuiDef.AggForeignKey.class
            && propertyName.equals("factColumn"))
            || (targetClassz == MondrianGuiDef.AggForeignKey.class
            && propertyName.equals("aggColumn")))
        {
            List<String> allcols = jdbcMetaData.getAllColumns(null, null);

            ComboBoxModel cAllcols =
                new DefaultComboBoxModel(new Vector<String>(allcols));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllcols);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;

        } else if (targetClassz == MondrianGuiDef.Table.class && propertyName
            .equals("schema"))
        {
            List<String> allschemas = jdbcMetaData.getAllSchemas();
            ComboBoxModel cAllschemas =
                new DefaultComboBoxModel(new Vector<String>(allschemas));

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);

            listEditor.setModel(cAllschemas);
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
        } else if (currentRelation != null
                   && nonTableRelation
                   && ((targetClassz == MondrianGuiDef.Hierarchy.class
                        && propertyName.equals("primaryKeyTable"))
                       || (targetClassz == MondrianGuiDef.Level.class
                           && propertyName.equals("table"))))
        {
            // Can't set a table on a non table relation
            listEditor.setEditable(false);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            activeEditor = listEditor;
        } else if ((targetClassz == MondrianGuiDef.Table.class
                    && propertyName.equals("name"))
                   || (targetClassz == MondrianGuiDef.Hierarchy.class
                       && propertyName.equals("primaryKeyTable"))
                   || (targetClassz == MondrianGuiDef.Level.class
                       && propertyName.equals("table")))
        {
            String schema = "";
            if (targetClassz == MondrianGuiDef.Table.class) {
                MondrianGuiDef.Table tProps =
                    (MondrianGuiDef.Table) tableModel.getValue();
                schema = tProps.schema;
            }
            Vector<String> factTables =
                new Vector<String>(jdbcMetaData.getFactTables(schema));
            Vector<String> allTablesMinusFact =
                new Vector<String>(
                    jdbcMetaData.getAllTables(
                        schema, selectedFactTable));
            Vector<String> allTables =
                new Vector<String>(jdbcMetaData.getAllTables(schema));
            Vector<String> dimeTables =
                new Vector<String>(
                    jdbcMetaData.getDimensionTables(
                        schema, selectedFactTable));

            // suggestive fact tables
            ComboBoxModel cFactTables =
                new DefaultComboBoxModel(factTables);

            // all tables of selected schema
            ComboBoxModel cAllTables = new DefaultComboBoxModel(
                (allTablesMinusFact.size() > 0)
                    ? allTablesMinusFact
                    : allTables);

            // suggestive dimension tables based on selected fact table
            ComboBoxModel cDimeTables =
                new DefaultComboBoxModel(dimeTables);

            // Sets the corresponding join tables on selection dropdown when
            // using joins.
            if (targetClassz == MondrianGuiDef.Level.class
                || targetClassz == MondrianGuiDef.Hierarchy.class)
            {
                MondrianGuiDef.RelationOrJoin relation = null;
                if (parent != null
                    && parent instanceof MondrianGuiDef.Hierarchy)
                {
                    relation = ((MondrianGuiDef.Hierarchy) parent).relation;
                } else {
                    relation =
                        ((MondrianGuiDef.Hierarchy) tableModel.target).relation;
                }
                if (relation instanceof MondrianGuiDef.Join) {
                    TreeSet<String> joinTables = new TreeSet<String>();
                    // getTableNamesForJoin calls itself recursively and
                    // collects table names in joinTables.
                    SchemaExplorer.getTableNamesForJoin(relation, joinTables);
                    cAllTables =
                        new DefaultComboBoxModel(
                            new Vector<String>(
                                joinTables));
                }
            }

            listEditor.setEditable(true);
            listEditor.setToolTipText(null);
            listEditor.removeActionListener(al);
            listEditor.setModel(cAllTables);
            allOptions = cAllTables;
            boolean toggleModel = false;
            if (parentClassz == MondrianGuiDef.Cube.class) {
                cAllTables = new DefaultComboBoxModel(allTables);
                allOptions = cAllTables;
                if (factTables.size() > 0) {
                    ((DefaultComboBoxModel) cFactTables).insertElementAt(
                        workbench.getResourceConverter().getString(
                            "schemaPropertyCellEditor.allTables",
                            "<< All Tables >>"), 0);
                    ((DefaultComboBoxModel) cAllTables).insertElementAt(
                        workbench.getResourceConverter().getString(
                            "schemaPropertyCellEditor.factTables",
                            "<< Fact Tables >>"), 0);
                    listEditor.setModel(cFactTables);
                    selOptions = cFactTables;
                    toggleModel = true;
                }
            } else {
                if (dimeTables.size() > 0) {
                    ((DefaultComboBoxModel) cDimeTables).insertElementAt(
                        workbench.getResourceConverter().getString(
                            "schemaPropertyCellEditor.allTables",
                            "<< All Tables >>"), 0);
                    ((DefaultComboBoxModel) cAllTables).insertElementAt(
                        workbench.getResourceConverter().getString(
                            "schemaPropertyCellEditor.dimensionTables",
                            "<< Dimension Tables >>"), 0);
                    listEditor.setModel(cDimeTables);
                    selOptions = cDimeTables;
                    toggleModel = true;
                }
            }

            if (toggleModel) {
                listEditor.addActionListener(al);
            }
            listEditor.setSelectedItem((String) value);
            listEditorValue = (String) value;
            activeEditor = listEditor;
            // Disables table selection when not using joins.
            if ((targetClassz == MondrianGuiDef.Level.class
                 && propertyName.equals(SchemaExplorer.DEF_LEVEL[1])
                 && parent != null)
                || (targetClassz == MondrianGuiDef.Hierarchy.class
                    && propertyName.equals(SchemaExplorer.DEF_HIERARCHY[7])
                    && parent != null))
            {
                MondrianGuiDef.RelationOrJoin relation = null;
                if (parent instanceof MondrianGuiDef.Hierarchy) {
                    relation = ((MondrianGuiDef.Hierarchy) parent).relation;
                } else if (parent instanceof MondrianGuiDef.Dimension) {
                    relation =
                        ((MondrianGuiDef.Hierarchy) tableModel.target).relation;
                }
                if (relation instanceof MondrianGuiDef.Table) {
                    activeEditor = stringEditor;
                    stringEditor.setText((String) value);
                }
            }
        } else if (propertyName.equals("cdata")) {
            try {
                cdataTextArea.read(new StringReader((String) value), null);
            } catch (Exception ex) {
            }

            activeEditor = jScrollPaneCDATA;
        } else if (value instanceof String) {
            activeEditor = stringEditor;
            stringEditor.setText((String) value);
        } else if (value instanceof Boolean) {
            activeEditor = booleanEditor;
            booleanEditor.setSelected((Boolean) value);
        } else if (value instanceof Integer) {
            activeEditor = integerEditor;
            integerEditor.setText((String) value);
        } else if (value == null) {
            value = "";
            activeEditor = stringEditor;
            stringEditor.setText((String) value);
        } else if (value.getClass() == MondrianGuiDef.Join.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench, value, SchemaExplorer.DEF_JOIN);
            tableEditor.setModel(ptm);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.NameExpression.class) {
            return null;
        } else if (value.getClass() == MondrianGuiDef.RelationOrJoin.class) {
            // REVIEW: I don't think this code will ever be executed, because
            // class RelationOrJoin is abstract.
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench, value, SchemaExplorer.DEF_RELATION);
            tableEditor.setModel(ptm);
            activeEditor = tableEditor;
            return null;
        } else if (value.getClass() == MondrianGuiDef.OrdinalExpression.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench,
                    ((MondrianGuiDef.OrdinalExpression) value).expressions[0],
                    SchemaExplorer.DEF_SQL);
            ptm.setParentTarget(((PropertyTableModel) table.getModel()).target);
            tableEditor.setModel(ptm);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.CaptionExpression.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench,
                    ((MondrianGuiDef.CaptionExpression) value).expressions[0],
                    SchemaExplorer.DEF_SQL);
            ptm.setParentTarget(((PropertyTableModel) table.getModel()).target);
            tableEditor.setModel(ptm);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.Formula.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(
                    workbench, jdbcMetaData);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench, value, SchemaExplorer.DEF_FORMULA);
            tableEditor.setModel(ptm);
            tableEditor.getColumnModel().getColumn(0).setMaxWidth(100);
            tableEditor.getColumnModel().getColumn(0).setMinWidth(100);
            activeEditor = tableEditor;
        } else if (value.getClass()
                   == MondrianGuiDef.CalculatedMemberProperty.class)
        {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench, jdbcMetaData);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench,
                    value,
                    SchemaExplorer.DEF_CALCULATED_MEMBER_PROPERTY);
            tableEditor.setModel(ptm);
            tableEditor.getColumnModel().getColumn(0).setMaxWidth(100);
            tableEditor.getColumnModel().getColumn(0).setMinWidth(100);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.Table.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench, jdbcMetaData);
            // Adding cell editing stopped listeners to nested property of type
            // table so that any change in value of table fields are reflected
            // in tree.
            for (int i = listeners.size() - 1; i >= 0; i--) {
                spce.addCellEditorListener(listeners.get(i));
            }
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr = new SchemaPropertyCellRenderer(
                workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm = new PropertyTableModel(
                workbench, value, SchemaExplorer.DEF_TABLE);
            ptm.setFactTable(selectedFactTable);
            if (targetClassz == MondrianGuiDef.Cube.class) {
                ptm.setParentTarget(
                    ((PropertyTableModel) table.getModel()).target);
            }
            tableEditor.setModel(ptm);
            tableEditor.getColumnModel().getColumn(0).setMaxWidth(100);
            tableEditor.getColumnModel().getColumn(0).setMinWidth(100);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.AggFactCount.class) {
            SchemaPropertyCellEditor spce = new SchemaPropertyCellEditor(
                workbench, jdbcMetaData);
            // Adding cell editing stopped listeners to nested property of type
            // table so that any change in value of table fields are reflected
            // in tree.
            for (int i = listeners.size() - 1; i >= 0; i--) {
                spce.addCellEditorListener(listeners.get(i));
            }
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr = new SchemaPropertyCellRenderer(
                workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm = new PropertyTableModel(
                workbench, value, SchemaExplorer.DEF_AGG_FACT_COUNT);
            ptm.setFactTable(selectedFactTable);
            tableEditor.setModel(ptm);
            tableEditor.getColumnModel().getColumn(0).setMaxWidth(100);
            tableEditor.getColumnModel().getColumn(0).setMinWidth(100);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.Closure.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench, jdbcMetaData);
            // Adding cell editing stopped listeners to nested property of type
            // table so that any change in value of table fields are reflected
            // in tree.
            for (int i = listeners.size() - 1; i >= 0; i--) {
                spce.addCellEditorListener(listeners.get(i));
            }
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench, value, SchemaExplorer.DEF_CLOSURE);
            ptm.setFactTable(selectedFactTable);
            if (targetClassz == MondrianGuiDef.Level.class) {
                ptm.setParentTarget(
                    ((PropertyTableModel) table.getModel()).target);
            }
            tableEditor.setModel(ptm);
            tableEditor.getColumnModel().getColumn(0).setMaxWidth(100);
            tableEditor.getColumnModel().getColumn(0).setMinWidth(100);
            spcr.setTableRendererHeight(tableEditor, null);
            activeEditor = tableEditor;
        } else if (value.getClass() == MondrianGuiDef.Property.class) {
            SchemaPropertyCellEditor spce =
                new SchemaPropertyCellEditor(workbench);
            tableEditor.setDefaultEditor(Object.class, spce);
            SchemaPropertyCellRenderer spcr =
                new SchemaPropertyCellRenderer(workbench);
            tableEditor.setDefaultRenderer(Object.class, spcr);
            PropertyTableModel ptm =
                new PropertyTableModel(
                    workbench, value, SchemaExplorer.DEF_PROPERTY);
            tableEditor.setModel(ptm);
            activeEditor = tableEditor;
        } else {
            value = "";
            activeEditor = stringEditor;
            stringEditor.setText((String) value);
        }
        activeEditor.setVisible(true);

        setOriginalValue();

        table.changeSelection(row, column, false, false);
        activeEditor.setBackground(new java.awt.Color(224, 249, 255));
        activeEditor.requestFocusInWindow();
        return activeEditor;
    }

    /**
     * save original value to see whether it changed
     */
    private void setOriginalValue() {
        if (activeEditor == stringEditor) {
            originalValue = stringEditor.getText();
        } else if (activeEditor == booleanEditor) {
            originalValue = booleanEditor.isSelected();
        } else if (activeEditor == listEditor) {
            if (listEditor.isEditable()) {
                // returns the edited value from combox box
                originalValue = listEditor.getEditor().getItem();
            } else {
                if (listEditor.getSelectedItem() == noSelect) {
                    originalValue = null;  // blank selection
                }
                // returns the selected value from combox box
                originalValue = listEditor.getSelectedItem();
            }
        } else if (activeEditor == tableEditor) {
            originalValue =
                ((PropertyTableModel) tableEditor.getModel()).getValue();
        } else if (activeEditor == jScrollPaneCDATA) {
            Writer cdataTextAreaStr = new StringWriter();
            try {
                cdataTextArea.write(cdataTextAreaStr);
            } catch (Exception ex) {
            }
            originalValue = cdataTextAreaStr.toString();
        }
    }

    /**
     * Adds a listener to the list that's notified when the editor
     * stops, or cancels editing.
     *
     * @param l the CellEditorListener
     */
    public void addCellEditorListener(CellEditorListener l) {
        listeners.add(l);
    }

    /**
     * Tells the editor to cancel editing and not accept any partially
     * edited value.
     */
    public void cancelCellEditing() {
        if (activeEditor != null) {
            activeEditor.setVisible(false);
            fireEditingCancelled();
        }
    }

    /**
     * Returns the value contained in the editor.
     *
     * @return the value contained in the editor
     */
    public Object getCellEditorValue() {
        if (activeEditor == stringEditor) {
            return stringEditor.getText();
        } else if (activeEditor == booleanEditor) {
            return booleanEditor.isSelected();
        } else if (activeEditor == listEditor) {
            if (listEditor.isEditable()) {
                // returns the edited value from combox box
                return listEditor.getEditor().getItem();
            } else {
                if (listEditor.getSelectedItem() == noSelect) {
                    return null;  // blank selection
                }
                // returns the selected value from combox box
                return listEditor.getSelectedItem();
            }
        } else if (activeEditor == tableEditor) {
            return ((PropertyTableModel) tableEditor.getModel()).getValue();
        } else if (activeEditor == jScrollPaneCDATA) {
            Writer cdataTextAreaStr = new StringWriter();
            try {
                cdataTextArea.write(cdataTextAreaStr);
            } catch (Exception ex) {
            }
            return cdataTextAreaStr.toString();
        }

        return null;
    }

    /**
     * Asks the editor if it can start editing using <code>anEvent</code>.
     * <code>anEvent</code> is in the invoking component coordinate system.
     * The editor can not assume the Component returned by
     * <code>getCellEditorComponent</code> is installed.  This method
     * is intended for the use of client to avoid the cost of setting up
     * and installing the editor component if editing is not possible.
     * If editing can be started this method returns true.
     *
     * @param anEvent the event the editor should use to consider
     *                whether to begin editing or not
     * @return true if editing can be started
     * @see #shouldSelectCell
     */
    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }

    /**
     * Removes a listener from the list that's notified
     *
     * @param l the CellEditorListener
     */
    public void removeCellEditorListener(CellEditorListener l) {
        listeners.remove(l);
    }

    /**
     * Returns true if the editing cell should be selected, false otherwise.
     * Typically, the return value is true, because is most cases the editing
     * cell should be selected.  However, it is useful to return false to
     * keep the selection from changing for some types of edits.
     * eg. A table that contains a column of check boxes, the user might
     * want to be able to change those checkboxes without altering the
     * selection.  (See Netscape Communicator for just such an example)
     * Of course, it is up to the client of the editor to use the return
     * value, but it doesn't need to if it doesn't want to.
     *
     * @param anEvent the event the editor should use to start
     *                editing
     * @return true if the editor would like the editing cell to be selected;
     *         otherwise returns false
     * @see #isCellEditable
     */
    public boolean shouldSelectCell(EventObject anEvent) {
        return true;
    }

    /**
     * Tells the editor to stop editing and accept any partially edited
     * value as the value of the editor.  The editor returns false if
     * editing was not stopped; this is useful for editors that validate
     * and can not accept invalid entries.
     *
     * @return true if editing was stopped; false otherwise
     */
    public boolean stopCellEditing() {
        if (activeEditor != null) {
            /* save the nested table as well */
            if (activeEditor == tableEditor) {
                if (tableEditor.isEditing()) {
                    List<JTable> nestedTableEditors = new ArrayList<JTable>();
                    JTable nestedTableEditor = tableEditor;
                    // Get the list of nested tables from outer->inner sequence,
                    // descending towards innermost nested table
                    // so that we can stop the editing in this order.
                    while (nestedTableEditor != null) {
                        nestedTableEditors.add(nestedTableEditor);
                        SchemaPropertyCellEditor sce =
                            (SchemaPropertyCellEditor) nestedTableEditor
                                .getCellEditor();
                        if (sce != null
                            && sce.activeEditor == sce.tableEditor
                            && sce.tableEditor.isEditing())
                        {
                            nestedTableEditor = sce.tableEditor; //
                            //tableEditor.editingStopped(null);
                        } else {
                            nestedTableEditor = null;
                        }
                    }
                    for (int i = nestedTableEditors.size() - 1; i >= 0; i--) {
                        nestedTableEditors.get(i).editingStopped(null);
                    }
                }
            }
            activeEditor.setVisible(false);
            fireEditingStopped();
        }
        return true;
    }

    protected void fireEditingStopped() {
        ChangeEvent ce = new ChangeEvent(this);
        for (int i = listeners.size() - 1; i >= 0; i--) {
            listeners.get(i).editingStopped(ce);
        }
    }

    protected void fireEditingCancelled() {
        ChangeEvent ce = new ChangeEvent(this);
        for (int i = listeners.size() - 1; i >= 0; i--) {
            listeners.get(i).editingCanceled(ce);
        }
    }

    private List<String> getUdfs() {
        List<String> udfs = new ArrayList<String>();
        MondrianGuiDef.Schema s = this.getSchema();
        if (s != null) {
            MondrianGuiDef.UserDefinedFunction[] u = s.userDefinedFunctions;
            for (int i = 0; i < u.length; i++) {
                if (!(u[i].className == null
                      || udfs.contains(u[i].className)))
                {
                    udfs.add(u[i].className);
                }
            }
        }

        return udfs;
    }

    private List<String> getFormatStrings() {
        List<String> fs = new ArrayList<String>();
        MondrianGuiDef.Schema s = this.getSchema();
        if (s != null) {
            MondrianGuiDef.Cube[] c = s.cubes;
            for (int i = 0; i < c.length; i++) {
                MondrianGuiDef.Measure[] m = c[i].measures;
                for (int j = 0; j < m.length; j++) {
                    if (!(m[j].formatString == null
                          || fs.contains(m[j].formatString)))
                    {
                        fs.add(m[j].formatString);
                    }
                }
            }
        }
        return fs;
    }

    private MondrianGuiDef.Schema getSchema() {
        SchemaExplorer se = this.getSchemaExplorer();
        return (se == null)
            ? null
            : se.getSchema();
    }

    private Object getParentObject() {
        SchemaExplorer se = this.getSchemaExplorer();
        if (se != null) {
            Object po = se.getParentObject();
            return po;
        }
        return null;
    }

    private SchemaExplorer getSchemaExplorer() {
        for (int i = listeners.size() - 1; i >= 0; i--) {
            CellEditorListener cel = listeners.get(i);
            if (cel instanceof SchemaExplorer) {
                return (SchemaExplorer) cel;
            }
        }
        return null;
    }

    // shared dimensions in schema
    private List<String> getSource() {
        List<String> source = new ArrayList<String>();
        MondrianGuiDef.Schema s = this.getSchema();
        if (s != null) {
            MondrianGuiDef.Dimension[] u = s.dimensions;
            for (int i = 0; i < u.length; i++) {
                source.add(u[i].name);
            }
        }
        return source;
    }

    private List<String> getCubes() {
        List<String> source = new ArrayList<String>();
        //===source.add(noSelect);
        MondrianGuiDef.Schema s = this.getSchema();
        if (s != null) {
            MondrianGuiDef.Cube[] u = s.cubes;
            for (int i = 0; i < u.length; i++) {
                source.add(u[i].name);
            }
        }
        return source;
    }

    private void generatePrimaryKeyTables(Object relation, List<String> v) {
        if (relation == null) {
            return;
        }
        if (relation instanceof MondrianGuiDef.Table) {
            String sname = ((MondrianGuiDef.Table) relation).schema;
            v.add(
                ((sname == null || sname.equals(""))
                    ? ""
                    : sname + "->") + ((MondrianGuiDef.Table) relation).name);
            return;
        }
        MondrianGuiDef.Join currentJoin = (MondrianGuiDef.Join) relation;
        generatePrimaryKeyTables(currentJoin.left, v);
        generatePrimaryKeyTables(currentJoin.right, v);
        return;
    }

    private List<String> getDimensions() {
        List<String> dims = new ArrayList<String>();
        Object po = getParentObject(); // cubegrant
        if (po != null) {
            MondrianGuiDef.CubeGrant parent = (MondrianGuiDef.CubeGrant) po;
            if (!(parent.cube == null || parent.cube.equals(""))) {
                MondrianGuiDef.Schema s = getSchema();
                if (s != null) {
                    for (int i = 0; i < s.cubes.length; i++) {
                        if (s.cubes[i].name.equals(parent.cube)) {
                            dims.add("Measures");
                            for (int j = 0; j < s.cubes[i].dimensions.length;
                                j++)
                            {
                                dims.add(s.cubes[i].dimensions[j].name);
                            }
                            break;
                        }
                    }
                }
            }
        }
        return dims;
    }

    private List<String> getHierarchies() {
        List<String> hiers = new ArrayList<String>();
        Object po = getParentObject(); // cubegrant
        if (po != null) {
            MondrianGuiDef.CubeGrant parent = (MondrianGuiDef.CubeGrant) po;
            if (!(parent.cube == null || parent.cube.equals(""))) {
                MondrianGuiDef.Schema s = getSchema();
                if (s != null) {
                    for (int i = 0; i < s.cubes.length; i++) {
                        if (s.cubes[i].name.equals(parent.cube)) {
                            for (int j = 0; j < s.cubes[i].dimensions.length;
                                j++)
                            {
                                MondrianGuiDef.Dimension sharedDim =
                                    lookupDimension(
                                        s, s.cubes[i].dimensions[j]);
                                NodeDef[] children = sharedDim.getChildren();
                                for (int k = 0; k < children.length; k++) {
                                    if (children[k] instanceof Hierarchy) {
                                        String hname =
                                            ((Hierarchy) children[k]).name;
                                        if (hname != null) {
                                            if (MondrianProperties.instance()
                                                .SsasCompatibleNaming.get())
                                            {
                                                hiers.add(
                                                    Util.quoteMdxIdentifier(
                                                        s.cubes[i].dimensions[j]
                                                            .name)
                                                        + "."
                                                        + Util
                                                            .quoteMdxIdentifier(
                                                                hname));
                                            } else {
                                                hiers.add(
                                                    Util.quoteMdxIdentifier(
                                                        s.cubes[i].dimensions[j]
                                                            .name
                                                        + "."
                                                        + hname));
                                            }
                                        } else {
                                            hiers.add(Util.quoteMdxIdentifier(
                                                s.cubes[i].dimensions[j].name));
                                        }
                                    }
                                }
                            }

                            break;
                        }
                    }
                }
            }
        }
        return hiers;
    }

    private String cacheCube = "";
    private String cacheHierarchy = "";
    private List<String> hlevels = new ArrayList<String>();

    private List<String> getLevels(String hierarchy) {
        if (hierarchy == null || hierarchy.equals("")) {
            return hlevels;
        }
        List<Id.Segment> segments = Util.parseIdentifier(hierarchy);
        if (segments == null || segments.size() == 0) {
            return hlevels;
        }
        if (!MondrianProperties.instance().SsasCompatibleNaming.get()) {
            String data = ((Id.NameSegment)segments.get(0)).getName();
            // if segment contains a hierarchy
            if (data.indexOf(".") >= 0) {
                Id.Segment segment = segments.get(0);
                // split the segment
                segments.clear();
                segments.add(new Id.NameSegment(
                    data.substring(0, data.indexOf(".")),
                    segment.getQuoting()));
                segments.add(new Id.NameSegment(
                    data.substring(data.indexOf(".") + 1),
                    segment.getQuoting()));
            }
        }
        Object po = getParentObject(); // cubegrant
        if (po == null) {
            return hlevels;
        }
        MondrianGuiDef.CubeGrant parent = (MondrianGuiDef.CubeGrant) po;
        if (parent.cube == null || parent.cube.equals("")) {
            return hlevels;
        }
        if (cacheCube.equals(parent.cube) && cacheHierarchy.equals(hierarchy)) {
            return hlevels;
        }
        hlevels = new ArrayList<String>();
        cacheCube = parent.cube;
        cacheHierarchy = hierarchy;
        MondrianGuiDef.Schema s = getSchema();
        if (s == null) {
            return hlevels;
        }
        for (int i = 0; i < s.cubes.length; i++) {
            final MondrianGuiDef.Cube cube = s.cubes[i];
            if (!cube.name.equals(parent.cube)) {
                continue;
            }
            for (int j = 0; j < cube.dimensions.length; j++) {
                final MondrianGuiDef.CubeDimension dimension =
                    cube.dimensions[j];
                if (!segments.get(0).matches(dimension.name)) {
                    continue;
                }
                MondrianGuiDef.Dimension d = lookupDimension(s, dimension);
                NodeDef[] children = d.getChildren();
                MondrianGuiDef.Hierarchy hierarchyObj = null;
                for (int k = 0; k < children.length; k++) {
                    if (children[k] instanceof Hierarchy) {
                        if ((segments.size() == 1
                                && ((Hierarchy) children[k]).name == null)
                            || (segments.size() != 0
                                && segments.get(1).matches(
                                    ((Hierarchy) children[k]).name)))
                        {
                            hierarchyObj = (Hierarchy)children[k];
                            break;
                        }
                    }
                }
                if (hierarchyObj != null) {
                    for (int k = 0; k < hierarchyObj.levels.length; k++) {
                        hlevels.add(
                            hierarchy + "."
                            + Util.quoteMdxIdentifier(
                                hierarchyObj.levels[k].name));
                    }
                }
            }
            break;
        }
        return hlevels;
    }

    private static MondrianGuiDef.Dimension lookupDimension(
        MondrianGuiDef.Schema schema,
        MondrianGuiDef.CubeDimension cubeDimension)
    {
        if (cubeDimension instanceof MondrianGuiDef.Dimension) {
            return (MondrianGuiDef.Dimension) cubeDimension;
        } else {
            MondrianGuiDef.DimensionUsage dimensionUsage =
                (MondrianGuiDef.DimensionUsage) cubeDimension;
            for (int m = 0; m < schema.dimensions.length; m++) {
                final MondrianGuiDef.Dimension dimension =
                    schema.dimensions[m];
                if (dimension.name.equals(dimensionUsage.source)) {
                    return dimension;
                }
            }
            return null;
        }
    }

    private I18n getResourceConverter() {
        return workbench.getResourceConverter();
    }

    public void setMetaData(JdbcMetaData jdbcMetaData) {
        // Called from the SchemaExplorer.resetMetadata(). A call to the
        // updateUI() should be made on the owning SchemaFrame to reflect the
        // use of the JdbcMetaData being set.
        this.jdbcMetaData = jdbcMetaData;
    }
}

// End SchemaPropertyCellEditor.java
