001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2008-2010 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.gui.validate;
011
012import mondrian.gui.MondrianGuiDef;
013import mondrian.gui.SchemaExplorer;
014
015import org.apache.log4j.Logger;
016
017import java.lang.reflect.Field;
018import java.util.TreeSet;
019
020/**
021 * Validates a <code>MondrianGuiDef</code>. Class contains <code>invalid</code>
022 * method formerly from <code>mondrian.gui.SchemaTreeCellRenderer</code>.
023 *
024 * @author mlowery
025 */
026public class ValidationUtils {
027
028    private static final Logger LOGGER =
029        Logger.getLogger(ValidationUtils.class);
030
031    static String[] DEF_LEVEL = {
032        "column", "nameColumn", "parentColumn", "ordinalColumn", "captionColumn"
033    };
034
035    /**
036     * Validate a schema model and returns the first error message if it is
037     * invalid.
038     *
039     * @param messages Message provider
040     * @param jdbcValidator Validator
041     * @param treeModel Tree model
042     * @param tpath Path
043     * @param value Value
044     * @param cube Cube
045     * @param parentDimension Parent dimension
046     * @param parentHierarchy Parent hierarchy
047     * @param parentLevel Parent level
048     * @param isSchemaRequired Whether schema is required
049     * @return Error message if element is invalid, null if it is valid
050     */
051    public static String invalid(
052        Messages messages,
053        JdbcValidator jdbcValidator,
054        TreeModel treeModel,
055        TreeModelPath tpath,
056        Object value,
057        MondrianGuiDef.Cube cube,
058        MondrianGuiDef.Dimension parentDimension,
059        MondrianGuiDef.Hierarchy parentHierarchy,
060        MondrianGuiDef.Level parentLevel,
061        boolean isSchemaRequired)
062    {
063        String nameMustBeSet = messages.getString(
064            "schemaTreeCellRenderer.nameMustBeSet.alert", "Name must be set");
065
066        if (!tpath.isEmpty()) {
067            int pathcount = tpath.getPathCount();
068            for (int i = 0;
069                i < pathcount
070                && (cube == null
071                    || parentDimension == null
072                    || parentHierarchy == null
073                    || parentLevel == null);
074                i++)
075            {
076                final Object p = tpath.getPathComponent(i);
077                if (p instanceof MondrianGuiDef.Cube
078                    && cube == null)
079                {
080                    cube = (MondrianGuiDef.Cube) p;
081                }
082                if (p instanceof MondrianGuiDef.Dimension
083                    && parentDimension == null)
084                {
085                    parentDimension = (MondrianGuiDef.Dimension) p;
086                }
087                if (p instanceof MondrianGuiDef.Hierarchy
088                    && parentHierarchy == null)
089                {
090                    parentHierarchy = (MondrianGuiDef.Hierarchy) p;
091                }
092                if (p instanceof MondrianGuiDef.Level
093                    && parentLevel == null)
094                {
095                    parentLevel = (MondrianGuiDef.Level) p;
096                }
097            }
098        }
099
100        //Step 1: check validity of this value object
101        if (value instanceof MondrianGuiDef.Schema) {
102            if (isEmpty(((MondrianGuiDef.Schema) value).name)) {
103                return nameMustBeSet;
104            }
105        } else if (value instanceof MondrianGuiDef.VirtualCube) {
106            MondrianGuiDef.VirtualCube virtCube =
107                (MondrianGuiDef.VirtualCube)value;
108            if (isEmpty(virtCube.name)) {
109                return nameMustBeSet;
110            }
111            if (isEmpty(virtCube.dimensions)) {
112                return messages.getString(
113                    "schemaTreeCellRenderer.cubeMustHaveDimensions.alert",
114                    "Cube must contain dimensions");
115            }
116            if (isEmpty(virtCube.measures)) {
117                return messages.getString(
118                    "schemaTreeCellRenderer.cubeMustHaveMeasures.alert",
119                    "Cube must contain measures");
120            }
121        } else if (value instanceof MondrianGuiDef.VirtualCubeDimension) {
122            if (isEmpty(((MondrianGuiDef.VirtualCubeDimension) value).name)) {
123                return nameMustBeSet;
124            }
125        } else if (value instanceof MondrianGuiDef.VirtualCubeMeasure) {
126            if (isEmpty(((MondrianGuiDef.VirtualCubeMeasure) value).name)) {
127                return nameMustBeSet;
128            }
129        } else if (value instanceof MondrianGuiDef.Cube) {
130            MondrianGuiDef.Cube cubeVal = (MondrianGuiDef.Cube) value;
131            if (isEmpty(cubeVal.name)) {
132                return nameMustBeSet;
133            }
134            if (cubeVal.fact == null
135                || ((cubeVal.fact instanceof MondrianGuiDef.Table)
136                    && isEmpty(((MondrianGuiDef.Table) cubeVal.fact).name))
137                || ((cubeVal.fact instanceof MondrianGuiDef.View)
138                    && isEmpty(((MondrianGuiDef.View) cubeVal.fact).alias)))
139            {
140                return messages.getString(
141                    "schemaTreeCellRenderer.factNameMustBeSet.alert",
142                    "Fact name must be set");
143            }
144            if (isEmpty(cubeVal.dimensions)) {
145                return messages.getString(
146                    "schemaTreeCellRenderer.cubeMustHaveDimensions.alert",
147                    "Cube must contain dimensions");
148            }
149            if (isEmpty(cubeVal.measures)) {
150                return messages.getString(
151                    "schemaTreeCellRenderer.cubeMustHaveMeasures.alert",
152                    "Cube must contain measures");
153            }
154            // database validity check, if database connection is successful
155            if (jdbcValidator.isInitialized()) {
156                if (((MondrianGuiDef.Cube) value).fact
157                    instanceof MondrianGuiDef.Table)
158                {
159                    final MondrianGuiDef.Table table =
160                        (MondrianGuiDef.Table) cubeVal.fact;
161                    String schemaName = table.schema;
162                    String factTable = table.name;
163                    if (!jdbcValidator.isTableExists(schemaName, factTable)) {
164                        return messages.getFormattedString(
165                            "schemaTreeCellRenderer.factTableDoesNotExist.alert",
166                            "Fact table {0} does not exist in database {1}",
167                            factTable,
168                            ((schemaName == null || schemaName.equals(""))
169                                ? "."
170                                : "schema " + schemaName));
171                    }
172                }
173            }
174        } else {
175            if (value instanceof MondrianGuiDef.CubeDimension) {
176                if (isEmpty(((MondrianGuiDef.CubeDimension) value).name)) {
177                    return nameMustBeSet;
178                }
179                if (value instanceof MondrianGuiDef.DimensionUsage) {
180                    if (isEmpty(
181                            ((MondrianGuiDef.DimensionUsage) value).source))
182                    {
183                        return messages.getString(
184                            "schemaTreeCellRenderer.sourceMustBeSet.alert",
185                            "Source must be set");
186                    }
187                    // Check source is name of one of dimensions of schema
188                    // (shared dimensions)
189                    MondrianGuiDef.Schema s =
190                        (MondrianGuiDef.Schema) treeModel.getRoot();
191                    MondrianGuiDef.Dimension ds[] = s.dimensions;
192                    String sourcename =
193                        ((MondrianGuiDef.DimensionUsage) value).source;
194                    boolean notfound = true;
195                    for (int j = 0; j < ds.length; j++) {
196                        if (ds[j].name.equalsIgnoreCase(sourcename)) {
197                            notfound = false;
198                            break;
199                        }
200                    }
201                    if (notfound) {
202                        return messages.getFormattedString(
203                            "schemaTreeCellRenderer.sourceInSharedDimensionDoesNotExist.alert",
204                            "Source {0} does not exist as Shared Dimension of Schema",
205                            sourcename);
206                    }
207                }
208                if (value instanceof MondrianGuiDef.Dimension && cube != null) {
209                    if (!isEmpty(
210                            ((MondrianGuiDef.Dimension) value).foreignKey))
211                    {
212                        // database validity check, if database connection is
213                        // successful
214                        if (jdbcValidator.isInitialized()) {
215                            // TODO: Need to add validation for Views
216                            if (cube.fact instanceof MondrianGuiDef.Table) {
217                                final MondrianGuiDef.Table factTable =
218                                    (MondrianGuiDef.Table) cube.fact;
219                                String foreignKey =
220                                    ((MondrianGuiDef.Dimension) value)
221                                    .foreignKey;
222                                if (!jdbcValidator.isColExists(
223                                        factTable.schema,
224                                        factTable.name,
225                                        foreignKey))
226                                {
227                                    return messages.getFormattedString(
228                                        "schemaTreeCellRenderer.foreignKeyDoesNotExist.alert",
229                                        "foreignKey {0} does not exist in fact table",
230                                        foreignKey);
231                                }
232                            }
233                        }
234                    }
235                }
236            } else if (value instanceof MondrianGuiDef.Level) {
237                // Check 'column' exists in 'table' if table is specified
238                // otherwise :: case of join.
239
240                // It should exist in relation table if it is specified
241                // otherwise :: case of table.
242
243                // It should exist in fact table :: case of degenerate dimension
244                // where dimension columns exist in fact table and there is no
245                // separate table.
246                MondrianGuiDef.Level level = (MondrianGuiDef.Level) value;
247                if (!isEmpty(level.levelType)) {
248                    // Empty leveltype is treated as default value of "Regular""
249                    // which is ok with standard/time dimension.
250                    if (parentDimension != null) {
251                        if ((isEmpty(parentDimension.type)
252                             || parentDimension.type.equals(
253                                 "StandardDimension"))
254                            && !isEmpty(level.levelType)
255                            && (!level.levelType.equals(
256                                MondrianGuiDef.Level._levelType_values[0])))
257                        {
258                            // If dimension type is 'standard' then leveltype
259                            // should be 'regular'
260                            return messages.getFormattedString(
261                                "schemaTreeCellRenderer.levelUsedOnlyInTimeDimension.alert",
262                                "levelType {0} can only be used with a TimeDimension",
263                                level.levelType);
264                        } else if (!isEmpty(parentDimension.type)
265                                   && (parentDimension.type.equals(
266                                       "TimeDimension"))
267                                   && !isEmpty(level.levelType)
268                                   && (level.levelType.equals(
269                                       MondrianGuiDef.Level
270                                       ._levelType_values[0])))
271                        {
272                            // If dimension type is 'time' then leveltype value
273                            // could be 'timeyears', 'timedays' etc'
274                            return messages.getFormattedString(
275                                "schemaTreeCellRenderer.levelUsedOnlyInStandardDimension.alert",
276                                "levelType {0} can only be used with a StandardDimension",
277                                level.levelType);
278                        }
279                    }
280                }
281                // verify level's name is set
282                if (isEmpty(level.name)) {
283                    return messages.getString(
284                        "schemaTreeCellRenderer.nameMustBeSet.alert",
285                        "Level name must be set"
286                    );
287                }
288
289                // check level's column is in fact table
290                String column = level.column;
291                if (isEmpty(column)) {
292                    if (level.properties == null
293                        || level.properties.length == 0)
294                    {
295                        return messages.getString(
296                            "schemaTreeCellRenderer.columnMustBeSet.alert",
297                            "Column must be set");
298                    }
299                } else {
300                    // Enforces validation for all column types against invalid
301                    // value.
302                    String theMessage = null;
303                    try {
304                        for (int i = 0; i < DEF_LEVEL.length; i++) {
305                            Field theField =
306                                level.getClass().getDeclaredField(DEF_LEVEL[i]);
307                            column = (String) theField.get(level);
308                            theMessage = validateColumn(
309                                column,
310                                DEF_LEVEL[i],
311                                messages,
312                                level,
313                                jdbcValidator,
314                                cube,
315                                parentHierarchy);
316                            if (theMessage != null) {
317                                break;
318                            }
319                        }
320                    } catch (Exception ex) {
321                        LOGGER.error("ValidationUtils", ex);
322                    }
323                    return theMessage;
324                }
325            } else if (value instanceof MondrianGuiDef.Property) {
326                // Check 'column' exists in 'table' if [level table] is
327                // specified otherwise :: case of join.
328
329                // It should exist in [hierarchy relation table] if it is
330                // specified otherwise :: case of table.
331
332                // It should exist in [fact table] :: case of degenerate
333                // dimension where dimension columns exist in fact table and
334                // there is no separate table.
335                MondrianGuiDef.Property p = (MondrianGuiDef.Property) value;
336                // check property's column is in table
337                String column = p.column;
338                if (isEmpty(column)) {
339                    return messages.getString(
340                        "schemaTreeCellRenderer.columnMustBeSet.alert",
341                        "Column must be set");
342                }
343                // Database validity check, if database connection is successful
344                if (jdbcValidator.isInitialized()) {
345                    String table = null;
346                    if (parentLevel != null) {
347                        // specified table for level's column'
348                        table = parentLevel.table;
349                    }
350                    if (isEmpty(table)) {
351                        if (parentHierarchy != null) {
352                            if (parentHierarchy.relation == null
353                                && cube != null)
354                            {
355                                // Case of degenerate dimension within cube,
356                                // hierarchy table not specified
357                                final MondrianGuiDef.Table factTable =
358                                    (MondrianGuiDef.Table) cube.fact;
359                                if (!jdbcValidator.isColExists(
360                                        factTable.schema,
361                                        factTable.name,
362                                        column))
363                                {
364                                    return messages.getFormattedString(
365                                        "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert",
366                                        "Degenerate dimension validation check - Column {0} does not exist in fact table",
367                                        column);
368                                }
369                            } else if (parentHierarchy.relation
370                                       instanceof MondrianGuiDef.Table)
371                            {
372                                final MondrianGuiDef.Table parentTable =
373                                    (MondrianGuiDef.Table)
374                                    parentHierarchy.relation;
375                                if (!jdbcValidator.isColExists(
376                                        parentTable.schema,
377                                        parentTable.name,
378                                        column))
379                                {
380                                    return messages.getFormattedString(
381                                        "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert",
382                                        "Column {0} does not exist in Dimension table",
383                                        parentTable.name);
384                                }
385                            }
386                        }
387                    } else {
388                        if (!jdbcValidator.isColExists(null, table, column)) {
389                            return messages.getFormattedString(
390                                "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert",
391                                "Column {0} does not exist in Level table {1}",
392                                column,
393                                table);
394                        }
395                    }
396                }
397            } else if (value instanceof MondrianGuiDef.Measure) {
398                final MondrianGuiDef.Measure measure =
399                    (MondrianGuiDef.Measure) value;
400                if (isEmpty(measure.name)) {
401                    return nameMustBeSet;
402                }
403                if (isEmpty(measure.aggregator)) {
404                    return messages.getString(
405                        "schemaTreeCellRenderer.aggregatorMustBeSet.alert",
406                        "Aggregator must be set");
407                }
408                if (measure.measureExp != null) {
409                    // Measure expressions are OK
410                } else if (isEmpty(measure.column)) {
411                    return messages.getString(
412                        "schemaTreeCellRenderer.columnMustBeSet.alert",
413                        "Column must be set");
414                } else if (cube != null && cube.fact != null) {
415                    // Database validity check, if database connection is
416                    // successful
417                    if (cube.fact instanceof MondrianGuiDef.Table) {
418                        final MondrianGuiDef.Table factTable =
419                            (MondrianGuiDef.Table) cube.fact;
420                        if (jdbcValidator.isInitialized()) {
421                            String column = measure.column;
422                            if (jdbcValidator.isColExists(
423                                    factTable.schema,
424                                    factTable.name,
425                                    column))
426                            {
427                                // Check for aggregator type only if column
428                                // exists in table.
429
430                                // Check if aggregator selected is valid on
431                                // the data type of the column selected.
432                                int colType =
433                                    jdbcValidator.getColumnDataType(
434                                        factTable.schema,
435                                        factTable.name,
436                                        measure.column);
437                                // Coltype of 2, 4,5, 7, 8, -5 is numeric types
438                                // whereas 1, 12 are char varchar string
439                                // and 91 is date type.
440                                // Types are enumerated in java.sql.Types.
441                                int agIndex = -1;
442                                if ("sum".equals(
443                                        measure.aggregator)
444                                    || "avg".equals(
445                                        measure.aggregator))
446                                {
447                                    // aggregator = sum or avg, column should
448                                    // be numeric
449                                    agIndex = 0;
450                                }
451                                if (!(agIndex == -1
452                                    || (colType >= 2 && colType <= 8)
453                                    || colType == -5 || colType == -6))
454                                {
455                                    return messages.getFormattedString(
456                                        "schemaTreeCellRenderer.aggregatorNotValidForColumn.alert",
457                                        "Aggregator {0} is not valid for the data type of the column {1}",
458                                        measure.aggregator,
459                                        measure.column);
460                                }
461                            }
462                        }
463                    }
464                }
465            } else if (value instanceof MondrianGuiDef.Hierarchy) {
466                final MondrianGuiDef.Hierarchy hierarchy =
467                    (MondrianGuiDef.Hierarchy) value;
468                if (hierarchy.relation instanceof MondrianGuiDef.Join) {
469                    if (isEmpty(hierarchy.primaryKeyTable)) {
470                        if (isEmpty(hierarchy.primaryKey)) {
471                            return messages.getString(
472                                "schemaTreeCellRenderer.primaryKeyTableAndPrimaryKeyMustBeSet.alert",
473                                "PrimaryKeyTable and PrimaryKey must be set for Join");
474                        } else {
475                            return messages.getString(
476                                "schemaTreeCellRenderer.primaryKeyTableMustBeSet.alert",
477                                "PrimaryKeyTable must be set for Join");
478                        }
479                    }
480                    if (isEmpty(hierarchy.primaryKey)) {
481                        return messages.getString(
482                            "schemaTreeCellRenderer.primaryKeyMustBeSet.alert",
483                            "PrimaryKey must be set for Join");
484                    }
485                }
486
487                MondrianGuiDef.Level[] levels = hierarchy.levels;
488                if (levels == null || levels.length == 0) {
489                    return messages.getString(
490                        "schemaTreeCellRenderer.atLeastOneLevelForHierarchy.alert",
491                        "At least one Level must be set for Hierarchy");
492                }
493
494                // Validates that value in primaryKey exists in Table.
495                String schema = null;
496                String pkTable = null;
497                if (hierarchy.relation instanceof MondrianGuiDef.Join) {
498                    String[] schemaAndTable =
499                        SchemaExplorer.getTableNameForAlias(
500                            hierarchy.relation,
501                            hierarchy.primaryKeyTable);
502                    schema = schemaAndTable[0];
503                    pkTable = schemaAndTable[1];
504                } else if (hierarchy.relation instanceof MondrianGuiDef.Table) {
505                    final MondrianGuiDef.Table table =
506                        (MondrianGuiDef.Table) hierarchy.relation;
507                    pkTable = table.name;
508                    schema = table.schema;
509                }
510
511                if (pkTable != null
512                    && !jdbcValidator.isColExists(
513                        schema, pkTable, hierarchy.primaryKey))
514                {
515                    return messages.getFormattedString(
516                        "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
517                        "Column {0} defined in field {1} does not exist in table {2}",
518                            isEmpty(hierarchy.primaryKey.trim())
519                                ? "' '"
520                                : hierarchy.primaryKey, "primaryKey",
521                            pkTable);
522                }
523
524                // Validates against primaryKeyTable name on field when using
525                // Table.
526                if (hierarchy.relation instanceof MondrianGuiDef.Table) {
527                    if (!isEmpty(hierarchy.primaryKeyTable)) {
528                        return messages.getString(
529                            "schemaTreeCellRenderer.fieldMustBeEmpty",
530                            "Table field must be empty");
531                    }
532                }
533
534                // Validates that the value at primaryKeyTable corresponds to
535                // tables in joins.
536                String primaryKeyTable = hierarchy.primaryKeyTable;
537                if (!isEmpty(primaryKeyTable)
538                    && (hierarchy.relation instanceof MondrianGuiDef.Join))
539                {
540                    TreeSet<String> joinTables = new TreeSet<String>();
541                    SchemaExplorer.getTableNamesForJoin(
542                        hierarchy.relation, joinTables);
543                    if (!joinTables.contains(primaryKeyTable)) {
544                        return messages.getString(
545                            "schemaTreeCellRenderer.wrongTableValue",
546                            "Table value does not correspond to any join");
547                    }
548                }
549
550                if (!isEmpty(primaryKeyTable)
551                    && (hierarchy.relation instanceof MondrianGuiDef.Table))
552                {
553                    MondrianGuiDef.Table theTable =
554                        (MondrianGuiDef.Table) hierarchy.relation;
555                    String compareTo =
556                        (theTable.alias != null
557                         && theTable.alias.trim().length() > 0)
558                            ? theTable.alias
559                            : theTable.name;
560                    if (!primaryKeyTable.equals(compareTo)) {
561                        return messages.getString(
562                            "schemaTreeCellRenderer.tableDoesNotMatch",
563                            "Table value does not correspond to Hierarchy Relation");
564                    }
565                }
566
567            } else if (value instanceof MondrianGuiDef.NamedSet) {
568                final MondrianGuiDef.NamedSet namedSet =
569                    (MondrianGuiDef.NamedSet) value;
570                if (isEmpty(namedSet.name)) {
571                    return nameMustBeSet;
572                }
573                if (isEmpty(namedSet.formula)
574                    && namedSet.formulaElement == null)
575                {
576                    return messages.getString(
577                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
578                        "Formula must be set");
579                }
580            } else if (value instanceof MondrianGuiDef.Formula) {
581                final MondrianGuiDef.Formula formula =
582                    (MondrianGuiDef.Formula) value;
583                if (isEmpty(formula.cdata)) {
584                    return messages.getString(
585                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
586                        "Formula must be set");
587                }
588            } else if (value instanceof MondrianGuiDef.UserDefinedFunction) {
589                final MondrianGuiDef.UserDefinedFunction udf =
590                    (MondrianGuiDef.UserDefinedFunction) value;
591                if (isEmpty(udf.name)) {
592                    return nameMustBeSet;
593                }
594                if (isEmpty(udf.className)
595                    && udf.script == null)
596                {
597                    return messages.getString(
598                        "Either a Class Name or a Script are required",
599                        "Class name must be set");
600                }
601            } else if (value instanceof MondrianGuiDef.MemberFormatter) {
602                final MondrianGuiDef.MemberFormatter f =
603                    (MondrianGuiDef.MemberFormatter) value;
604                if (isEmpty(f.className)
605                    && f.script == null)
606                {
607                    return messages.getString(
608                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
609                        "Either a Class Name or a Script are required");
610                }
611            } else if (value instanceof MondrianGuiDef.CellFormatter) {
612                final MondrianGuiDef.CellFormatter f =
613                    (MondrianGuiDef.CellFormatter) value;
614                if (isEmpty(f.className)
615                    && f.script == null)
616                {
617                    return messages.getString(
618                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
619                        "Either a Class Name or a Script are required");
620                }
621            } else if (value instanceof MondrianGuiDef.PropertyFormatter) {
622                final MondrianGuiDef.PropertyFormatter f =
623                    (MondrianGuiDef.PropertyFormatter) value;
624                if (isEmpty(f.className)
625                    && f.script == null)
626                {
627                    return messages.getString(
628                        "schemaTreeCellRenderer.classNameOrScriptRequired.alert",
629                        "Either a Class Name or a Script are required");
630                }
631            } else if (value instanceof MondrianGuiDef.CalculatedMember) {
632                final MondrianGuiDef.CalculatedMember calculatedMember =
633                    (MondrianGuiDef.CalculatedMember) value;
634                if (isEmpty(calculatedMember.name)) {
635                    return nameMustBeSet;
636                }
637                if (isEmpty(calculatedMember.dimension)) {
638                    return messages.getString(
639                        "schemaTreeCellRenderer.dimensionMustBeSet.alert",
640                        "Dimension must be set");
641                }
642                if (isEmpty(calculatedMember.formula)
643                    && calculatedMember.formulaElement == null)
644                {
645                    return messages.getString(
646                        "schemaTreeCellRenderer.formulaMustBeSet.alert",
647                        "Formula must be set");
648                }
649            } else if (value instanceof MondrianGuiDef.Join) {
650                final MondrianGuiDef.Join join = (MondrianGuiDef.Join) value;
651                if (isEmpty(join.leftKey)) {
652                    return messages.getString(
653                        "schemaTreeCellRenderer.leftKeyMustBeSet.alert",
654                        "Left key must be set");
655                }
656                if (isEmpty(join.rightKey)) {
657                    return messages.getString(
658                        "schemaTreeCellRenderer.rightKeyMustBeSet.alert",
659                        "Right key must be set");
660                }
661            } else if (value instanceof MondrianGuiDef.Table) {
662                final MondrianGuiDef.Table table = (MondrianGuiDef.Table) value;
663                String tableName = table.name;
664                if (!jdbcValidator.isTableExists(null, tableName)) {
665                    return messages.getFormattedString(
666                        "schemaTreeCellRenderer.tableDoesNotExist.alert",
667                        "Table {0} does not exist in database",
668                        tableName);
669                }
670
671                String theSchema = table.schema;
672                if (!isEmpty(theSchema)
673                    && !jdbcValidator.isSchemaExists(theSchema))
674                {
675                    return messages.getFormattedString(
676                        "schemaTreeCellRenderer.schemaDoesNotExist.alert",
677                        "Schema {0} does not exist",
678                        theSchema);
679                }
680                if (isEmpty(theSchema) && isSchemaRequired) {
681                    return messages.getString(
682                        "schemaTreeCellRenderer.schemaMustBeSet.alert",
683                        "Schema must be set");
684                }
685            }
686        }
687
688        // Step 2: check validity of all child objects for this value object.
689        int childCnt = treeModel.getChildCount(value);
690        for (int i = 0; i < childCnt; i++) {
691            Object child = treeModel.getChild(value, i);
692            String childErrMsg;
693            if (child instanceof MondrianGuiDef.Cube) {
694                // check current cube child and its children
695                childErrMsg = invalid(
696                    messages,
697                    jdbcValidator,
698                    treeModel,
699                    tpath,
700                    child,
701                    (MondrianGuiDef.Cube) child,
702                    parentDimension,
703                    parentHierarchy,
704                    parentLevel,
705                    isSchemaRequired);
706            } else if (child instanceof MondrianGuiDef.Dimension) {
707                // check the current hierarchy and its children
708                childErrMsg = invalid(
709                    messages,
710                    jdbcValidator,
711                    treeModel,
712                    tpath,
713                    child,
714                    cube,
715                    (MondrianGuiDef.Dimension) child,
716                    parentHierarchy,
717                    parentLevel,
718                    isSchemaRequired);
719            } else if (child instanceof MondrianGuiDef.Hierarchy) {
720                // special check for cube dimension where foreign key is blank :
721                // allowed/not allowed
722                if (value instanceof MondrianGuiDef.Dimension
723                    && cube != null
724                    && ((MondrianGuiDef.Hierarchy) child).relation != null)
725                {
726                    if (isEmpty(
727                            ((MondrianGuiDef.Dimension) value).foreignKey))
728                    {
729                        // check foreignkey is not blank;
730                        // if relation is null, foreignkey must be specified
731                        return messages.getString(
732                            "schemaTreeCellRenderer.foreignKeyMustBeSet.alert",
733                            "Foreign key must be set");
734                    }
735                }
736                // check the current hierarchy and its children
737                childErrMsg = invalid(
738                    messages,
739                    jdbcValidator,
740                    treeModel,
741                    tpath,
742                    child,
743                    cube,
744                    parentDimension,
745                    (MondrianGuiDef.Hierarchy) child,
746                    parentLevel,
747                    isSchemaRequired);
748            } else if (child instanceof MondrianGuiDef.Level) {
749                // check the current hierarchy and its children
750                childErrMsg = invalid(
751                    messages,
752                    jdbcValidator,
753                    treeModel,
754                    tpath,
755                    child,
756                    cube,
757                    parentDimension,
758                    parentHierarchy,
759                    (MondrianGuiDef.Level) child,
760                    isSchemaRequired);
761            } else {
762                // check this child and all its children objects with incoming
763                // cube and hierarchy
764                childErrMsg = invalid(
765                    messages,
766                    jdbcValidator,
767                    treeModel,
768                    tpath,
769                    child,
770                    cube,
771                    parentDimension,
772                    parentHierarchy,
773                    parentLevel,
774                    isSchemaRequired);
775            }
776
777            // If all children are valid then do a special check.
778            // Special check for cubes to see if their child dimensions have
779            // foreign key set and set the childErrMsg with error msg
780            /* === Begin : disabled
781            if (childErrMsg == null) {  // all children are valid
782                if (child instanceof MondrianGuiDef.Cube) {
783                    MondrianGuiDef.Cube c = (MondrianGuiDef.Cube) child;
784                    MondrianGuiDef.CubeDimension [] ds = c.dimensions;
785                    for (int j=0; j<ds.length; j++) {
786                        MondrianGuiDef.CubeDimension d =
787                            (MondrianGuiDef.CubeDimension) ds[j];
788                        if (d instanceof MondrianGuiDef.DimensionUsage) {
789                            continue;   // check the next dimension.
790                        }
791
792                        // check foreignkey is not blank
793                        if(isEmpty(d.foreignKey)) {
794                            childErrMsg = "ForeignKey" + emptyMsg;
795                            break;
796                        }
797
798                        // database validity check, if database connection is
799                        // successful
800                        if (jdbcMetaData.getErrMsg() == null) {
801                            String foreignKey = d.foreignKey;
802                            if (! jdbcMetaData.isColExists(
803                                ((MondrianGuiDef.Table) c.fact).schema,
804                                 ((MondrianGuiDef.Table) c.fact).name,
805                                  foreignKey))
806                            {
807                                childErrMsg =
808                                 "ForeignKey '" + foreignKey +
809                                  "' does not exist in fact table.";
810                                break;
811                            }
812                           // check foreignKey is a fact table column
813                            if (! allcols.contains(foreignKey)) {
814                               childErrMsg =
815                                "ForeignKey '" + foreignKey
816                                + "' does not exist in fact table.";
817                                break;
818                            }
819             * /
820                        }
821                    }
822                }
823            }
824             * === End : disabled
825             */
826            // Now set the final errormsg
827            if (childErrMsg != null) {
828                String childClassName = child.getClass().getName();
829                String simpleName[] = childClassName.split("[$.]", 0);
830                String childName;
831                try {
832                    Field f = child.getClass().getField("name");
833                    childName = (String) f.get(child);
834                    if (childName == null) {
835                        childName = "";
836                    }
837                    childErrMsg = messages.getFormattedString(
838                        "schemaTreeCellRenderer.childErrorMessageWithName.alert",
839                        "{0} {1} is invalid",
840                        simpleName[simpleName.length - 1],
841                        childName);
842                } catch (Exception ex) {
843                    childErrMsg = messages.getFormattedString(
844                        "schemaTreeCellRenderer.childErrorExceptionMessage.alert",
845                        "{0} is invalid",
846                        simpleName[simpleName.length - 1]);
847                }
848                return childErrMsg;
849            }
850        }
851
852        return null;
853    }
854
855    /**
856     * Returns whether an object is null or the empty string.
857     *
858     * @param v Object
859     * @return Whether object is null or the empty string
860     */
861    public static boolean isEmpty(String v) {
862        return (v == null) || v.equals("");
863    }
864
865    /**
866     * Returns whether an array is null or empty
867     *
868     * @param arr array
869     * @return whether the array is null or empty
870     */
871    public static boolean isEmpty(Object[] arr) {
872        return arr == null || arr.length == 0;
873    }
874
875    /**
876     * Validates a column, and returns an error message if it is invalid.
877     *
878     * @param column Column
879     * @param fieldName Field name
880     * @param messages Message provider
881     * @param level  Level
882     * @param jdbcValidator JDBC validator
883     * @param cube Cube
884     * @param parentHierarchy Hierarchy
885     * @return Error message if invalid, null if valid
886     */
887    private static String validateColumn(
888        String column,
889        String fieldName,
890        Messages messages,
891        MondrianGuiDef.Level level,
892        JdbcValidator jdbcValidator,
893        MondrianGuiDef.Cube cube,
894        MondrianGuiDef.Hierarchy parentHierarchy)
895    {
896        if (!isEmpty(column)) {
897            // database validity check, if database connection is successful
898            if (jdbcValidator.isInitialized()) {
899                // specified table for level's column
900                String table = level.table;
901                // If table has been changed in join then sets the table value
902                // to null to cause "tableMustBeSet" validation fail.
903                if (!isEmpty(table)
904                    && parentHierarchy != null
905                    && parentHierarchy.relation instanceof MondrianGuiDef.Join)
906                {
907                    TreeSet<String> joinTables = new TreeSet<String>();
908                    SchemaExplorer.getTableNamesForJoin(
909                        parentHierarchy.relation, joinTables);
910                    if (!joinTables.contains(table)) {
911                        return messages.getString(
912                            "schemaTreeCellRenderer.wrongTableValue",
913                            "Table value does not correspond to any join");
914                    }
915                }
916
917                if (!isEmpty(table)
918                    && parentHierarchy != null
919                    && parentHierarchy.relation instanceof MondrianGuiDef.Table)
920                {
921                    final MondrianGuiDef.Table parentTable =
922                        (MondrianGuiDef.Table) parentHierarchy.relation;
923                    MondrianGuiDef.Table theTable = parentTable;
924                    String compareTo =
925                        (theTable.alias != null
926                         && theTable.alias.trim().length() > 0)
927                        ? theTable.alias
928                        : theTable.name;
929                    if (!table.equals(compareTo)) {
930                        return messages.getString(
931                            "schemaTreeCellRenderer.tableDoesNotMatch",
932                            "Table value does not correspond to Hierarchy Relation");
933                    }
934                }
935
936                if (!isEmpty(table)
937                    && parentHierarchy != null
938                    && parentHierarchy.relation instanceof MondrianGuiDef.View)
939                {
940                    return messages.getString(
941                        "schemaTreeCellRenderer.noTableForView",
942                        "Table for column cannot be set in View");
943                }
944
945                if (isEmpty(table)) {
946                    if (parentHierarchy != null) {
947                        if (parentHierarchy.relation == null
948                            && cube != null)
949                        {
950                            // case of degenerate dimension within cube,
951                            // hierarchy table not specified
952                            if (!jdbcValidator.isColExists(
953                                    ((MondrianGuiDef.Table) cube.fact).schema,
954                                    ((MondrianGuiDef.Table) cube.fact).name,
955                                    column))
956                            {
957                                return messages.getFormattedString(
958                                    "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert",
959                                    "Degenerate dimension validation check - Column {0} does not exist in fact table",
960                                    column);
961                            }
962                        } else if (parentHierarchy.relation
963                                   instanceof MondrianGuiDef.Table)
964                        {
965                            final MondrianGuiDef.Table parentTable =
966                                (MondrianGuiDef.Table) parentHierarchy.relation;
967                            if (!jdbcValidator.isColExists(
968                                    parentTable.schema,
969                                    parentTable.name,
970                                    column))
971                            {
972                                return messages.getFormattedString(
973                                    "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
974                                    "Column {0} defined in field {1} does not exist in table {2}",
975                                    isEmpty(column.trim())
976                                        ? "' '"
977                                        : column,
978                                    fieldName,
979                                    parentTable.name);
980                            }
981                        } else if (parentHierarchy.relation
982                            instanceof MondrianGuiDef.Join)
983                        {
984                            // relation is join, table should be specified
985                            return messages.getString(
986                                "schemaTreeCellRenderer.tableMustBeSet.alert",
987                                "Table must be set");
988                        }
989                    }
990                } else {
991                    String schema = null;
992                    // if using Joins then gets the table name for isColExists
993                    // validation.
994                    if (parentHierarchy != null
995                        && parentHierarchy.relation
996                        instanceof MondrianGuiDef.Join)
997                    {
998                        String[] schemaAndTable =
999                            SchemaExplorer.getTableNameForAlias(
1000                                parentHierarchy.relation,
1001                                table);
1002                        schema = schemaAndTable[0];
1003                        table = schemaAndTable[1];
1004                    }
1005                    if (!jdbcValidator.isColExists(schema, table, column)) {
1006                        return messages.getFormattedString(
1007                            "schemaTreeCellRenderer.columnInTableDoesNotExist.alert",
1008                            "Column {0} defined in field {1} does not exist in table {2}",
1009                            isEmpty(column.trim())
1010                                ? "' '"
1011                                : column,
1012                            fieldName,
1013                            table);
1014                    }
1015                }
1016            }
1017        }
1018        return null;
1019    }
1020
1021}
1022
1023// End ValidationUtils.java