/*
* 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-2013 Pentaho Corporation..  All rights reserved.
*/

package mondrian.gui.validate;

import mondrian.gui.MondrianGuiDef;
import mondrian.gui.SchemaExplorer;

import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.util.TreeSet;

/**
 * Validates a <code>MondrianGuiDef</code>. Class contains <code>invalid</code>
 * method formerly from <code>mondrian.gui.SchemaTreeCellRenderer</code>.
 *
 * @author mlowery
 */
public class ValidationUtils {

    private static final Logger LOGGER =
        Logger.getLogger(ValidationUtils.class);

    static String[] DEF_LEVEL = {
        "column", "nameColumn", "parentColumn", "ordinalColumn", "captionColumn"
    };

    /**
     * Validate a schema model and returns the first error message if it is
     * invalid.
     *
     * @param messages Message provider
     * @param jdbcValidator Validator
     * @param treeModel Tree model
     * @param tpath Path
     * @param value Value
     * @param cube Cube
     * @param parentDimension Parent dimension
     * @param parentHierarchy Parent hierarchy
     * @param parentLevel Parent level
     * @param isSchemaRequired Whether schema is required
     * @return Error message if element is invalid, null if it is valid
     */
    public static String invalid(
        Messages messages,
        JdbcValidator jdbcValidator,
        TreeModel treeModel,
        TreeModelPath tpath,
        Object value,
        MondrianGuiDef.Cube cube,
        MondrianGuiDef.Dimension parentDimension,
        MondrianGuiDef.Hierarchy parentHierarchy,
        MondrianGuiDef.Level parentLevel,
        boolean isSchemaRequired)
    {
        String nameMustBeSet = messages.getString(
            "schemaTreeCellRenderer.nameMustBeSet.alert", "Name must be set");

        if (!tpath.isEmpty()) {
            int pathcount = tpath.getPathCount();
            for (int i = 0;
                i < pathcount
                && (cube == null
                    || parentDimension == null
                    || parentHierarchy == null
                    || parentLevel == null);
                i++)
            {
                final Object p = tpath.getPathComponent(i);
                if (p instanceof MondrianGuiDef.Cube
                    && cube == null)
                {
                    cube = (MondrianGuiDef.Cube) p;
                }
                if (p instanceof MondrianGuiDef.Dimension
                    && parentDimension == null)
                {
                    parentDimension = (MondrianGuiDef.Dimension) p;
                }
                if (p instanceof MondrianGuiDef.Hierarchy
                    && parentHierarchy == null)
                {
                    parentHierarchy = (MondrianGuiDef.Hierarchy) p;
                }
                if (p instanceof MondrianGuiDef.Level
                    && parentLevel == null)
                {
                    parentLevel = (MondrianGuiDef.Level) p;
                }
            }
        }

        //Step 1: check validity of this value object
        if (value instanceof MondrianGuiDef.Schema) {
            if (isEmpty(((MondrianGuiDef.Schema) value).name)) {
                return nameMustBeSet;
            }
        } else if (value instanceof MondrianGuiDef.VirtualCube) {
            MondrianGuiDef.VirtualCube virtCube =
                (MondrianGuiDef.VirtualCube)value;
            if (isEmpty(virtCube.name)) {
                return nameMustBeSet;
            }
            if (isEmpty(virtCube.dimensions)) {
                return messages.getString(
                    "schemaTreeCellRenderer.cubeMustHaveDimensions.alert",
                    "Cube must contain dimensions");
            }
            if (isEmpty(virtCube.measures)) {
                return messages.getString(
                    "schemaTreeCellRenderer.cubeMustHaveMeasures.alert",
                    "Cube must contain measures");
            }
        } else if (value instanceof MondrianGuiDef.VirtualCubeDimension) {
            if (isEmpty(((MondrianGuiDef.VirtualCubeDimension) value).name)) {
                return nameMustBeSet;
            }
        } else if (value instanceof MondrianGuiDef.VirtualCubeMeasure) {
            if (isEmpty(((MondrianGuiDef.VirtualCubeMeasure) value).name)) {
                return nameMustBeSet;
            }
        } else if (value instanceof MondrianGuiDef.Cube) {
            MondrianGuiDef.Cube cubeVal = (MondrianGuiDef.Cube) value;
            if (isEmpty(cubeVal.name)) {
                return nameMustBeSet;
            }
            if (cubeVal.fact == null
                || ((cubeVal.fact instanceof MondrianGuiDef.Table)
                    && isEmpty(((MondrianGuiDef.Table) cubeVal.fact).name))
                || ((cubeVal.fact instanceof MondrianGuiDef.View)
                    && isEmpty(((MondrianGuiDef.View) cubeVal.fact).alias)))
            {
                return messages.getString(
                    "schemaTreeCellRenderer.factNameMustBeSet.alert",
                    "Fact name must be set");
            }
            if (isEmpty(cubeVal.dimensions)) {
                return messages.getString(
                    "schemaTreeCellRenderer.cubeMustHaveDimensions.alert",
                    "Cube must contain dimensions");
            }
            if (isEmpty(cubeVal.measures)) {
                return messages.getString(
                    "schemaTreeCellRenderer.cubeMustHaveMeasures.alert",
                    "Cube must contain measures");
            }
            // database validity check, if database connection is successful
            if (jdbcValidator.isInitialized()) {
                if (((MondrianGuiDef.Cube) value).fact
                    instanceof MondrianGuiDef.Table)
                {
                    final MondrianGuiDef.Table table =
                        (MondrianGuiDef.Table) cubeVal.fact;
                    String schemaName = table.schema;
                    String factTable = table.name;
                    if (!jdbcValidator.isTableExists(schemaName, factTable)) {
                        return messages.getFormattedString(
                            "schemaTreeCellRenderer.factTableDoesNotExist.alert",
                            "Fact table {0} does not exist in database {1}",
                            factTable,
                            ((schemaName == null || schemaName.equals(""))
                                ? "."
                                : "schema " + schemaName));
                    }
                }
            }
        } else {
            if (value instanceof MondrianGuiDef.CubeDimension) {
                if (isEmpty(((MondrianGuiDef.CubeDimension) value).name)) {
                    return nameMustBeSet;
                }
                if (value instanceof MondrianGuiDef.DimensionUsage) {
                    if (isEmpty(
                            ((MondrianGuiDef.DimensionUsage) value).source))
                    {
                        return messages.getString(
                            "schemaTreeCellRenderer.sourceMustBeSet.alert",
                            "Source must be set");
                    }
                    // Check source is name of one of dimensions of schema
                    // (shared dimensions)
                    MondrianGuiDef.Schema s =
                        (MondrianGuiDef.Schema) treeModel.getRoot();
                    MondrianGuiDef.Dimension ds[] = s.dimensions;
                    String sourcename =
                        ((MondrianGuiDef.DimensionUsage) value).source;
                    boolean notfound = true;
                    for (int j = 0; j < ds.length; j++) {
                        if (ds[j].name.equalsIgnoreCase(sourcename)) {
                            notfound = false;
                            break;
                        }
                    }
                    if (notfound) {
                        return messages.getFormattedString(
                            "schemaTreeCellRenderer.sourceInSharedDimensionDoesNotExist.alert",
                            "Source {0} does not exist as Shared Dimension of Schema",
                            sourcename);
                    }
                }
                if (value instanceof MondrianGuiDef.Dimension && cube != null) {
                    if (!isEmpty(
                            ((MondrianGuiDef.Dimension) value).foreignKey))
                    {
                        // database validity check, if database connection is
                        // successful
                        if (jdbcValidator.isInitialized()) {
                            // TODO: Need to add validation for Views
                            if (cube.fact instanceof MondrianGuiDef.Table) {
                                final MondrianGuiDef.Table factTable =
                                    (MondrianGuiDef.Table) cube.fact;
                                String foreignKey =
                                    ((MondrianGuiDef.Dimension) value)
                                    .foreignKey;
                                if (!jdbcValidator.isColExists(
                                        factTable.schema,
                                        factTable.name,
                                        foreignKey))
                                {
                                    return messages.getFormattedString(
                                        "schemaTreeCellRenderer.foreignKeyDoesNotExist.alert",
                                        "foreignKey {0} does not exist in fact table",
                                        foreignKey);
                                }
                            }
                        }
                    }
                }
            } else if (value instanceof MondrianGuiDef.Level) {
                // Check 'column' exists in 'table' if table is specified
                // otherwise :: case of join.

                // It should exist in relation table if it is specified
                // otherwise :: case of table.

                // It should exist in fact table :: case of degenerate dimension
                // where dimension columns exist in fact table and there is no
                // separate table.
                MondrianGuiDef.Level level = (MondrianGuiDef.Level) value;
                if (!isEmpty(level.levelType)) {
                    // Empty leveltype is treated as default value of "Regular""
                    // which is ok with standard/time dimension.
                    if (parentDimension != null) {
                        if ((isEmpty(parentDimension.type)
                             || parentDimension.type.equals(
                                 "StandardDimension"))
                            && !isEmpty(level.levelType)
                            && (!level.levelType.equals(
                                MondrianGuiDef.Level._levelType_values[0])))
                        {
                            // If dimension type is 'standard' then leveltype
                            // should be 'regular'
                            return messages.getFormattedString(
                                "schemaTreeCellRenderer.levelUsedOnlyInTimeDimension.alert",
                                "levelType {0} can only be used with a TimeDimension",
                                level.levelType);
                        } else if (!isEmpty(parentDimension.type)
                                   && (parentDimension.type.equals(
                                       "TimeDimension"))
                                   && !isEmpty(level.levelType)
                                   && (level.levelType.equals(
                                       MondrianGuiDef.Level
                                       ._levelType_values[0])))
                        {
                            // If dimension type is 'time' then leveltype value
                            // could be 'timeyears', 'timedays' etc'
                            return messages.getFormattedString(
                                "schemaTreeCellRenderer.levelUsedOnlyInStandardDimension.alert",
                                "levelType {0} can only be used with a StandardDimension",
                                level.levelType);
                        }
                    }
                }
                // verify level's name is set
                if (isEmpty(level.name)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.nameMustBeSet.alert",
                        "Level name must be set"
                    );
                }

                // check level's column is in fact table
                String column = level.column;
                if (isEmpty(column)) {
                    if (level.properties == null
                        || level.properties.length == 0)
                    {
                        return messages.getString(
                            "schemaTreeCellRenderer.columnMustBeSet.alert",
                            "Column must be set");
                    }
                } else {
                    // Enforces validation for all column types against invalid
                    // value.
                    String theMessage = null;
                    try {
                        for (int i = 0; i < DEF_LEVEL.length; i++) {
                            Field theField =
                                level.getClass().getDeclaredField(DEF_LEVEL[i]);
                            column = (String) theField.get(level);
                            theMessage = validateColumn(
                                column,
                                DEF_LEVEL[i],
                                messages,
                                level,
                                jdbcValidator,
                                cube,
                                parentHierarchy);
                            if (theMessage != null) {
                                break;
                            }
                        }
                    } catch (Exception ex) {
                        LOGGER.error("ValidationUtils", ex);
                    }
                    return theMessage;
                }
            } else if (value instanceof MondrianGuiDef.Property) {
                // Check 'column' exists in 'table' if [level table] is
                // specified otherwise :: case of join.

                // It should exist in [hierarchy relation table] if it is
                // specified otherwise :: case of table.

                // It should exist in [fact table] :: case of degenerate
                // dimension where dimension columns exist in fact table and
                // there is no separate table.
                MondrianGuiDef.Property p = (MondrianGuiDef.Property) value;
                // check property's column is in table
                String column = p.column;
                if (isEmpty(column)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.columnMustBeSet.alert",
                        "Column must be set");
                }
                // Database validity check, if database connection is successful
                if (jdbcValidator.isInitialized()) {
                    String table = null;
                    if (parentLevel != null) {
                        // specified table for level's column'
                        table = parentLevel.table;
                    }
                    if (isEmpty(table)) {
                        if (parentHierarchy != null) {
                            if (parentHierarchy.relation == null
                                && cube != null)
                            {
                                // Case of degenerate dimension within cube,
                                // hierarchy table not specified
                                final MondrianGuiDef.Table factTable =
                                    (MondrianGuiDef.Table) cube.fact;
                                if (!jdbcValidator.isColExists(
                                        factTable.schema,
                                        factTable.name,
                                        column))
                                {
                                    return messages.getFormattedString(
                                        "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert",
                                        "Degenerate dimension validation check - Column {0} does not exist in fact table",
                                        column);
                                }
                            } else if (parentHierarchy.relation
                                       instanceof MondrianGuiDef.Table)
                            {
                                final MondrianGuiDef.Table parentTable =
                                    (MondrianGuiDef.Table)
                                    parentHierarchy.relation;
                                if (!jdbcValidator.isColExists(
                                        parentTable.schema,
                                        parentTable.name,
                                        column))
                                {
                                    return messages.getFormattedString(
                                        "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert",
                                        "Column {0} does not exist in Dimension table",
                                        parentTable.name);
                                }
                            }
                        }
                    } else {
                        if (!jdbcValidator.isColExists(null, table, column)) {
                            return messages.getFormattedString(
                                "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert",
                                "Column {0} does not exist in Level table {1}",
                                column,
                                table);
                        }
                    }
                }
            } else if (value instanceof MondrianGuiDef.Measure) {
                final MondrianGuiDef.Measure measure =
                    (MondrianGuiDef.Measure) value;
                if (isEmpty(measure.name)) {
                    return nameMustBeSet;
                }
                if (isEmpty(measure.aggregator)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.aggregatorMustBeSet.alert",
                        "Aggregator must be set");
                }
                if (measure.measureExp != null) {
                    // Measure expressions are OK
                } else if (isEmpty(measure.column)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.columnMustBeSet.alert",
                        "Column must be set");
                } else if (cube != null && cube.fact != null) {
                    // Database validity check, if database connection is
                    // successful
                    if (cube.fact instanceof MondrianGuiDef.Table) {
                        final MondrianGuiDef.Table factTable =
                            (MondrianGuiDef.Table) cube.fact;
                        if (jdbcValidator.isInitialized()) {
                            String column = measure.column;
                            if (jdbcValidator.isColExists(
                                    factTable.schema,
                                    factTable.name,
                                    column))
                            {
                                // Check for aggregator type only if column
                                // exists in table.

                                // Check if aggregator selected is valid on
                                // the data type of the column selected.
                                int colType =
                                    jdbcValidator.getColumnDataType(
                                        factTable.schema,
                                        factTable.name,
                                        measure.column);
                                // Coltype of 2, 4,5, 7, 8, -5 is numeric types
                                // whereas 1, 12 are char varchar string
                                // and 91 is date type.
                                // Types are enumerated in java.sql.Types.
                                int agIndex = -1;
                                if ("sum".equals(
                                        measure.aggregator)
                                    || "avg".equals(
                                        measure.aggregator))
                                {
                                    // aggregator = sum or avg, column should
                                    // be numeric
                                    agIndex = 0;
                                }
                                if (!(agIndex == -1
                                    || (colType >= 2 && colType <= 8)
                                    || colType == -5 || colType == -6))
                                {
                                    return messages.getFormattedString(
                                        "schemaTreeCellRenderer.aggregatorNotValidForColumn.alert",
                                        "Aggregator {0} is not valid for the data type of the column {1}",
                                        measure.aggregator,
                                        measure.column);
                                }
                            }
                        }
                    }
                }
            } else if (value instanceof MondrianGuiDef.Hierarchy) {
                final MondrianGuiDef.Hierarchy hierarchy =
                    (MondrianGuiDef.Hierarchy) value;
                if (hierarchy.relation instanceof MondrianGuiDef.Join) {
                    if (isEmpty(hierarchy.primaryKeyTable)) {
                        if (isEmpty(hierarchy.primaryKey)) {
                            return messages.getString(
                                "schemaTreeCellRenderer.primaryKeyTableAndPrimaryKeyMustBeSet.alert",
                                "PrimaryKeyTable and PrimaryKey must be set for Join");
                        } else {
                            return messages.getString(
                                "schemaTreeCellRenderer.primaryKeyTableMustBeSet.alert",
                                "PrimaryKeyTable must be set for Join");
                        }
                    }
                    if (isEmpty(hierarchy.primaryKey)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.primaryKeyMustBeSet.alert",
                            "PrimaryKey must be set for Join");
                    }
                }

                MondrianGuiDef.Level[] levels = hierarchy.levels;
                if (levels == null || levels.length == 0) {
                    return messages.getString(
                        "schemaTreeCellRenderer.atLeastOneLevelForHierarchy.alert",
                        "At least one Level must be set for Hierarchy");
                }

                // Validates that value in primaryKey exists in Table.
                String schema = null;
                String pkTable = null;
                if (hierarchy.relation instanceof MondrianGuiDef.Join) {
                    String[] schemaAndTable =
                        SchemaExplorer.getTableNameForAlias(
                            hierarchy.relation,
                            hierarchy.primaryKeyTable);
                    schema = schemaAndTable[0];
                    pkTable = schemaAndTable[1];
                } else if (hierarchy.relation instanceof MondrianGuiDef.Table) {
                    final MondrianGuiDef.Table table =
                        (MondrianGuiDef.Table) hierarchy.relation;
                    pkTable = table.name;
                    schema = table.schema;
                }

                if (pkTable != null
                    && !jdbcValidator.isColExists(
                        schema, pkTable, hierarchy.primaryKey))
                {
                    return messages.getFormattedString(
                        "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
                        "Column {0} defined in field {1} does not exist in table {2}",
                            isEmpty(hierarchy.primaryKey.trim())
                                ? "' '"
                                : hierarchy.primaryKey, "primaryKey",
                            pkTable);
                }

                // Validates against primaryKeyTable name on field when using
                // Table.
                if (hierarchy.relation instanceof MondrianGuiDef.Table) {
                    if (!isEmpty(hierarchy.primaryKeyTable)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.fieldMustBeEmpty",
                            "Table field must be empty");
                    }
                }

                // Validates that the value at primaryKeyTable corresponds to
                // tables in joins.
                String primaryKeyTable = hierarchy.primaryKeyTable;
                if (!isEmpty(primaryKeyTable)
                    && (hierarchy.relation instanceof MondrianGuiDef.Join))
                {
                    TreeSet<String> joinTables = new TreeSet<String>();
                    SchemaExplorer.getTableNamesForJoin(
                        hierarchy.relation, joinTables);
                    if (!joinTables.contains(primaryKeyTable)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.wrongTableValue",
                            "Table value does not correspond to any join");
                    }
                }

                if (!isEmpty(primaryKeyTable)
                    && (hierarchy.relation instanceof MondrianGuiDef.Table))
                {
                    MondrianGuiDef.Table theTable =
                        (MondrianGuiDef.Table) hierarchy.relation;
                    String compareTo =
                        (theTable.alias != null
                         && theTable.alias.trim().length() > 0)
                            ? theTable.alias
                            : theTable.name;
                    if (!primaryKeyTable.equals(compareTo)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.tableDoesNotMatch",
                            "Table value does not correspond to Hierarchy Relation");
                    }
                }

            } else if (value instanceof MondrianGuiDef.NamedSet) {
                final MondrianGuiDef.NamedSet namedSet =
                    (MondrianGuiDef.NamedSet) value;
                if (isEmpty(namedSet.name)) {
                    return nameMustBeSet;
                }
                if (isEmpty(namedSet.formula)
                    && namedSet.formulaElement == null)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
                        "Formula must be set");
                }
            } else if (value instanceof MondrianGuiDef.Formula) {
                final MondrianGuiDef.Formula formula =
                    (MondrianGuiDef.Formula) value;
                if (isEmpty(formula.cdata)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
                        "Formula must be set");
                }
            } else if (value instanceof MondrianGuiDef.UserDefinedFunction) {
                final MondrianGuiDef.UserDefinedFunction udf =
                    (MondrianGuiDef.UserDefinedFunction) value;
                if (isEmpty(udf.name)) {
                    return nameMustBeSet;
                }
                if (isEmpty(udf.className)
                    && udf.script == null)
                {
                    return messages.getString(
                        "Either a Class Name or a Script are required",
                        "Class name must be set");
                }
            } else if (value instanceof MondrianGuiDef.MemberFormatter) {
                final MondrianGuiDef.MemberFormatter f =
                    (MondrianGuiDef.MemberFormatter) value;
                if (isEmpty(f.className)
                    && f.script == null)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
                        "Either a Class Name or a Script are required");
                }
            } else if (value instanceof MondrianGuiDef.CellFormatter) {
                final MondrianGuiDef.CellFormatter f =
                    (MondrianGuiDef.CellFormatter) value;
                if (isEmpty(f.className)
                    && f.script == null)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
                        "Either a Class Name or a Script are required");
                }
            } else if (value instanceof MondrianGuiDef.PropertyFormatter) {
                final MondrianGuiDef.PropertyFormatter f =
                    (MondrianGuiDef.PropertyFormatter) value;
                if (isEmpty(f.className)
                    && f.script == null)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
                        "Either a Class Name or a Script are required");
                }
            } else if (value instanceof MondrianGuiDef.CalculatedMember) {
                final MondrianGuiDef.CalculatedMember calculatedMember =
                    (MondrianGuiDef.CalculatedMember) value;
                if (isEmpty(calculatedMember.name)) {
                    return nameMustBeSet;
                }
                if (isEmpty(calculatedMember.dimension)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.dimensionMustBeSet.alert",
                        "Dimension must be set");
                }
                if (isEmpty(calculatedMember.formula)
                    && calculatedMember.formulaElement == null)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
                        "Formula must be set");
                }
            } else if (value instanceof MondrianGuiDef.Join) {
                final MondrianGuiDef.Join join = (MondrianGuiDef.Join) value;
                if (isEmpty(join.leftKey)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.leftKeyMustBeSet.alert",
                        "Left key must be set");
                }
                if (isEmpty(join.rightKey)) {
                    return messages.getString(
                        "schemaTreeCellRenderer.rightKeyMustBeSet.alert",
                        "Right key must be set");
                }
            } else if (value instanceof MondrianGuiDef.Table) {
                final MondrianGuiDef.Table table = (MondrianGuiDef.Table) value;
                String tableName = table.name;
                if (!jdbcValidator.isTableExists(null, tableName)) {
                    return messages.getFormattedString(
                        "schemaTreeCellRenderer.tableDoesNotExist.alert",
                        "Table {0} does not exist in database",
                        tableName);
                }

                String theSchema = table.schema;
                if (!isEmpty(theSchema)
                    && !jdbcValidator.isSchemaExists(theSchema))
                {
                    return messages.getFormattedString(
                        "schemaTreeCellRenderer.schemaDoesNotExist.alert",
                        "Schema {0} does not exist",
                        theSchema);
                }
                if (isEmpty(theSchema) && isSchemaRequired) {
                    return messages.getString(
                        "schemaTreeCellRenderer.schemaMustBeSet.alert",
                        "Schema must be set");
                }
            }
        }

        // Step 2: check validity of all child objects for this value object.
        int childCnt = treeModel.getChildCount(value);
        for (int i = 0; i < childCnt; i++) {
            Object child = treeModel.getChild(value, i);
            String childErrMsg;
            if (child instanceof MondrianGuiDef.Cube) {
                // check current cube child and its children
                childErrMsg = invalid(
                    messages,
                    jdbcValidator,
                    treeModel,
                    tpath,
                    child,
                    (MondrianGuiDef.Cube) child,
                    parentDimension,
                    parentHierarchy,
                    parentLevel,
                    isSchemaRequired);
            } else if (child instanceof MondrianGuiDef.Dimension) {
                // check the current hierarchy and its children
                childErrMsg = invalid(
                    messages,
                    jdbcValidator,
                    treeModel,
                    tpath,
                    child,
                    cube,
                    (MondrianGuiDef.Dimension) child,
                    parentHierarchy,
                    parentLevel,
                    isSchemaRequired);
            } else if (child instanceof MondrianGuiDef.Hierarchy) {
                // special check for cube dimension where foreign key is blank :
                // allowed/not allowed
                if (value instanceof MondrianGuiDef.Dimension
                    && cube != null
                    && ((MondrianGuiDef.Hierarchy) child).relation != null)
                {
                    if (isEmpty(
                            ((MondrianGuiDef.Dimension) value).foreignKey))
                    {
                        // check foreignkey is not blank;
                        // if relation is null, foreignkey must be specified
                        return messages.getString(
                            "schemaTreeCellRenderer.foreignKeyMustBeSet.alert",
                            "Foreign key must be set");
                    }
                }
                // check the current hierarchy and its children
                childErrMsg = invalid(
                    messages,
                    jdbcValidator,
                    treeModel,
                    tpath,
                    child,
                    cube,
                    parentDimension,
                    (MondrianGuiDef.Hierarchy) child,
                    parentLevel,
                    isSchemaRequired);
            } else if (child instanceof MondrianGuiDef.Level) {
                // check the current hierarchy and its children
                childErrMsg = invalid(
                    messages,
                    jdbcValidator,
                    treeModel,
                    tpath,
                    child,
                    cube,
                    parentDimension,
                    parentHierarchy,
                    (MondrianGuiDef.Level) child,
                    isSchemaRequired);
            } else {
                // check this child and all its children objects with incoming
                // cube and hierarchy
                childErrMsg = invalid(
                    messages,
                    jdbcValidator,
                    treeModel,
                    tpath,
                    child,
                    cube,
                    parentDimension,
                    parentHierarchy,
                    parentLevel,
                    isSchemaRequired);
            }

            // If all children are valid then do a special check.
            // Special check for cubes to see if their child dimensions have
            // foreign key set and set the childErrMsg with error msg
            /* === Begin : disabled
            if (childErrMsg == null) {  // all children are valid
                if (child instanceof MondrianGuiDef.Cube) {
                    MondrianGuiDef.Cube c = (MondrianGuiDef.Cube) child;
                    MondrianGuiDef.CubeDimension [] ds = c.dimensions;
                    for (int j=0; j<ds.length; j++) {
                        MondrianGuiDef.CubeDimension d =
                            (MondrianGuiDef.CubeDimension) ds[j];
                        if (d instanceof MondrianGuiDef.DimensionUsage) {
                            continue;   // check the next dimension.
                        }

                        // check foreignkey is not blank
                        if(isEmpty(d.foreignKey)) {
                            childErrMsg = "ForeignKey" + emptyMsg;
                            break;
                        }

                        // database validity check, if database connection is
                        // successful
                        if (jdbcMetaData.getErrMsg() == null) {
                            String foreignKey = d.foreignKey;
                            if (! jdbcMetaData.isColExists(
                                ((MondrianGuiDef.Table) c.fact).schema,
                                 ((MondrianGuiDef.Table) c.fact).name,
                                  foreignKey))
                            {
                                childErrMsg =
                                 "ForeignKey '" + foreignKey +
                                  "' does not exist in fact table.";
                                break;
                            }
                           // check foreignKey is a fact table column
                            if (! allcols.contains(foreignKey)) {
                               childErrMsg =
                                "ForeignKey '" + foreignKey
                                + "' does not exist in fact table.";
                                break;
                            }
             * /
                        }
                    }
                }
            }
             * === End : disabled
             */
            // Now set the final errormsg
            if (childErrMsg != null) {
                String childClassName = child.getClass().getName();
                String simpleName[] = childClassName.split("[$.]", 0);
                String childName;
                try {
                    Field f = child.getClass().getField("name");
                    childName = (String) f.get(child);
                    if (childName == null) {
                        childName = "";
                    }
                    childErrMsg = messages.getFormattedString(
                        "schemaTreeCellRenderer.childErrorMessageWithName.alert",
                        "{0} {1} is invalid",
                        simpleName[simpleName.length - 1],
                        childName);
                } catch (Exception ex) {
                    childErrMsg = messages.getFormattedString(
                        "schemaTreeCellRenderer.childErrorExceptionMessage.alert",
                        "{0} is invalid",
                        simpleName[simpleName.length - 1]);
                }
                return childErrMsg;
            }
        }

        return null;
    }

    /**
     * Returns whether an object is null or the empty string.
     *
     * @param v Object
     * @return Whether object is null or the empty string
     */
    public static boolean isEmpty(String v) {
        return (v == null) || v.equals("");
    }

    /**
     * Returns whether an array is null or empty
     *
     * @param arr array
     * @return whether the array is null or empty
     */
    public static boolean isEmpty(Object[] arr) {
        return arr == null || arr.length == 0;
    }

    /**
     * Validates a column, and returns an error message if it is invalid.
     *
     * @param column Column
     * @param fieldName Field name
     * @param messages Message provider
     * @param level  Level
     * @param jdbcValidator JDBC validator
     * @param cube Cube
     * @param parentHierarchy Hierarchy
     * @return Error message if invalid, null if valid
     */
    private static String validateColumn(
        String column,
        String fieldName,
        Messages messages,
        MondrianGuiDef.Level level,
        JdbcValidator jdbcValidator,
        MondrianGuiDef.Cube cube,
        MondrianGuiDef.Hierarchy parentHierarchy)
    {
        if (!isEmpty(column)) {
            // database validity check, if database connection is successful
            if (jdbcValidator.isInitialized()) {
                // specified table for level's column
                String table = level.table;
                // If table has been changed in join then sets the table value
                // to null to cause "tableMustBeSet" validation fail.
                if (!isEmpty(table)
                    && parentHierarchy != null
                    && parentHierarchy.relation instanceof MondrianGuiDef.Join)
                {
                    TreeSet<String> joinTables = new TreeSet<String>();
                    SchemaExplorer.getTableNamesForJoin(
                        parentHierarchy.relation, joinTables);
                    if (!joinTables.contains(table)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.wrongTableValue",
                            "Table value does not correspond to any join");
                    }
                }

                if (!isEmpty(table)
                    && parentHierarchy != null
                    && parentHierarchy.relation instanceof MondrianGuiDef.Table)
                {
                    final MondrianGuiDef.Table parentTable =
                        (MondrianGuiDef.Table) parentHierarchy.relation;
                    MondrianGuiDef.Table theTable = parentTable;
                    String compareTo =
                        (theTable.alias != null
                         && theTable.alias.trim().length() > 0)
                        ? theTable.alias
                        : theTable.name;
                    if (!table.equals(compareTo)) {
                        return messages.getString(
                            "schemaTreeCellRenderer.tableDoesNotMatch",
                            "Table value does not correspond to Hierarchy Relation");
                    }
                }

                if (!isEmpty(table)
                    && parentHierarchy != null
                    && parentHierarchy.relation instanceof MondrianGuiDef.View)
                {
                    return messages.getString(
                        "schemaTreeCellRenderer.noTableForView",
                        "Table for column cannot be set in View");
                }

                if (isEmpty(table)) {
                    if (parentHierarchy != null) {
                        if (parentHierarchy.relation == null
                            && cube != null)
                        {
                            // case of degenerate dimension within cube,
                            // hierarchy table not specified
                            if (!jdbcValidator.isColExists(
                                    ((MondrianGuiDef.Table) cube.fact).schema,
                                    ((MondrianGuiDef.Table) cube.fact).name,
                                    column))
                            {
                                return messages.getFormattedString(
                                    "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert",
                                    "Degenerate dimension validation check - Column {0} does not exist in fact table",
                                    column);
                            }
                        } else if (parentHierarchy.relation
                                   instanceof MondrianGuiDef.Table)
                        {
                            final MondrianGuiDef.Table parentTable =
                                (MondrianGuiDef.Table) parentHierarchy.relation;
                            if (!jdbcValidator.isColExists(
                                    parentTable.schema,
                                    parentTable.name,
                                    column))
                            {
                                return messages.getFormattedString(
                                    "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
                                    "Column {0} defined in field {1} does not exist in table {2}",
                                    isEmpty(column.trim())
                                        ? "' '"
                                        : column,
                                    fieldName,
                                    parentTable.name);
                            }
                        } else if (parentHierarchy.relation
                            instanceof MondrianGuiDef.Join)
                        {
                            // relation is join, table should be specified
                            return messages.getString(
                                "schemaTreeCellRenderer.tableMustBeSet.alert",
                                "Table must be set");
                        }
                    }
                } else {
                    String schema = null;
                    // if using Joins then gets the table name for isColExists
                    // validation.
                    if (parentHierarchy != null
                        && parentHierarchy.relation
                        instanceof MondrianGuiDef.Join)
                    {
                        String[] schemaAndTable =
                            SchemaExplorer.getTableNameForAlias(
                                parentHierarchy.relation,
                                table);
                        schema = schemaAndTable[0];
                        table = schemaAndTable[1];
                    }
                    if (!jdbcValidator.isColExists(schema, table, column)) {
                        return messages.getFormattedString(
                            "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
                            "Column {0} defined in field {1} does not exist in table {2}",
                            isEmpty(column.trim())
                                ? "' '"
                                : column,
                            fieldName,
                            table);
                    }
                }
            }
        }
        return null;
    }

}

// End ValidationUtils.java
