001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRecognizer.java#18 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2005-2008 Julian Hyde and others
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    
011    package mondrian.rolap.aggmatcher;
012    
013    import mondrian.olap.Hierarchy;
014    import mondrian.olap.Util;
015    import mondrian.olap.Id;
016    import mondrian.recorder.MessageRecorder;
017    import mondrian.rolap.*;
018    
019    import java.util.Iterator;
020    import java.util.List;
021    
022    /**
023     * This is the Recognizer for the aggregate table descriptions that appear in
024     * the catalog schema files; the user explicitly defines the aggregate.
025     *
026     * @author Richard M. Emberson
027     * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRecognizer.java#18 $
028     */
029    class ExplicitRecognizer extends Recognizer {
030        private ExplicitRules.TableDef tableDef;
031        private RolapCube cube;
032    
033        ExplicitRecognizer(
034            final ExplicitRules.TableDef tableDef,
035            final RolapStar star,
036            RolapCube cube,
037            final JdbcSchema.Table dbFactTable,
038            final JdbcSchema.Table aggTable,
039            final MessageRecorder msgRecorder)
040        {
041            super(star, dbFactTable, aggTable, msgRecorder);
042            this.tableDef = tableDef;
043            this.cube = cube;
044        }
045    
046        /**
047         * Get the ExplicitRules.TableDef associated with this instance.
048         */
049        protected ExplicitRules.TableDef getTableDef() {
050            return tableDef;
051        }
052    
053        /**
054         * Get the Matcher to be used to match columns to be ignored.
055         */
056        protected Recognizer.Matcher getIgnoreMatcher() {
057            return getTableDef().getIgnoreMatcher();
058        }
059    
060        /**
061         * Get the Matcher to be used to match the column which is the fact count
062         * column.
063         */
064        protected Recognizer.Matcher getFactCountMatcher() {
065            return getTableDef().getFactCountMatcher();
066        }
067    
068        /**
069         * Make the measures for this aggregate table.
070         * <p>
071         * First, iterate through all of the columns in the table.
072         * For each column, iterate through all of the tableDef measures, the
073         * explicit definitions of a measure.
074         * If the table's column name matches the column name in the measure
075         * definition, then make a measure.
076         * Next, look through all of the fact table column usage measures.
077         * For each such measure usage that has a sibling foreign key usage
078         * see if the tableDef has a foreign key defined with the same name.
079         * If so, then, for free, we can make a measure for the aggregate using
080         * its foreign key.
081         * <p>
082         *
083         * @return number of measures created.
084         */
085        protected int checkMeasures() {
086            msgRecorder.pushContextName("ExplicitRecognizer.checkMeasures");
087            try {
088    
089                int measureColumnCounts = 0;
090                // Look at each aggregate table column. For each measure defined,
091                // see if the measure's column name equals the column's name.
092                // If so, make the aggregate measure usage for that column.
093                for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
094                    // if marked as ignore, then do not consider
095                    if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) {
096                        continue;
097                    }
098    
099                    String aggColumnName = aggColumn.getName();
100    
101                    for (ExplicitRules.TableDef.Measure measure :
102                        getTableDef().getMeasures())
103                    {
104                        // Column name match is case insensitive
105                        if (measure.getColumnName().equalsIgnoreCase(aggColumnName)) {
106                            String name = measure.getName();
107                            List<Id.Segment> parts = Util.parseIdentifier(name);
108                            String nameLast = parts.get(parts.size() - 1).name;
109    
110                            RolapStar.Measure m =
111                                star.getFactTable().lookupMeasureByName(
112                                    cube.getName(), nameLast);
113                            RolapAggregator agg = null;
114                            if (m != null) {
115                                agg = m.getAggregator();
116                            }
117                            // Ok, got a match, so now make a measure
118                            makeMeasure(measure, agg, aggColumn);
119                            measureColumnCounts++;
120                        }
121                    }
122                }
123                // Ok, now look at all of the fact table columns with measure usage
124                // that have a sibling foreign key usage. These can be automagically
125                // generated for the aggregate table as long as it still has the
126                // foreign key.
127                for (Iterator<JdbcSchema.Table.Column.Usage> it =
128                         dbFactTable.getColumnUsages(JdbcSchema.UsageType.MEASURE);
129                     it.hasNext();) {
130                    JdbcSchema.Table.Column.Usage factUsage = it.next();
131                    JdbcSchema.Table.Column factColumn = factUsage.getColumn();
132    
133                    if (factColumn.hasUsage(JdbcSchema.UsageType.FOREIGN_KEY)) {
134                        // What we've got here is a measure based upon a foreign key
135                        String aggFK =
136                            getTableDef().getAggregateFK(factColumn.getName());
137                        // OK, not a lost dimension
138                        if (aggFK != null) {
139                            JdbcSchema.Table.Column aggColumn =
140                                aggTable.getColumn(aggFK);
141    
142                            // Column name match is case insensitive
143                            if (aggColumn == null) {
144                                aggColumn = aggTable.getColumn(aggFK.toLowerCase());
145                            }
146                            if (aggColumn == null) {
147                                aggColumn = aggTable.getColumn(aggFK.toUpperCase());
148                            }
149    
150                            if (aggColumn != null) {
151                                makeMeasure(factUsage, aggColumn);
152                                measureColumnCounts++;
153                            }
154                        }
155                    }
156                }
157                return measureColumnCounts;
158    
159            } finally {
160                msgRecorder.popContextName();
161            }
162        }
163    
164        /**
165         * Make a measure. This makes a measure usage using the Aggregator found in
166         * the RolapStar.Measure associated with the ExplicitRules.TableDef.Measure.
167         *
168         * @param measure
169         * @param aggColumn
170         */
171        protected void makeMeasure(
172                final ExplicitRules.TableDef.Measure measure,
173                RolapAggregator factAgg,
174                final JdbcSchema.Table.Column aggColumn) {
175            RolapStar.Measure rm = measure.getRolapStarMeasure();
176    
177            JdbcSchema.Table.Column.Usage aggUsage =
178                aggColumn.newUsage(JdbcSchema.UsageType.MEASURE);
179    
180            aggUsage.setSymbolicName(measure.getSymbolicName());
181            RolapAggregator ra = (factAgg == null)
182                        ? convertAggregator(aggUsage, rm.getAggregator())
183                        : convertAggregator(aggUsage, factAgg, rm.getAggregator());
184            aggUsage.setAggregator(ra);
185    
186            aggUsage.rMeasure = rm;
187        }
188    
189        /**
190         * This creates a foreign key usage.
191         *
192         * <p> First the column name of the fact usage which is a foreign key is used to
193         * search for a foreign key definition in the ExplicitRules.tableDef.
194         * If not found, thats ok, it is just a lost dimension.
195         * If found, look for a column in the aggregate table with that name and
196         * make a foreign key usage.
197         */
198        protected int matchForeignKey(
199                final JdbcSchema.Table.Column.Usage factUsage) {
200            JdbcSchema.Table.Column factColumn = factUsage.getColumn();
201            String aggFK = getTableDef().getAggregateFK(factColumn.getName());
202    
203            // OK, a lost dimension
204            if (aggFK == null) {
205                return 0;
206            }
207    
208            int matchCount = 0;
209            for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
210                // if marked as ignore, then do not consider
211                if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) {
212                    continue;
213                }
214    
215                if (aggFK.equals(aggColumn.getName())) {
216                    makeForeignKey(factUsage, aggColumn, aggFK);
217                    matchCount++;
218                }
219            }
220            return matchCount;
221    
222        }
223    
224        /**
225         * This creates a level usage. A level usage is a column that is used in a
226         * collapsed dimension aggregate table.
227         *
228         * <p> First, iterate through the ExplicitRules.TableDef's level
229         * definitions for one with a name equal to the RolapLevel unique name,
230         * i.e., [Time].[Quarter].  Now, using the level's column name, search
231         * through the aggregate table's columns for one with that name and make a
232         * level usage for the column.  Return true if the aggregate table's column
233         * was found.
234         */
235        protected boolean matchLevel(
236                final Hierarchy hierarchy,
237                final HierarchyUsage hierarchyUsage,
238                final RolapLevel rlevel) {
239            msgRecorder.pushContextName("ExplicitRecognizer.matchLevel");
240            try {
241    
242                // Try to match a Level's name against the RolapLevel unique name.
243                String levelUniqueName = rlevel.getUniqueName();
244                for (ExplicitRules.TableDef.Level level : getTableDef().getLevels()) {
245                    if (level.getName().equals(levelUniqueName)) {
246                        // Ok, got a match, so now make a measue
247                        //makeLevel(level, xxxxolumn);
248                        // Now can we find a column in the aggTable that matches the
249                        // Level's column
250                        String columnName = level.getColumnName();
251                        for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
252                            if (aggColumn.getName().equals(columnName)) {
253                                makeLevel(
254                                    aggColumn,
255                                    hierarchy,
256                                    hierarchyUsage,
257                                    getColumnName(rlevel.getKeyExp()),
258                                    columnName,
259                                    rlevel.getName());
260                                return true;
261                            }
262                        }
263    
264                    }
265                }
266                return false;
267    
268            } finally {
269                msgRecorder.popContextName();
270            }
271        }
272    }
273    
274    // End ExplicitRecognizer.java