001 package mondrian.gui.validate; 002 003 import java.lang.reflect.Field; 004 005 import mondrian.gui.MondrianGuiDef; 006 007 /** 008 * Validates a <code>MondrianGuiDef</code>. Class contains <code>invalid</code> 009 * method formerly from <code>mondrian.gui.SchemaTreeCellRenderer</code>. 010 * 011 * @author mlowery 012 */ 013 public class ValidationUtils { 014 015 public static String invalid(Messages messages, JDBCValidator jdbcValidator, TreeModel treeModel, 016 TreeModelPath tpath, Object value, Object icube, Object iparentDimension, Object iparentHierarchy, 017 Object iparentLevel) { 018 //String errMsg = null; 019 String nameMustBeSet = messages.getString("schemaTreeCellRenderer.nameMustBeSet.alert", "Name must be set"); 020 021 MondrianGuiDef.Cube cube = (MondrianGuiDef.Cube) icube; //null; 022 MondrianGuiDef.Dimension parentDimension = (MondrianGuiDef.Dimension) iparentDimension; // null // used only by level to check for leveltype value 023 MondrianGuiDef.Hierarchy parentHierarchy = (MondrianGuiDef.Hierarchy) iparentHierarchy; //null; // used only by level validation 024 MondrianGuiDef.Level parentLevel = (MondrianGuiDef.Level) iparentLevel; // null // used only by property validation 025 026 if (!tpath.isEmpty()) { 027 int pathcount = tpath.getPathCount(); 028 for (int i = 0; i < pathcount 029 && (cube == null || parentDimension == null || parentHierarchy == null || parentLevel == null); i++) { 030 if (tpath.getPathComponent(i) instanceof MondrianGuiDef.Cube && cube == null) { 031 cube = (MondrianGuiDef.Cube) tpath.getPathComponent(i); 032 } 033 if (tpath.getPathComponent(i) instanceof MondrianGuiDef.Dimension && parentDimension == null) { 034 parentDimension = (MondrianGuiDef.Dimension) tpath.getPathComponent(i); 035 } 036 if (tpath.getPathComponent(i) instanceof MondrianGuiDef.Hierarchy && parentHierarchy == null) { 037 parentHierarchy = (MondrianGuiDef.Hierarchy) tpath.getPathComponent(i); 038 } 039 if (tpath.getPathComponent(i) instanceof MondrianGuiDef.Level && parentLevel == null) { 040 parentLevel = (MondrianGuiDef.Level) tpath.getPathComponent(i); 041 } 042 } 043 } 044 045 //Step 1: check validity of this value object 046 if (value instanceof MondrianGuiDef.Schema) { 047 if (isEmpty(((MondrianGuiDef.Schema) value).name)) { 048 return nameMustBeSet; 049 } 050 } else if (value instanceof MondrianGuiDef.VirtualCube) { 051 if (isEmpty(((MondrianGuiDef.VirtualCube) value).name)) { 052 return nameMustBeSet; 053 } 054 } else if (value instanceof MondrianGuiDef.VirtualCubeDimension) { 055 if (isEmpty(((MondrianGuiDef.VirtualCubeDimension) value).name)) { 056 return nameMustBeSet; 057 } 058 } else if (value instanceof MondrianGuiDef.VirtualCubeMeasure) { 059 if (isEmpty(((MondrianGuiDef.VirtualCubeMeasure) value).name)) { 060 return nameMustBeSet; 061 } 062 } else if (value instanceof MondrianGuiDef.Cube) { 063 if (isEmpty(((MondrianGuiDef.Cube) value).name)) { 064 return nameMustBeSet; 065 } 066 if (((MondrianGuiDef.Cube) value).fact == null 067 || isEmpty(((MondrianGuiDef.Table) ((MondrianGuiDef.Cube) value).fact).name)) //check name is not blank 068 { 069 return messages.getString("schemaTreeCellRenderer.factNameMustBeSet.alert", "Fact name must be set"); 070 } 071 072 // database validity check, if database connection is successful 073 if (jdbcValidator.isInitialized()) { 074 075 //Vector allTables = jdbcMetaData.getAllTables(((MondrianGuiDef.Table) ((MondrianGuiDef.Cube) value).fact).schema); 076 String schemaName = ((MondrianGuiDef.Table) ((MondrianGuiDef.Cube) value).fact).schema; 077 String factTable = ((MondrianGuiDef.Table) ((MondrianGuiDef.Cube) value).fact).name; 078 if (!jdbcValidator.isTableExists(schemaName, factTable)) { 079 return messages.getFormattedString("schemaTreeCellRenderer.factTableDoesNotExist.alert", 080 "Fact table {0} does not exist in database {1}", new String[] { factTable, 081 ((schemaName == null || schemaName.equals("")) ? "." : "schema " + schemaName) }); 082 } 083 } 084 } else if (value instanceof MondrianGuiDef.CubeDimension) { 085 if (isEmpty(((MondrianGuiDef.CubeDimension) value).name)) //check name is not blank 086 { 087 return nameMustBeSet; 088 } 089 if (value instanceof MondrianGuiDef.DimensionUsage) { 090 if (isEmpty(((MondrianGuiDef.DimensionUsage) value).source)) //check source is not blank 091 { 092 return messages.getString("schemaTreeCellRenderer.sourceMustBeSet.alert", "Source must be set"); 093 } 094 // check source is name of one of dimensions of schema (shared dimensions) 095 MondrianGuiDef.Schema s = (MondrianGuiDef.Schema) treeModel.getRoot(); 096 MondrianGuiDef.Dimension ds[] = s.dimensions; 097 String sourcename = ((MondrianGuiDef.DimensionUsage) value).source; 098 boolean notfound = true; 099 for (int j = 0; j < ds.length; j++) { 100 if (ds[j].name.equalsIgnoreCase(sourcename)) { 101 notfound = false; 102 break; 103 } 104 } 105 if (notfound) { 106 return messages.getFormattedString( 107 "schemaTreeCellRenderer.sourceInSharedDimensionDoesNotExist.alert", 108 "Source {0} does not exist as Shared Dimension of Schema", new String[] { sourcename }); 109 } 110 } 111 if (value instanceof MondrianGuiDef.Dimension && cube != null) { 112 /* //foreignkey can be blank if hierarchy relation is null 113 * // this check moved to child hierarchies relation check below 114 */ 115 if (!isEmpty(((MondrianGuiDef.Dimension) value).foreignKey)) { 116 // database validity check, if database connection is successful 117 if (jdbcValidator.isInitialized()) { 118 119 String foreignKey = ((MondrianGuiDef.Dimension) value).foreignKey; 120 if (!jdbcValidator.isColExists(((MondrianGuiDef.Table) cube.fact).schema, 121 ((MondrianGuiDef.Table) cube.fact).name, foreignKey)) { 122 return messages.getFormattedString("schemaTreeCellRenderer.foreignKeyDoesNotExist.alert", 123 "foreignKey {0} does not exist in fact table", new String[] { foreignKey }); 124 } 125 } 126 } 127 } 128 } else if (value instanceof MondrianGuiDef.Level) { 129 /* 130 // check 'column' exists in 'table' if table is specified otherwise :: case of join 131 // it should exist in relation table if it is specified otherwise :: case of table 132 // it should exist in fact table :: case of degenerate dimension where dimension columns exist in fact table 133 // and there is no separate table 134 */ 135 MondrianGuiDef.Level l = (MondrianGuiDef.Level) value; 136 if (!isEmpty(l.levelType)) { 137 // empty leveltype is treated as default value of "Regular"" which is ok with standard/time dimension 138 if (parentDimension != null) { 139 if ((isEmpty(parentDimension.type) || parentDimension.type.equals("StandardDimension")) 140 && !isEmpty(l.levelType) 141 && (!l.levelType.equals(MondrianGuiDef.Level._levelType_values[0]))) { 142 // if dimension type is 'standard' then leveltype should be 'regular' 143 return messages.getFormattedString("schemaTreeCellRenderer.levelUsedOnlyInTimeDimension.alert", 144 "levelType {0} can only be used with a TimeDimension", new String[] { l.levelType }); 145 } else if (!isEmpty(parentDimension.type) && (parentDimension.type.equals("TimeDimension")) 146 && !isEmpty(l.levelType) && (l.levelType.equals(MondrianGuiDef.Level._levelType_values[0]))) { 147 // if dimension type is 'time' then leveltype value could be 'timeyears', 'timedays' etc' 148 return messages 149 .getFormattedString("schemaTreeCellRenderer.levelUsedOnlyInStandardDimension.alert", 150 "levelType {0} can only be used with a StandardDimension", 151 new String[] { l.levelType }); 152 } 153 } 154 } 155 String column = l.column; // check level's column is in fact table' 156 /* // level column may be blank, if it has properties defined with cols. 157 if (isEmpty(column)) { 158 return "Column" + emptyMsg; 159 } 160 */ 161 if (isEmpty(column)) { 162 if (l.properties == null || l.properties.length == 0) { 163 return messages.getString("schemaTreeCellRenderer.columnMustBeSet.alert", "Column must be set"); 164 } 165 } else { 166 // database validity check, if database connection is successful 167 if (jdbcValidator.isInitialized()) { 168 String table = l.table; // specified table for level's column' 169 if (isEmpty(table)) { 170 if (parentHierarchy != null) { 171 if (parentHierarchy.relation == null && cube != null) { // case of degenerate dimension within cube, hierarchy table not specified 172 if (!jdbcValidator.isColExists(((MondrianGuiDef.Table) cube.fact).schema, 173 ((MondrianGuiDef.Table) cube.fact).name, column)) { 174 return messages 175 .getFormattedString( 176 "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert", 177 "Degenerate dimension validation check - Column {0} does not exist in fact table", 178 new String[] { column }); 179 } 180 } else if (parentHierarchy.relation instanceof MondrianGuiDef.Table) { 181 if (!jdbcValidator.isColExists( 182 ((MondrianGuiDef.Table) parentHierarchy.relation).schema, 183 ((MondrianGuiDef.Table) parentHierarchy.relation).name, column)) { 184 return messages.getFormattedString( 185 "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert", 186 "Column {0} does not exist in Dimension table", 187 new String[] { ((MondrianGuiDef.Table) parentHierarchy.relation).name }); 188 } 189 } else if (parentHierarchy.relation instanceof MondrianGuiDef.Join) { // relation is join, table should be specified 190 return messages.getString("schemaTreeCellRenderer.tableMustBeSet.alert", 191 "Table must be set"); 192 } 193 } 194 } else { 195 if (!jdbcValidator.isColExists(null, table, column)) { 196 return messages.getFormattedString( 197 "schemaTreeCellRenderer.columnInTableDoesNotExist.alert", 198 "Column {0} does not exist in table {1}", new String[] { column, table }); 199 } 200 } 201 } 202 } 203 } else if (value instanceof MondrianGuiDef.Property) { 204 /* 205 // check 'column' exists in 'table' if [level table] is specified otherwise :: case of join 206 // it should exist in [hierarchy relation table] if it is specified otherwise :: case of table 207 // it should exist in [fact table] :: case of degenerate dimension where dimension columns exist in fact table 208 // and there is no separate table 209 */ 210 MondrianGuiDef.Property p = (MondrianGuiDef.Property) value; 211 String column = p.column; // check property's column is in table' 212 if (isEmpty(column)) { 213 return messages.getString("schemaTreeCellRenderer.columnMustBeSet.alert", "Column must be set"); 214 } 215 // database validity check, if database connection is successful 216 if (jdbcValidator.isInitialized()) { 217 String table = null; 218 if (parentLevel != null) { 219 table = parentLevel.table; // specified table for level's column' 220 } 221 if (isEmpty(table)) { 222 if (parentHierarchy != null) { 223 if (parentHierarchy.relation == null && cube != null) { // case of degenerate dimension within cube, hierarchy table not specified 224 if (!jdbcValidator.isColExists(((MondrianGuiDef.Table) cube.fact).schema, 225 ((MondrianGuiDef.Table) cube.fact).name, column)) { 226 return messages 227 .getFormattedString( 228 "schemaTreeCellRenderer.degenDimensionColumnDoesNotExist.alert", 229 "Degenerate dimension validation check - Column {0} does not exist in fact table", 230 new String[] { column }); 231 } 232 } else if (parentHierarchy.relation instanceof MondrianGuiDef.Table) { 233 if (!jdbcValidator.isColExists(((MondrianGuiDef.Table) parentHierarchy.relation).schema, 234 ((MondrianGuiDef.Table) parentHierarchy.relation).name, column)) { 235 return messages.getFormattedString( 236 "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert", 237 "Column {0} does not exist in Dimension table", 238 new String[] { ((MondrianGuiDef.Table) parentHierarchy.relation).name }); 239 } 240 } 241 } 242 } else { 243 if (!jdbcValidator.isColExists(null, table, column)) { 244 return messages.getFormattedString( 245 "schemaTreeCellRenderer.columnInDimensionDoesNotExist.alert", 246 "Column {0} does not exist in Level table {1}", new String[] { column, table }); 247 } 248 } 249 } 250 } else if (value instanceof MondrianGuiDef.Measure) { 251 if (isEmpty(((MondrianGuiDef.Measure) value).name)) { 252 return nameMustBeSet; 253 } 254 if (isEmpty(((MondrianGuiDef.Measure) value).aggregator)) { 255 return messages.getString("schemaTreeCellRenderer.aggregatorMustBeSet.alert", "Aggregator must be set"); 256 } 257 if (((MondrianGuiDef.Measure) value).measureExp != null) { 258 // Measure expressions are OK 259 } else if (isEmpty(((MondrianGuiDef.Measure) value).column)) { 260 return messages.getString("schemaTreeCellRenderer.columnMustBeSet.alert", "Column must be set"); 261 } else if (cube != null && cube.fact != null) { 262 263 // database validity check, if database connection is successful 264 if (jdbcValidator.isInitialized()) { 265 266 //Vector allcols = jdbcMetaData.getAllColumns(((MondrianGuiDef.Table) cube.fact).schema, ((MondrianGuiDef.Table) cube.fact).name); 267 268 String column = ((MondrianGuiDef.Measure) value).column; 269 if (jdbcValidator.isColExists(((MondrianGuiDef.Table) cube.fact).schema, 270 ((MondrianGuiDef.Table) cube.fact).name, column)) { 271 /* disabled check that the column value should exist in table because column could also be an expression 272 if (! jdbcMetaData.isColExists(((MondrianGuiDef.Table) cube.fact).schema, ((MondrianGuiDef.Table) cube.fact).name, column)) { 273 return "Column '"+column+"' does not exist in fact table."; 274 } 275 */ 276 /* 277 if (! allcols.contains(column)) // check foreignKey is a fact table column 278 { return "Column '"+column+"' does not exist in fact table.";} 279 */ 280 // check for aggregator type only if column exists in table 281 // check if aggregator selected is valid on the data type of the column selected. 282 int colType = jdbcValidator.getColumnDataType(((MondrianGuiDef.Table) cube.fact).schema, 283 ((MondrianGuiDef.Table) cube.fact).name, ((MondrianGuiDef.Measure) value).column); 284 // colType of 2, 4,5, 7,8 is numeric types whereas 1, 12 are char varchar string and 91 is date type 285 int agIndex = -1; 286 if ("sum".equals(((MondrianGuiDef.Measure) value).aggregator) 287 || "avg".equals(((MondrianGuiDef.Measure) value).aggregator)) { 288 agIndex = 0; // aggregator = sum or avg, column should be numeric 289 } 290 if (!(agIndex == -1 || (colType >= 2 && colType <= 8))) { 291 return messages.getFormattedString( 292 "schemaTreeCellRenderer.aggregatorNotValidForColumn.alert", 293 "Aggregator {0} is not valid for the data type of the column {1}", new String[] { 294 ((MondrianGuiDef.Measure) value).aggregator, 295 ((MondrianGuiDef.Measure) value).column }); 296 } 297 } 298 } 299 } 300 } else if (value instanceof MondrianGuiDef.Hierarchy) { 301 if (((MondrianGuiDef.Hierarchy) value).relation instanceof MondrianGuiDef.Join) { 302 if (isEmpty(((MondrianGuiDef.Hierarchy) value).primaryKeyTable)) { 303 if (isEmpty(((MondrianGuiDef.Hierarchy) value).primaryKey)) { 304 return messages.getString("schemaTreeCellRenderer.primaryKeyTableAndPrimaryKeyMustBeSet.alert", 305 "PrimaryKeyTable and PrimaryKey must be set for Join"); 306 } else { 307 return messages.getString("schemaTreeCellRenderer.primaryKeyTableMustBeSet.alert", 308 "PrimaryKeyTable must be set for Join"); 309 } 310 } 311 if (isEmpty(((MondrianGuiDef.Hierarchy) value).primaryKey)) { 312 return messages.getString("schemaTreeCellRenderer.primaryKeyMustBeSet.alert", 313 "PrimaryKey must be set for Join"); 314 } 315 } 316 } else if (value instanceof MondrianGuiDef.NamedSet) { 317 if (isEmpty(((MondrianGuiDef.NamedSet) value).name)) { 318 return nameMustBeSet; 319 } 320 if (isEmpty(((MondrianGuiDef.NamedSet) value).formula)) { 321 return messages.getString("schemaTreeCellRenderer.formulaMustBeSet.alert", "Formula must be set"); 322 } 323 } else if (value instanceof MondrianGuiDef.UserDefinedFunction) { 324 if (isEmpty(((MondrianGuiDef.UserDefinedFunction) value).name)) { 325 return nameMustBeSet; 326 } 327 if (isEmpty(((MondrianGuiDef.UserDefinedFunction) value).className)) { 328 return messages.getString("schemaTreeCellRenderer.classNameMustBeSet.alert", "Class name must be set"); 329 } 330 } else if (value instanceof MondrianGuiDef.CalculatedMember) { 331 if (isEmpty(((MondrianGuiDef.CalculatedMember) value).name)) { 332 return nameMustBeSet; 333 } 334 if (isEmpty(((MondrianGuiDef.CalculatedMember) value).dimension)) { 335 return messages.getString("schemaTreeCellRenderer.dimensionMustBeSet.alert", "Dimension must be set"); 336 } 337 } else if (value instanceof MondrianGuiDef.Join) { 338 if (isEmpty(((MondrianGuiDef.Join) value).leftKey)) { 339 return messages.getString("schemaTreeCellRenderer.leftKeyMustBeSet.alert", "Left key must be set"); 340 } 341 if (isEmpty(((MondrianGuiDef.Join) value).rightKey)) { 342 return messages.getString("schemaTreeCellRenderer.rightKeyMustBeSet.alert", "Right key must be set"); 343 } 344 } 345 346 // Step 2: check validity of all child objects for this value object. 347 int childCnt = treeModel.getChildCount(value); 348 for (int i = 0; i < childCnt; i++) { 349 Object child = treeModel.getChild(value, i); 350 String childErrMsg; 351 if (child instanceof MondrianGuiDef.Cube) { 352 childErrMsg = invalid(messages, jdbcValidator, treeModel, tpath, child, child, parentDimension, 353 parentHierarchy, parentLevel); //check current cube child and its children 354 } else if (child instanceof MondrianGuiDef.Dimension) { 355 childErrMsg = invalid(messages, jdbcValidator, treeModel, tpath, child, cube, child, parentHierarchy, 356 parentLevel); //check the current hierarchy and its children 357 } else if (child instanceof MondrianGuiDef.Hierarchy) { 358 // special check for cube dimension where foreign key is blank : allowed /not allowed 359 if (value instanceof MondrianGuiDef.Dimension && cube != null 360 && ((MondrianGuiDef.Hierarchy) child).relation != null) { 361 if (isEmpty(((MondrianGuiDef.Dimension) value).foreignKey)) //check foreignkey is not blank 362 { 363 // if relation is null, foreignkey must be specified 364 365 return messages.getString("schemaTreeCellRenderer.foreignKeyMustBeSet.alert", 366 "Foreign key must be set"); 367 } 368 } 369 childErrMsg = invalid(messages, jdbcValidator, treeModel, tpath, child, cube, parentDimension, child, 370 parentLevel); //check the current hierarchy and its children 371 } else if (child instanceof MondrianGuiDef.Level) { 372 childErrMsg = invalid(messages, jdbcValidator, treeModel, tpath, child, cube, parentDimension, 373 parentHierarchy, child); //check the current hierarchy and its children 374 } else { 375 childErrMsg = invalid(messages, jdbcValidator, treeModel, tpath, child, cube, parentDimension, 376 parentHierarchy, parentLevel); //check this child and all its children objects with incoming cube and hierarchy 377 } 378 379 /* If all children are valid then do a special check. 380 * Special check for cubes to see if their child dimensions have foreign key set and set the childErrMsg with error msg 381 */ 382 /* === Begin : disabled 383 if (childErrMsg == null) { // all children are valid 384 if (child instanceof MondrianGuiDef.Cube) { 385 MondrianGuiDef.Cube c = (MondrianGuiDef.Cube) child; 386 MondrianGuiDef.CubeDimension [] ds = c.dimensions; 387 for (int j=0; j<ds.length; j++) { 388 MondrianGuiDef.CubeDimension d = (MondrianGuiDef.CubeDimension) ds[j]; 389 if (d instanceof MondrianGuiDef.DimensionUsage) { 390 continue; // check the next dimension. 391 } 392 393 if(isEmpty(d.foreignKey)) //check foreignkey is not blank 394 { childErrMsg = "ForeignKey" + emptyMsg; 395 break; 396 } 397 398 // database validity check, if database connection is successful 399 if (jdbcMetaData.getErrMsg() == null) { 400 401 //Vector allcols = jdbcMetaData.getAllColumns(((MondrianGuiDef.Table) c.fact).schema, ((MondrianGuiDef.Table) c.fact).name); 402 String foreignKey = d.foreignKey; 403 if (! jdbcMetaData.isColExists(((MondrianGuiDef.Table) c.fact).schema, ((MondrianGuiDef.Table) c.fact).name, foreignKey)) { 404 childErrMsg = "ForeignKey '"+foreignKey+"' does not exist in fact table."; 405 break; 406 } 407 /* 408 if (! allcols.contains(foreignKey)) // check foreignKey is a fact table column 409 { childErrMsg = "ForeignKey '"+foreignKey+"' does not exist in fact table."; 410 break; 411 } 412 * / 413 } 414 } 415 } 416 } 417 * === End : disabled 418 */ 419 // Now set the final errormsg 420 if (childErrMsg != null) { 421 String childClassName = child.getClass().getName(); 422 String simpleName[] = childClassName.split("[$.]", 0); 423 String childName; 424 try { 425 Field f = child.getClass().getField("name"); 426 childName = (String) f.get(child); 427 if (childName == null) { 428 childName = ""; 429 } 430 childErrMsg = messages.getFormattedString("schemaTreeCellRenderer.childErrorMessageWithName.alert", 431 "{0} {1} is invalid", new String[] { simpleName[simpleName.length - 1], childName }); 432 433 } catch (Exception ex) { 434 childErrMsg = messages.getFormattedString( 435 "schemaTreeCellRenderer.childErrorExceptionMessage.alert", "{0} is invalid", 436 new String[] { simpleName[simpleName.length - 1] }); 437 } 438 return childErrMsg; 439 } 440 } 441 442 return null; 443 } 444 445 private static boolean isEmpty(Object v) { 446 if ((v == null) || v.equals("")) { 447 return true; 448 } else { 449 return false; 450 } 451 } 452 453 } 454 455 // End ValidationUtils.java