001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/DefaultRecognizer.java#14 $
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.resource.MondrianResource;
015    import mondrian.recorder.MessageRecorder;
016    import mondrian.rolap.RolapStar;
017    import mondrian.rolap.RolapLevel;
018    import mondrian.rolap.HierarchyUsage;
019    
020    import java.util.Iterator;
021    
022    /**
023     * This is the default Recognizer. It uses the rules found in the file
024     * DefaultRules.xml to find aggregate tables and there columns.
025     *
026     * @author Richard M. Emberson
027     * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/DefaultRecognizer.java#14 $
028     */
029    class DefaultRecognizer extends Recognizer {
030    
031        private static final MondrianResource mres = MondrianResource.instance();
032    
033        private final DefaultRules aggDefault;
034    
035        DefaultRecognizer(final DefaultRules aggDefault,
036                          final RolapStar star,
037                          final JdbcSchema.Table dbFactTable,
038                          final JdbcSchema.Table aggTable,
039                          final MessageRecorder msgRecorder) {
040            super(star, dbFactTable, aggTable, msgRecorder);
041            this.aggDefault = aggDefault;
042        }
043    
044        /**
045         * Get the DefaultRules instance associated with this object.
046         */
047        DefaultRules getRules() {
048            return aggDefault;
049        }
050    
051        /**
052         * Get the Matcher to be used to match columns to be ignored.
053         */
054        protected Recognizer.Matcher getIgnoreMatcher() {
055            return getRules().getIgnoreMatcher();
056        }
057    
058        /**
059         * Get the Matcher to be used to match the column which is the fact count
060         * column.
061         */
062        protected Recognizer.Matcher getFactCountMatcher() {
063            return getRules().getFactCountMatcher();
064        }
065    
066        /**
067         * Get the Match used to identify columns that are measures.
068         */
069        protected Recognizer.Matcher getMeasureMatcher(
070                JdbcSchema.Table.Column.Usage factUsage) {
071    
072            String measureName = factUsage.getSymbolicName();
073            String measureColumnName = factUsage.getColumn().getName();
074            String aggregateName = factUsage.getAggregator().getName();
075    
076            return getRules().getMeasureMatcher(
077                measureName,
078                measureColumnName,
079                aggregateName);
080        }
081    
082        /**
083         * Create measures for an aggregate table.
084         * <p>
085         * First, iterator through all fact table measure usages.
086         * Create a Matcher for each such usage.
087         * Iterate through all aggregate table columns.
088         * For each column that matches create a measure usage.
089         * <p>
090         * Per fact table measure usage, at most only one aggregate measure should
091         * be created.
092         *
093         * @return number of measures created.
094         */
095        protected int checkMeasures() {
096            msgRecorder.pushContextName("DefaultRecognizer.checkMeasures");
097    
098            try {
099                int measureCountCount = 0;
100    
101                for (Iterator<JdbcSchema.Table.Column.Usage> it =
102                        dbFactTable.getColumnUsages(JdbcSchema.UsageType.MEASURE);
103                        it.hasNext();) {
104                    JdbcSchema.Table.Column.Usage factUsage = it.next();
105    
106                    Matcher matcher = getMeasureMatcher(factUsage);
107    
108                    int matchCount = 0;
109                    for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
110                        // if marked as ignore, then do not consider
111                        if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) {
112                            continue;
113                        }
114    
115                        if (matcher.matches(aggColumn.getName())) {
116                            makeMeasure(factUsage, aggColumn);
117    
118                            measureCountCount++;
119                            matchCount++;
120                        }
121                    }
122    
123                    if (matchCount > 1) {
124                        String msg = mres.AggMultipleMatchingMeasure.str(
125                            msgRecorder.getContext(),
126                            aggTable.getName(),
127                            dbFactTable.getName(),
128                            matchCount,
129                            factUsage.getSymbolicName(),
130                            factUsage.getColumn().getName(),
131                            factUsage.getAggregator().getName());
132                        msgRecorder.reportError(msg);
133    
134                        returnValue = false;
135                    }
136                }
137                return measureCountCount;
138    
139            } finally {
140                msgRecorder.popContextName();
141            }
142        }
143    
144        /**
145         * This creates a foreign key usage.
146         *
147         * <p>Using the foreign key Matcher with the fact usage's column name the
148         * aggregate table's columns are searched for one that matches.  For each
149         * that matches a foreign key usage is created (thought if more than one is
150         * created its is an error which is handled in the calling code.
151         */
152        protected int matchForeignKey(JdbcSchema.Table.Column.Usage factUsage) {
153            JdbcSchema.Table.Column factColumn = factUsage.getColumn();
154    
155            // search to see if any of the aggTable's columns match
156            Recognizer.Matcher matcher =
157                getRules().getForeignKeyMatcher(factColumn.getName());
158    
159            int matchCount = 0;
160            for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
161                // if marked as ignore, then do not consider
162                if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) {
163                    continue;
164                }
165    
166                if (matcher.matches(aggColumn.getName())) {
167                    makeForeignKey(factUsage, aggColumn, null);
168                    matchCount++;
169                }
170            }
171            return matchCount;
172        }
173    
174        /**
175         * Create level usages.
176         *
177         * <p> A Matcher is created using the Hierarchy's name, the RolapLevel
178         * name, and the column name associated with the RolapLevel's key
179         * expression.  The aggregate table columns are search for the first match
180         * and, if found, a level usage is created for that column and true is
181         * returned.
182         */
183        protected boolean matchLevel(
184                final Hierarchy hierarchy,
185                final HierarchyUsage hierarchyUsage,
186                final RolapLevel level) {
187    
188            msgRecorder.pushContextName("DefaultRecognizer.matchLevel");
189            try {
190    
191                String usagePrefix = hierarchyUsage.getUsagePrefix();
192                String hierName = hierarchy.getName();
193                String levelName = level.getName();
194                String levelColumnName = getColumnName(level.getKeyExp());
195    
196                Recognizer.Matcher matcher = getRules().getLevelMatcher(
197                    usagePrefix, hierName, levelName, levelColumnName);
198    
199                for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) {
200                    if (matcher.matches(aggColumn.getName())) {
201                        makeLevel(
202                            aggColumn,
203                            hierarchy,
204                            hierarchyUsage,
205                            getColumnName(level.getKeyExp()),
206                            getColumnName(level.getKeyExp()),
207                            level.getName());
208                        return true;
209                    }
210                }
211                return false;
212    
213            } finally {
214                msgRecorder.popContextName();
215            }
216        }
217    }
218    
219    // End DefaultRecognizer.java