KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > aggmatcher > ExplicitRules


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRules.java#13 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2005-2007 Julian Hyde and others
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10
11 package mondrian.rolap.aggmatcher;
12
13 import mondrian.olap.*;
14 import mondrian.rolap.*;
15 import mondrian.recorder.MessageRecorder;
16 import mondrian.resource.MondrianResource;
17
18 import org.apache.log4j.Logger;
19
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.util.*;
23 import java.util.regex.Pattern JavaDoc;
24
25 /**
26  * A class containing a RolapCube's Aggregate tables exclude/include
27  * criteria.
28  *
29  * @author Richard M. Emberson
30  * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRules.java#13 $
31  */

32 public class ExplicitRules {
33     private static final Logger LOGGER = Logger.getLogger(ExplicitRules.class);
34
35     private static final MondrianResource mres = MondrianResource.instance();
36
37     /**
38      * Returns whether the given is tableName explicitly excluded from
39      * consideration as a candidate aggregate table.
40      */

41     public static boolean excludeTable(
42             final String JavaDoc tableName,
43             final List<Group> aggGroups) {
44         for (Group group : aggGroups) {
45             if (group.excludeTable(tableName)) {
46                 return true;
47             }
48         }
49         return false;
50     }
51
52     /**
53      * Returns the {@link TableDef} for a tableName that is a candidate
54      * aggregate table. If null is returned, then the default rules are used
55      * otherwise if not null, then the ExplicitRules.TableDef is used.
56      */

57     public static ExplicitRules.TableDef getIncludeByTableDef(
58             final String JavaDoc tableName,
59             final List<Group> aggGroups) {
60         for (Group group : aggGroups) {
61             TableDef tableDef = group.getIncludeByTableDef(tableName);
62             if (tableDef != null) {
63                 return tableDef;
64             }
65         }
66         return null;
67     }
68
69     /**
70      * This class forms a collection of aggregate table explicit rules for a
71      * given cube.
72      *
73      */

74     public static class Group {
75
76         /**
77          * Make an ExplicitRules.Group for a given RolapCube given the
78          * MondrianDef.Cube associated with that cube.
79          */

80         public static ExplicitRules.Group make(
81                 final RolapCube cube,
82                 final MondrianDef.Cube xmlCube) {
83             Group group = new Group(cube);
84
85             MondrianDef.Relation relation = xmlCube.fact;
86
87             if (relation instanceof MondrianDef.Table) {
88                 MondrianDef.AggExclude[] aggExcludes =
89                     ((MondrianDef.Table) relation).getAggExcludes();
90                 if (aggExcludes != null) {
91                     for (MondrianDef.AggExclude aggExclude : aggExcludes) {
92                         Exclude exclude =
93                             ExplicitRules.make(aggExclude);
94                         group.addExclude(exclude);
95                     }
96                 }
97                 MondrianDef.AggTable[] aggTables =
98                     ((MondrianDef.Table) relation).getAggTables();
99                 if (aggTables != null) {
100                     for (MondrianDef.AggTable aggTable : aggTables) {
101                         TableDef tableDef = TableDef.make(aggTable, group);
102                         group.addTableDef(tableDef);
103                     }
104                 }
105             } else {
106                 String JavaDoc msg = mres.CubeRelationNotTable.str(
107                         cube.getName(),
108                         relation.getClass().getName());
109                 LOGGER.warn(msg);
110             }
111
112             if (LOGGER.isDebugEnabled()) {
113                 LOGGER.debug(Util.nl+group);
114             }
115             return group;
116         }
117
118         private final RolapCube cube;
119         private List<TableDef> tableDefs;
120         private List<Exclude> excludes;
121
122         public Group(final RolapCube cube) {
123             this.cube = cube;
124             this.excludes = Collections.emptyList();
125             this.tableDefs = Collections.emptyList();
126         }
127
128         /**
129          * Get the RolapCube associated with this Group.
130          */

131         public RolapCube getCube() {
132             return cube;
133         }
134
135         /**
136          * Get the RolapStar associated with this Group's RolapCube.
137          */

138         public RolapStar getStar() {
139             return getCube().getStar();
140         }
141
142         /**
143          * Get the name of this Group (its the name of its RolapCube).
144          */

145         public String JavaDoc getName() {
146             return getCube().getName();
147         }
148
149         /**
150          * Are there any rules associated with this Group.
151          */

152         public boolean hasRules() {
153             return (excludes != Collections.EMPTY_LIST) ||
154                 (tableDefs != Collections.EMPTY_LIST);
155         }
156
157         /**
158          * Add an exclude rule.
159          *
160          * @param exclude
161          */

162         public void addExclude(final ExplicitRules.Exclude exclude) {
163             if (excludes == Collections.EMPTY_LIST) {
164                 excludes = new ArrayList<Exclude>();
165             }
166             excludes.add(exclude);
167         }
168
169         /**
170          * Add a name or pattern (table) rule.
171          *
172          * @param tableDef
173          */

174         public void addTableDef(final ExplicitRules.TableDef tableDef) {
175             if (tableDefs == Collections.EMPTY_LIST) {
176                 tableDefs = new ArrayList<TableDef>();
177             }
178             tableDefs.add(tableDef);
179         }
180
181         /**
182          * Returns whether the given tableName is excluded.
183          */

184         public boolean excludeTable(final String JavaDoc tableName) {
185             // See if the table is explicitly, by name, excluded
186
for (Exclude exclude : excludes) {
187                 if (exclude.isExcluded(tableName)) {
188                     return true;
189                 }
190             }
191             return false;
192         }
193
194         /**
195          * Is the given tableName included either by exact name or by pattern.
196          */

197         public ExplicitRules.TableDef getIncludeByTableDef(
198                 final String JavaDoc tableName) {
199             // An exact match on a NameTableDef takes precedences over a
200
// fuzzy match on a PatternTableDef, so
201
// first look throught NameTableDef then PatternTableDef
202
for (ExplicitRules.TableDef tableDef : tableDefs) {
203                 if (tableDef instanceof NameTableDef) {
204                     if (tableDef.matches(tableName)) {
205                         return tableDef;
206                     }
207                 }
208             }
209             for (ExplicitRules.TableDef tableDef : tableDefs) {
210                 if (tableDef instanceof PatternTableDef) {
211                     if (tableDef.matches(tableName)) {
212                         return tableDef;
213                     }
214                 }
215             }
216             return null;
217         }
218
219         /**
220          * Get the database table name associated with this Group's RolapStar's
221          * fact table.
222          */

223         public String JavaDoc getTableName() {
224             RolapStar.Table table = getStar().getFactTable();
225             MondrianDef.Relation relation = table.getRelation();
226             return relation.getAlias();
227         }
228
229         /**
230          * Get the database schema name associated with this Group's RolapStar's
231          * fact table.
232          */

233         public String JavaDoc getSchemaName() {
234             String JavaDoc schema = null;
235
236             RolapStar.Table table = getStar().getFactTable();
237             MondrianDef.Relation relation = table.getRelation();
238
239             if (relation instanceof MondrianDef.Table) {
240                 MondrianDef.Table mtable = (MondrianDef.Table) relation;
241                 schema = mtable.schema;
242             }
243             return schema;
244         }
245         /**
246          * Get the database catalog name associated with this Group's
247          * RolapStar's fact table.
248          * Note: this currently this always returns null.
249          */

250         public String JavaDoc getCatalogName() {
251             return null;
252         }
253
254         /**
255          * Validate the content and structure of this Group.
256          */

257         public void validate(final MessageRecorder msgRecorder) {
258             msgRecorder.pushContextName(getName());
259             try {
260                 for (ExplicitRules.TableDef tableDef : tableDefs) {
261                     tableDef.validate(msgRecorder);
262                 }
263             } finally {
264                 msgRecorder.popContextName();
265             }
266         }
267
268         public String JavaDoc toString() {
269             StringWriter JavaDoc sw = new StringWriter JavaDoc(256);
270             PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
271             print(pw, "");
272             pw.flush();
273             return sw.toString();
274         }
275
276         public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
277             pw.print(prefix);
278             pw.println("ExplicitRules.Group:");
279             String JavaDoc subprefix = prefix + " ";
280             String JavaDoc subsubprefix = subprefix + " ";
281
282             pw.print(subprefix);
283             pw.print("name=");
284             pw.println(getStar().getFactTable().getRelation());
285
286             pw.print(subprefix);
287             pw.println("TableDefs: [");
288             for (ExplicitRules.TableDef tableDef : tableDefs) {
289                 tableDef.print(pw, subsubprefix);
290             }
291             pw.print(subprefix);
292             pw.println("]");
293         }
294     }
295
296     private static Exclude make(final MondrianDef.AggExclude aggExclude) {
297         return (aggExclude.getNameAttribute() != null) ?
298                 new ExcludeName(
299                         aggExclude.getNameAttribute(),
300                         aggExclude.isIgnoreCase()) :
301                 (Exclude) new ExcludePattern(
302                         aggExclude.getPattern(),
303                         aggExclude.isIgnoreCase());
304     }
305
306     /**
307      * Interface of an Exclude type. There are two implementations, one that
308      * excludes by exact name match (as an option, ignore case) and the second
309      * that matches a regular expression.
310      */

311     private interface Exclude {
312         /**
313          * Return true if the tableName is exculed.
314          *
315          * @param tableName
316          * @return
317          */

318         boolean isExcluded(final String JavaDoc tableName);
319
320         void validate(final MessageRecorder msgRecorder);
321
322         void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix);
323     }
324
325     /**
326      * This class is an exact name matching Exclude implementation.
327      */

328     private static class ExcludeName implements Exclude {
329         private final String JavaDoc name;
330         private final boolean ignoreCase;
331
332         private ExcludeName(final String JavaDoc name, final boolean ignoreCase) {
333             this.name = name;
334             this.ignoreCase = ignoreCase;
335         }
336
337         /**
338          * Get the name that is to be matched.
339          */

340         public String JavaDoc getName() {
341             return name;
342         }
343
344         /**
345          * Returns true if the matching can ignore case.
346          */

347         public boolean isIgnoreCase() {
348             return ignoreCase;
349         }
350
351         /**
352          * Return true if the tableName is exculed.
353          *
354          * @param tableName
355          * @return
356          */

357         public boolean isExcluded(final String JavaDoc tableName) {
358             return (this.ignoreCase)
359                 ? this.name.equals(tableName)
360                 : this.name.equalsIgnoreCase(tableName);
361         }
362
363         /**
364          * Validate that the exclude name matches the table pattern.
365          *
366          * @param msgRecorder
367          */

368         public void validate(final MessageRecorder msgRecorder) {
369             msgRecorder.pushContextName("ExcludeName");
370             try {
371                 String JavaDoc name = getName();
372                 checkAttributeString(msgRecorder, name, "name");
373
374 /*
375 RME TODO
376                 // If name does not match the PatternTableDef pattern,
377                 // then issue warning.
378                 // Why, because no table with the exclude's name will
379                 // ever match the pattern, so the exclude is superfluous.
380                 // This is best effort.
381                 Pattern pattern = ExplicitRules.PatternTableDef.this.getPattern();
382                 boolean patternIgnoreCase =
383                             ExplicitRules.PatternTableDef.this.isIgnoreCase();
384                 boolean ignoreCase = isIgnoreCase();
385
386                 // If pattern is ignoreCase and name is any case or pattern
387                 // is not ignoreCase and name is not ignoreCase, then simply
388                 // see if name matches.
389                 // Else pattern in not ignoreCase and name is ignoreCase,
390                 // then pattern could be "AB.*" and name "abc".
391                 // Here "abc" would name, but not pattern - but who cares
392                 if (patternIgnoreCase || ! ignoreCase) {
393                     if (! pattern.matcher(name).matches()) {
394                         msgRecorder.reportWarning(
395                             mres.getSuperfluousExludeName(
396                                         msgRecorder.getContext(),
397                                         name,
398                                         pattern.pattern()));
399                     }
400                 }
401 */

402             } finally {
403                 msgRecorder.popContextName();
404             }
405         }
406
407         public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
408             pw.print(prefix);
409             pw.println("ExplicitRules.PatternTableDef.ExcludeName:");
410
411             String JavaDoc subprefix = prefix + " ";
412
413             pw.print(subprefix);
414             pw.print("name=");
415             pw.println(this.name);
416
417             pw.print(subprefix);
418             pw.print("ignoreCase=");
419             pw.println(this.ignoreCase);
420         }
421     }
422
423     /**
424      * This class is a regular expression base name matching Exclude
425      * implementation.
426      */

427     private static class ExcludePattern implements Exclude {
428         private final Pattern JavaDoc pattern;
429
430         private ExcludePattern(
431                 final String JavaDoc pattern,
432                 final boolean ignoreCase) {
433             this.pattern = (ignoreCase) ?
434                     Pattern.compile(pattern, Pattern.CASE_INSENSITIVE) :
435                     Pattern.compile(pattern);
436         }
437
438         /**
439          * Return true if the tableName is exculed.
440          *
441          * @param tableName
442          * @return
443          */

444         public boolean isExcluded(final String JavaDoc tableName) {
445             return pattern.matcher(tableName).matches();
446         }
447
448         /**
449          * Validate that the exclude pattern overlaps with table pattern.
450          *
451          * @param msgRecorder
452          */

453         public void validate(final MessageRecorder msgRecorder) {
454             msgRecorder.pushContextName("ExcludePattern");
455             try {
456                 checkAttributeString(
457                         msgRecorder,
458                         pattern.pattern(),
459                         "pattern");
460                 //String context = msgRecorder.getContext();
461
// Is there any way to determine if the exclude pattern
462
// is never a sub-set of the table pattern.
463
// I will have to think about this.
464
// Until then, this method is empty.
465
} finally {
466                 msgRecorder.popContextName();
467             }
468         }
469
470         public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
471             pw.print(prefix);
472             pw.println("ExplicitRules.PatternTableDef.ExcludePattern:");
473
474             String JavaDoc subprefix = prefix + " ";
475
476             pw.print(subprefix);
477             pw.print("pattern=");
478             pw.print(this.pattern.pattern());
479             pw.print(":");
480             pw.println(this.pattern.flags());
481         }
482     }
483
484     /**
485      * This is the base class for the exact name based and name pattern based
486      * aggregate table mapping definitions. It contains the mappings for the
487      * fact count column, optional ignore columns, foreign key mappings,
488      * measure column mappings and level column mappings.
489      */

490     public static abstract class TableDef {
491
492         /**
493          * Given a MondrianDef.AggTable instance create a TableDef instance
494          * which is either a NameTableDef or PatternTableDef.
495          */

496         static ExplicitRules.TableDef make(
497                 final MondrianDef.AggTable aggTable,
498                 final ExplicitRules.Group group) {
499             return (aggTable instanceof MondrianDef.AggName) ?
500                     ExplicitRules.NameTableDef.make(
501                             (MondrianDef.AggName) aggTable, group) :
502                     (ExplicitRules.TableDef)
503                     ExplicitRules.PatternTableDef.make(
504                             (MondrianDef.AggPattern) aggTable, group);
505         }
506
507         /**
508          * This method extracts information from the MondrianDef.AggTable and
509          * places it in the ExplicitRules.TableDef. This code is used for both
510          * the NameTableDef and PatternTableDef subclasses of TableDef (it
511          * extracts information common to both).
512          *
513          * @param tableDef
514          * @param aggTable
515          */

516         private static void add(
517                 final ExplicitRules.TableDef tableDef,
518                 final MondrianDef.AggTable aggTable) {
519
520             if (aggTable.getAggFactCount() != null) {
521                 tableDef.setFactCountName(aggTable.getAggFactCount().getColumnName());
522             }
523
524             MondrianDef.AggIgnoreColumn[] ignores =
525                     aggTable.getAggIgnoreColumns();
526
527             if (ignores != null) {
528                 for (MondrianDef.AggIgnoreColumn ignore : ignores) {
529                     tableDef.addIgnoreColumnName(ignore.getColumnName());
530                 }
531             }
532
533             MondrianDef.AggForeignKey[] fks = aggTable.getAggForeignKeys();
534             if (fks != null) {
535                 for (MondrianDef.AggForeignKey fk : fks) {
536                     tableDef.addFK(fk);
537                 }
538             }
539             MondrianDef.AggMeasure[] measures = aggTable.getAggMeasures();
540             if (measures != null) {
541                 for (MondrianDef.AggMeasure measure : measures) {
542                     addTo(tableDef, measure);
543                 }
544             }
545
546             MondrianDef.AggLevel[] levels = aggTable.getAggLevels();
547             if (levels != null) {
548                 for (MondrianDef.AggLevel level : levels) {
549                     addTo(tableDef, level);
550                 }
551             }
552         }
553         private static void addTo(
554                 final ExplicitRules.TableDef tableDef,
555                 final MondrianDef.AggLevel aggLevel) {
556             addLevelTo(tableDef,
557                        aggLevel.getNameAttribute(),
558                        aggLevel.getColumnName());
559         }
560
561         private static void addTo(
562                 final ExplicitRules.TableDef tableDef,
563                 final MondrianDef.AggMeasure aggMeasure) {
564             addMeasureTo(tableDef,
565                          aggMeasure.getNameAttribute(),
566                          aggMeasure.getColumn());
567         }
568
569         public static void addLevelTo(
570                 final ExplicitRules.TableDef tableDef,
571                 final String JavaDoc name,
572                 final String JavaDoc columnName) {
573             Level level = tableDef.new Level(name, columnName);
574             tableDef.add(level);
575         }
576
577         public static void addMeasureTo(
578                 final ExplicitRules.TableDef tableDef,
579                 final String JavaDoc name,
580                 final String JavaDoc column) {
581             Measure measure = tableDef.new Measure(name, column);
582             tableDef.add(measure);
583         }
584
585         /**
586          * This class is used to map from a Level's symbolic name,
587          * [Time]&#46;[Year] to the aggregate table's column name, TIME_YEAR.
588          */

589         class Level {
590             private final String JavaDoc name;
591             private final String JavaDoc columnName;
592             private RolapLevel rlevel;
593
594             Level(final String JavaDoc name, final String JavaDoc columnName) {
595                 this.name = name;
596                 this.columnName = columnName;
597             }
598
599             /**
600              * Get the symbolic name, the level name.
601              */

602             public String JavaDoc getName() {
603                 return name;
604             }
605
606             /**
607              * Get the foreign key column name of the aggregate table.
608              */

609             public String JavaDoc getColumnName() {
610                 return columnName;
611             }
612
613             /**
614              * Get the RolapLevel associated with level name.
615              */

616             public RolapLevel getRolapLevel() {
617                 return rlevel;
618             }
619
620             /**
621              * Validates a level's name.
622              *
623              * <p>The level name must be of the form
624              * <blockquote><code>[hierarchy usage name].[level name]</code></blockquote>
625              *
626              * This method checks that is of length 2, starts with a hierarchy and
627              * the "level name" exists.
628              */

629             public void validate(final MessageRecorder msgRecorder) {
630                 msgRecorder.pushContextName("Level");
631                 try {
632                     String JavaDoc name = getName();
633                     String JavaDoc columnName = getColumnName();
634                     checkAttributeString(msgRecorder, name, "name");
635                     checkAttributeString(msgRecorder, columnName, "column");
636
637                     String JavaDoc[] names = Util.explode(name);
638                     // must be [hierarchy usage name].[level name]
639
if (names.length != 2) {
640                         msgRecorder.reportError(
641                             mres.BadLevelNameFormat.str(
642                                 msgRecorder.getContext(),
643                                 name));
644                     } else {
645
646                         RolapCube cube = ExplicitRules.TableDef.this.getCube();
647                         SchemaReader schemaReader = cube.getSchemaReader();
648                         RolapLevel level =
649                             (RolapLevel) schemaReader.lookupCompound(
650                                     cube,
651                                     names,
652                                     false,
653                                     Category.Level);
654                         if (level == null) {
655                             Hierarchy hierarchy = (Hierarchy)
656                                 schemaReader.lookupCompound(
657                                     cube,
658                                     new String JavaDoc[] { names[0] },
659                                     false,
660                                     Category.Hierarchy);
661                             if (hierarchy == null) {
662                                 msgRecorder.reportError(
663                                     mres.UnknownHierarchyName.str(
664                                         msgRecorder.getContext(),
665                                         names[0]));
666                             } else {
667                                 msgRecorder.reportError(
668                                     mres.UnknownLevelName.str(
669                                             msgRecorder.getContext(),
670                                             names[0],
671                                             names[1]));
672                             }
673                         }
674                         rlevel = level;
675                     }
676                 } finally {
677                     msgRecorder.popContextName();
678                 }
679             }
680
681             public String JavaDoc toString() {
682                 StringWriter JavaDoc sw = new StringWriter JavaDoc(256);
683                 PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
684                 print(pw, "");
685                 pw.flush();
686                 return sw.toString();
687             }
688
689             public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
690                 pw.print(prefix);
691                 pw.println("Level:");
692                 String JavaDoc subprefix = prefix + " ";
693
694                 pw.print(subprefix);
695                 pw.print("name=");
696                 pw.println(this.name);
697
698                 pw.print(subprefix);
699                 pw.print("columnName=");
700                 pw.println(this.columnName);
701             }
702         }
703
704         /**
705          * This class is used to map from a measure's symbolic name,
706          * [Measures]&#46;[Unit Sales] to the aggregate table's column
707          * name, UNIT_SALES_SUM.
708          */

709         class Measure {
710             private final String JavaDoc name;
711             private String JavaDoc symbolicName;
712             private final String JavaDoc columnName;
713             private RolapStar.Measure rolapMeasure;
714
715             Measure(final String JavaDoc name, final String JavaDoc columnName) {
716                 this.name = name;
717                 this.columnName = columnName;
718             }
719
720             /**
721              * Get the symbolic name, the measure name, i.e.,
722              * [Measures].[Unit Sales].
723              */

724             public String JavaDoc getName() {
725                 return name;
726             }
727
728             /**
729              * Get the symbolic name, the measure name, i.e., [Unit Sales].
730              */

731             public String JavaDoc getSymbolicName() {
732                 return symbolicName;
733             }
734
735             /**
736              * Get the aggregate table column name.
737              */

738             public String JavaDoc getColumnName() {
739                 return columnName;
740             }
741
742             /**
743              * Get the RolapStar.Measure associated with this symbolic name.
744              */

745             public RolapStar.Measure getRolapStarMeasure() {
746                 return rolapMeasure;
747             }
748
749             /**
750              * Validates a measure's name.
751              *
752              * <p>The measure name must be of the form
753              * <blockquote><code>[Measures].[measure name]</code></blockquote>
754              *
755              * <p>This method checks that is of length 2, starts with "Measures" and
756              * the "measure name" exists.
757              */

758             public void validate(final MessageRecorder msgRecorder) {
759                 msgRecorder.pushContextName("Measure");
760                 try {
761                     String JavaDoc name = getName();
762                     String JavaDoc column = getColumnName();
763                     checkAttributeString(msgRecorder, name, "name");
764                     checkAttributeString(msgRecorder, column, "column");
765
766                     String JavaDoc[] names = Util.explode(name);
767                     if (names.length != 2) {
768                         msgRecorder.reportError(
769                             mres.BadMeasureNameFormat.str(
770                                    msgRecorder.getContext(),
771                                    name));
772                     } else {
773                         RolapCube cube = ExplicitRules.TableDef.this.getCube();
774                         SchemaReader schemaReader = cube.getSchemaReader();
775                         Member member = (Member) schemaReader.lookupCompound(
776                                     cube,
777                                     names,
778                                     false,
779                                     Category.Member);
780                         if (member == null) {
781                             if (! names[0].equals("Measures")) {
782                                 msgRecorder.reportError(
783                                     mres.BadMeasures.str(
784                                         msgRecorder.getContext(),
785                                         names[0]));
786                             } else {
787                                 msgRecorder.reportError(
788                                     mres.UnknownMeasureName.str(
789                                             msgRecorder.getContext(),
790                                             names[1]));
791                             }
792                         }
793                         RolapStar star = cube.getStar();
794                         rolapMeasure =
795                             star.getFactTable().lookupMeasureByName(names[1]);
796                         if (rolapMeasure == null) {
797                             msgRecorder.reportError(
798                                     mres.BadMeasureName.str(
799                                        msgRecorder.getContext(),
800                                        names[1],
801                                        cube.getName()));
802                         }
803                         symbolicName = names[1];
804                     }
805                 } finally {
806                     msgRecorder.popContextName();
807                 }
808             }
809
810             public String JavaDoc toString() {
811                 StringWriter JavaDoc sw = new StringWriter JavaDoc(256);
812                 PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
813                 print(pw, "");
814                 pw.flush();
815                 return sw.toString();
816             }
817
818             public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
819                 pw.print(prefix);
820                 pw.println("Measure:");
821                 String JavaDoc subprefix = prefix + " ";
822
823                 pw.print(subprefix);
824                 pw.print("name=");
825                 pw.println(this.name);
826
827                 pw.print(subprefix);
828                 pw.print("column=");
829                 pw.println(this.columnName);
830             }
831         }
832
833         private static int idCount = 0;
834         private static int nextId() {
835             return idCount++;
836         }
837
838         protected final int id;
839         protected final boolean ignoreCase;
840         protected final ExplicitRules.Group aggGroup;
841         protected String JavaDoc factCountName;
842         protected List<String JavaDoc> ignoreColumnNames;
843         private Map<String JavaDoc, String JavaDoc> foreignKeyMap;
844         private List<Level> levels;
845         private List<Measure> measures;
846
847         protected TableDef(
848                 final boolean ignoreCase,
849                 final ExplicitRules.Group aggGroup) {
850             this.id = nextId();
851             this.ignoreCase = ignoreCase;
852             this.aggGroup = aggGroup;
853             this.foreignKeyMap = Collections.emptyMap();
854             this.levels = Collections.emptyList();
855             this.measures = Collections.emptyList();
856             this.ignoreColumnNames = Collections.emptyList();
857         }
858
859         /**
860          * TODO: This does not seemed to be used anywhere???
861          */

862         public int getId() {
863             return this.id;
864         }
865
866         /**
867          * Return true if this name/pattern matching ignores case.
868          */

869         public boolean isIgnoreCase() {
870             return this.ignoreCase;
871         }
872
873         /**
874          * Get the RolapStar associated with this cube.
875          */

876         public RolapStar getStar() {
877             return getAggGroup().getStar();
878         }
879
880         /**
881          * Get the Group with which is a part.
882          */

883         public ExplicitRules.Group getAggGroup() {
884             return this.aggGroup;
885         }
886
887         /**
888          * Get the name of the fact count column.
889          */

890         protected String JavaDoc getFactCountName() {
891             return factCountName;
892         }
893
894         /**
895          * Set the name of the fact count column.
896          *
897          * @param factCountName
898          */

899         protected void setFactCountName(final String JavaDoc factCountName) {
900             this.factCountName = factCountName;
901         }
902
903         /**
904          * Get an Iterator over all ignore column name entries.
905          */

906         protected Iterator<String JavaDoc> getIgnoreColumnNames() {
907             return ignoreColumnNames.iterator();
908         }
909
910         /**
911          * Gets all level mappings.
912          */

913         public List<Level> getLevels() {
914             return levels;
915         }
916
917         /**
918          * Gets all level mappings.
919          */

920         public List<Measure> getMeasures() {
921             return measures;
922         }
923
924         /**
925          * Get Matcher for ignore columns.
926          */

927         protected Recognizer.Matcher getIgnoreMatcher() {
928             return new Recognizer.Matcher() {
929                 public boolean matches(final String JavaDoc name) {
930                     for (Iterator<String JavaDoc> it =
931                             ExplicitRules.TableDef.this.getIgnoreColumnNames();
932                             it.hasNext();) {
933                         String JavaDoc ignoreName = it.next();
934                         if (ignoreName.equals(name)) {
935                             return true;
936                         }
937                     }
938                     return false;
939                 }
940             };
941         }
942
943         /**
944          * Get Matcher for the fact count column.
945          */

946         protected Recognizer.Matcher getFactCountMatcher() {
947             return new Recognizer.Matcher() {
948                 public boolean matches(String JavaDoc name) {
949                     // Match is case insensitive
950
return ExplicitRules.TableDef.this.factCountName.equalsIgnoreCase(name);
951                 }
952             };
953         }
954
955         /**
956          * Get the RolapCube associated with this mapping.
957          */

958         RolapCube getCube() {
959             return aggGroup.getCube();
960         }
961
962         /**
963          * Checks that ALL of the columns in the dbTable have a mapping in in the
964          * tableDef.
965          *
966          * <p>It is an error if there is a column that does not have a mapping.
967          */

968         public boolean columnsOK(
969                 final RolapStar star,
970                 final JdbcSchema.Table dbFactTable,
971                 final JdbcSchema.Table dbTable,
972                 final MessageRecorder msgRecorder) {
973             Recognizer cb = new ExplicitRecognizer(
974                     this, star, dbFactTable, dbTable, msgRecorder);
975             return cb.check();
976         }
977
978         /**
979          * Adds the name of an aggregate table column that is to be ignored.
980          */

981         protected void addIgnoreColumnName(final String JavaDoc ignoreName) {
982             if (this.ignoreColumnNames == Collections.EMPTY_LIST) {
983                 this.ignoreColumnNames = new ArrayList<String JavaDoc>();
984             }
985             this.ignoreColumnNames.add(ignoreName);
986         }
987
988         /**
989          * Add foreign key mapping entry (maps from fact table foreign key
990          * column name to aggregate table foreign key column name).
991          */

992         protected void addFK(final MondrianDef.AggForeignKey fk) {
993             if (this.foreignKeyMap == Collections.EMPTY_MAP) {
994                 this.foreignKeyMap = new HashMap<String JavaDoc, String JavaDoc>();
995             }
996             this.foreignKeyMap.put(fk.getFactFKColumnName(),
997                                    fk.getAggregateFKColumnName());
998         }
999
1000        /**
1001         * Get the name of the aggregate table's foreign key column that matches
1002         * the base fact table's foreign key column or return null.
1003         */

1004        protected String JavaDoc getAggregateFK(final String JavaDoc baseFK) {
1005            return this.foreignKeyMap.get(baseFK);
1006        }
1007
1008        /**
1009         * Adds a Level.
1010         */

1011        protected void add(final Level level) {
1012            if (this.levels == Collections.EMPTY_LIST) {
1013                this.levels = new ArrayList<Level>();
1014            }
1015            this.levels.add(level);
1016        }
1017
1018        /**
1019         * Add a Measure.
1020         */

1021        protected void add(final Measure measure) {
1022            if (this.measures == Collections.EMPTY_LIST) {
1023                this.measures = new ArrayList<Measure>();
1024            }
1025            this.measures.add(measure);
1026        }
1027
1028        /**
1029         * Does the TableDef match a table with name tableName.
1030         */

1031        public abstract boolean matches(final String JavaDoc tableName);
1032
1033        /**
1034         * Validate the Levels and Measures, also make sure each definition
1035         * is different, both name and column.
1036         */

1037        public void validate(final MessageRecorder msgRecorder) {
1038            msgRecorder.pushContextName("TableDef");
1039            try {
1040                // used to detect duplicates
1041
Map<String JavaDoc, Object JavaDoc> namesToObjects =
1042                    new HashMap<String JavaDoc, Object JavaDoc>();
1043                // used to detect duplicates
1044
Map<String JavaDoc, Object JavaDoc> columnsToObjects =
1045                    new HashMap<String JavaDoc, Object JavaDoc>();
1046
1047                for (Level level : levels) {
1048                    level.validate(msgRecorder);
1049
1050                    // Is the level name a duplicate
1051
if (namesToObjects.containsKey(level.getName())) {
1052                        msgRecorder.reportError(
1053                            mres.DuplicateLevelNames.str(
1054                                msgRecorder.getContext(),
1055                                level.getName()));
1056                    } else {
1057                        namesToObjects.put(level.getName(), level);
1058                    }
1059
1060                    // Is the level foreign key name a duplicate
1061
if (columnsToObjects.containsKey(level.getColumnName())) {
1062                        Level l = (Level)
1063                            columnsToObjects.get(level.getColumnName());
1064                        msgRecorder.reportError(
1065                            mres.DuplicateLevelColumnNames.str(
1066                                msgRecorder.getContext(),
1067                                level.getName(),
1068                                l.getName(),
1069                                level.getColumnName()));
1070                    } else {
1071                        columnsToObjects.put(level.getColumnName(), level);
1072                    }
1073                }
1074
1075                // reset names map, but keep the columns from levels
1076
namesToObjects.clear();
1077                for (Measure measure : measures) {
1078                    measure.validate(msgRecorder);
1079
1080                    if (namesToObjects.containsKey(measure.getName())) {
1081                        msgRecorder.reportError(
1082                            mres.DuplicateMeasureNames.str(
1083                                msgRecorder.getContext(),
1084                                measure.getName()));
1085                        continue;
1086                    } else {
1087                        namesToObjects.put(measure.getName(), measure);
1088                    }
1089
1090                    if (columnsToObjects.containsKey(measure.getColumnName())) {
1091                        Object JavaDoc o =
1092                            columnsToObjects.get(measure.getColumnName());
1093                        if (o instanceof Measure) {
1094                            Measure m = (Measure) o;
1095                            msgRecorder.reportError(
1096                                mres.DuplicateMeasureColumnNames.str(
1097                                    msgRecorder.getContext(),
1098                                    measure.getName(),
1099                                    m.getName(),
1100                                    measure.getColumnName()));
1101                        } else {
1102                            Level l = (Level) o;
1103                            msgRecorder.reportError(
1104                                mres.DuplicateLevelMeasureColumnNames.str(
1105                                    msgRecorder.getContext(),
1106                                    l.getName(),
1107                                    measure.getName(),
1108                                    measure.getColumnName()));
1109                        }
1110
1111                    } else {
1112                        columnsToObjects.put(measure.getColumnName(), measure);
1113                    }
1114                }
1115
1116                // reset both
1117
namesToObjects.clear();
1118                columnsToObjects.clear();
1119
1120                // Make sure that the base fact table foreign key names match
1121
// real columns
1122
RolapStar star = getStar();
1123                RolapStar.Table factTable = star.getFactTable();
1124                String JavaDoc tableName = factTable.getAlias();
1125                for (Map.Entry<String JavaDoc, String JavaDoc> e : foreignKeyMap.entrySet()) {
1126                    String JavaDoc baseFKName = e.getKey();
1127                    String JavaDoc aggFKName = e.getValue();
1128
1129                    if (namesToObjects.containsKey(baseFKName)) {
1130                        msgRecorder.reportError(
1131                            mres.DuplicateFactForeignKey.str(
1132                                msgRecorder.getContext(),
1133                                baseFKName,
1134                                aggFKName));
1135                    } else {
1136                        namesToObjects.put(baseFKName, aggFKName);
1137                    }
1138                    if (columnsToObjects.containsKey(aggFKName)) {
1139                        msgRecorder.reportError(
1140                            mres.DuplicateFactForeignKey.str(
1141                                msgRecorder.getContext(),
1142                                baseFKName,
1143                                aggFKName));
1144                    } else {
1145                        columnsToObjects.put(aggFKName, baseFKName);
1146                    }
1147
1148                    MondrianDef.Column c =
1149                        new MondrianDef.Column(tableName, baseFKName);
1150                    if (factTable.findTableWithLeftCondition(c) == null) {
1151                        msgRecorder.reportError(
1152                            mres.UnknownLeftJoinCondition.str(
1153                                msgRecorder.getContext(),
1154                                tableName,
1155                                baseFKName));
1156                    }
1157                }
1158            } finally {
1159                msgRecorder.popContextName();
1160            }
1161        }
1162
1163        public String JavaDoc toString() {
1164            StringWriter JavaDoc sw = new StringWriter JavaDoc(256);
1165            PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
1166            print(pw, "");
1167            pw.flush();
1168            return sw.toString();
1169        }
1170
1171        public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
1172            String JavaDoc subprefix = prefix + " ";
1173            String JavaDoc subsubprefix = subprefix + " ";
1174
1175            pw.print(subprefix);
1176            pw.print("id=");
1177            pw.println(this.id);
1178
1179            pw.print(subprefix);
1180            pw.print("ignoreCase=");
1181            pw.println(this.ignoreCase);
1182
1183            pw.print(subprefix);
1184            pw.println("Levels: [");
1185            for (Level level : this.levels) {
1186                level.print(pw, subsubprefix);
1187            }
1188            pw.print(subprefix);
1189            pw.println("]");
1190
1191            pw.print(subprefix);
1192            pw.println("Measures: [");
1193            for (Measure measure : this.measures) {
1194                measure.print(pw, subsubprefix);
1195            }
1196            pw.print(subprefix);
1197            pw.println("]");
1198        }
1199    }
1200
1201    static class NameTableDef extends ExplicitRules.TableDef {
1202        /**
1203         * Make a NameTableDef from the calalog schema.
1204         */

1205        static ExplicitRules.NameTableDef make(
1206                final MondrianDef.AggName aggName,
1207                final ExplicitRules.Group group) {
1208            ExplicitRules.NameTableDef name =
1209                new ExplicitRules.NameTableDef(aggName.getNameAttribute(),
1210                                     aggName.isIgnoreCase(),
1211                                     group);
1212
1213            ExplicitRules.TableDef.add(name, aggName);
1214
1215            return name;
1216        }
1217
1218        private final String JavaDoc name;
1219        public NameTableDef(
1220                final String JavaDoc name,
1221                final boolean ignoreCase,
1222                final ExplicitRules.Group group) {
1223            super(ignoreCase, group);
1224            this.name = name;
1225        }
1226
1227        /**
1228         * Does the given tableName match this NameTableDef (either exact match
1229         * or, if set, a case insensitive match).
1230         */

1231        public boolean matches(final String JavaDoc tableName) {
1232            return (this.ignoreCase) ?
1233                    this.name.equals(tableName) :
1234                    this.name.equalsIgnoreCase(tableName);
1235        }
1236
1237        /**
1238         * Validate name and base class.
1239         *
1240         * @param msgRecorder
1241         */

1242        public void validate(final MessageRecorder msgRecorder) {
1243            msgRecorder.pushContextName("NameTableDef");
1244            try {
1245                checkAttributeString(msgRecorder, name, "name");
1246
1247                super.validate(msgRecorder);
1248            } finally {
1249                msgRecorder.popContextName();
1250            }
1251        }
1252
1253        public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
1254            pw.print(prefix);
1255            pw.println("ExplicitRules.NameTableDef:");
1256            super.print(pw, prefix);
1257
1258            String JavaDoc subprefix = prefix + " ";
1259
1260            pw.print(subprefix);
1261            pw.print("name=");
1262            pw.println(this.name);
1263
1264        }
1265    }
1266
1267    /**
1268     * This class matches candidate aggregate table name with a pattern.
1269     */

1270    public static class PatternTableDef extends ExplicitRules.TableDef {
1271
1272        /**
1273         * Make a PatternTableDef from the catalog schema.
1274         */

1275        static ExplicitRules.PatternTableDef make(
1276                final MondrianDef.AggPattern aggPattern,
1277                final ExplicitRules.Group group) {
1278
1279            ExplicitRules.PatternTableDef pattern =
1280                    new ExplicitRules.PatternTableDef(
1281                            aggPattern.getPattern(),
1282                            aggPattern.isIgnoreCase(),
1283                            group);
1284
1285            MondrianDef.AggExclude[] excludes = aggPattern.getAggExcludes();
1286            if (excludes != null) {
1287                for (MondrianDef.AggExclude exclude1 : excludes) {
1288                    Exclude exclude = ExplicitRules.make(exclude1);
1289                    pattern.add(exclude);
1290                }
1291            }
1292
1293            ExplicitRules.TableDef.add(pattern, aggPattern);
1294
1295            return pattern;
1296        }
1297
1298        private final Pattern JavaDoc pattern;
1299        private List<Exclude> excludes;
1300
1301        public PatternTableDef(
1302                final String JavaDoc pattern,
1303                final boolean ignoreCase,
1304                final ExplicitRules.Group group) {
1305            super(ignoreCase, group);
1306            this.pattern = (this.ignoreCase)
1307                ? Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)
1308                : Pattern.compile(pattern);
1309            this.excludes = Collections.emptyList();
1310        }
1311
1312        /**
1313         * Get the Pattern.
1314         */

1315        public Pattern JavaDoc getPattern() {
1316            return pattern;
1317        }
1318
1319        /**
1320         * Get an Iterator over the list of Excludes.
1321         */

1322        public List<Exclude> getExcludes() {
1323            return excludes;
1324        }
1325
1326        /**
1327         * Add an Exclude.
1328         *
1329         * @param exclude
1330         */

1331        private void add(final Exclude exclude) {
1332            if (this.excludes == Collections.EMPTY_LIST) {
1333                this.excludes = new ArrayList<Exclude>();
1334            }
1335            this.excludes.add(exclude);
1336        }
1337
1338        /**
1339         * Return true if the tableName 1) matches the pattern and 2) is not
1340         * matched by any of the Excludes.
1341         */

1342        public boolean matches(final String JavaDoc tableName) {
1343            if (! pattern.matcher(tableName).matches()) {
1344                return false;
1345            } else {
1346                for (Exclude exclude : getExcludes()) {
1347                    if (exclude.isExcluded(tableName)) {
1348                        return false;
1349                    }
1350                }
1351                return true;
1352            }
1353        }
1354
1355        /**
1356         * Validate excludes and base class.
1357         */

1358        public void validate(final MessageRecorder msgRecorder) {
1359            msgRecorder.pushContextName("PatternTableDef");
1360            try {
1361                checkAttributeString(msgRecorder, pattern.pattern(), "pattern");
1362
1363                for (Exclude exclude : getExcludes()) {
1364                    exclude.validate(msgRecorder);
1365                }
1366                super.validate(msgRecorder);
1367            } finally {
1368                msgRecorder.popContextName();
1369            }
1370        }
1371
1372        public void print(final PrintWriter JavaDoc pw, final String JavaDoc prefix) {
1373            pw.print(prefix);
1374            pw.println("ExplicitRules.PatternTableDef:");
1375            super.print(pw, prefix);
1376
1377            String JavaDoc subprefix = prefix + " ";
1378            String JavaDoc subsubprefix = subprefix + " ";
1379
1380            pw.print(subprefix);
1381            pw.print("pattern=");
1382            pw.print(this.pattern.pattern());
1383            pw.print(":");
1384            pw.println(this.pattern.flags());
1385
1386            pw.print(subprefix);
1387            pw.println("Excludes: [");
1388            Iterator<Exclude> it = this.excludes.iterator();
1389            while (it.hasNext()) {
1390                Exclude exclude = it.next();
1391                exclude.print(pw, subsubprefix);
1392            }
1393            pw.print(subprefix);
1394            pw.println("]");
1395
1396        }
1397    }
1398
1399    /**
1400     * Helper method used to determine if an attribute with name attrName has a
1401     * non-empty value.
1402     *
1403     * @param msgRecorder
1404     * @param attrValue
1405     * @param attrName
1406     */

1407    private static void checkAttributeString(
1408            final MessageRecorder msgRecorder,
1409            final String JavaDoc attrValue,
1410            final String JavaDoc attrName) {
1411        if (attrValue == null) {
1412            msgRecorder.reportError(mres.NullAttributeString.str(
1413                    msgRecorder.getContext(),
1414                    attrName));
1415        } else if (attrValue.length() == 0) {
1416            msgRecorder.reportError(mres.EmptyAttributeString.str(
1417                    msgRecorder.getContext(),
1418                    attrName));
1419        }
1420    }
1421
1422
1423    private ExplicitRules() {}
1424}
1425
1426// End ExplicitRules.java
1427
Popular Tags