📄 recognizer.java
字号:
/*
// $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/Recognizer.java#9 $
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// Copyright (C) 2001-2005 Kana Software, Inc. and others.
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 30 August, 2001
*/
package mondrian.rolap.aggmatcher;
import mondrian.olap.Hierarchy;
import mondrian.olap.Dimension;
import mondrian.olap.MondrianDef;
import mondrian.resource.MondrianResource;
import mondrian.recorder.MessageRecorder;
import mondrian.rolap.RolapStar;
import mondrian.rolap.RolapLevel;
import mondrian.rolap.RolapSchema;
import mondrian.rolap.RolapCube;
import mondrian.rolap.RolapAggregator;
import mondrian.rolap.HierarchyUsage;
import mondrian.rolap.sql.SqlQuery;
import mondrian.resource.MondrianResource;
import java.util.*;
import org.apache.log4j.Logger;
/**
* Abstract Recognizer class used to determine if a candidate aggregate table
* has the column categories: "fact_count" column, measure columns, foreign key
* and level columns.
* Derived classes use either the default or explicit column descriptions in
* matching column categories. The basic matching algorithm is in this class
* while some specific column category matching and column building must be
* specified in derived classes.
* <p>
* A Recognizer is created per candidate aggregate table. The tables columns are
* then categorized. All errors and warnings are added to a MessageRecorder.
* <p>
* This class is less about defining a type and more about code sharing.
*
* @author <a>Richard M. Emberson</a>
* @version
*/
abstract class Recognizer {
private static final MondrianResource mres = MondrianResource.instance();
private static final Logger LOGGER = Logger.getLogger(Recognizer.class);
/**
* This is used to wrap column name matching rules.
*/
public interface Matcher {
/**
* Return true it the name matches and false otherwise.
*
* @param name
* @return
*/
boolean matches(String name);
}
protected final RolapStar star;
protected final JdbcSchema.Table dbFactTable;
protected final JdbcSchema.Table aggTable;
protected final MessageRecorder msgRecorder;
protected boolean returnValue;
protected Recognizer(final RolapStar star,
final JdbcSchema.Table dbFactTable,
final JdbcSchema.Table aggTable,
final MessageRecorder msgRecorder) {
this.star = star;
this.dbFactTable = dbFactTable;
this.aggTable = aggTable;
this.msgRecorder = msgRecorder;
returnValue = true;
}
/**
* Return true if the candidate aggregate table was successfully mapped into
* the fact table. This is the top-level checking method.
* <p>
* It first checks the ignore columns.
* <p>
* Next, the existence of a fact count column is checked.
* <p>
* Then the measures are checked. First the specified (defined,
* explicit) measures are all determined. There must be at least one such
* measure. This if followed by checking for implied measures (e.g., if base
* fact table as both sum and average of a column and the aggregate has a
* sum measure, the there is an implied average measure in the aggregate).
* <p>
* Now the levels are checked. This is in two parts. First, foreign keys are
* checked followed by level columns (for collapsed dimension aggregates).
* <p>
* If eveything checks out, then true is returned.
*
* @return
*/
public boolean check() {
checkIgnores();
checkFactCount();
// Check measures
int nosMeasures = checkMeasures();
// There must be at least one measure
checkNosMeasures(nosMeasures);
generateImpliedMeasures();
// Check levels
List notSeenForeignKeys = checkForeignKeys();
//printNotSeenForeignKeys(notSeenForeignKeys);
checkLevels(notSeenForeignKeys);
if (returnValue) {
// Add all unused columns as warning to the MessageRecorder
checkUnusedColumns();
}
return returnValue;
}
/**
* Return the ignore column Matcher.
*
* @return
*/
protected abstract Matcher getIgnoreMatcher();
/**
* Check all columns to be marked as ignore.
*/
protected void checkIgnores() {
Matcher ignoreMatcher = getIgnoreMatcher();
for (Iterator it = aggTable.getColumns(); it.hasNext(); ) {
JdbcSchema.Table.Column aggColumn =
(JdbcSchema.Table.Column) it.next();
if (ignoreMatcher.matches(aggColumn.getName())) {
makeIgnore(aggColumn);
}
}
}
/**
* Create an ignore usage for the aggColumn.
*
* @param aggColumn
*/
protected void makeIgnore(final JdbcSchema.Table.Column aggColumn) {
JdbcSchema.Table.Column.Usage usage =
aggColumn.newUsage(JdbcSchema.IGNORE_COLUMN_USAGE);
usage.setSymbolicName("Ignore");
}
/**
* Return the fact count column Matcher.
*
* @return
*/
protected abstract Matcher getFactCountMatcher();
/**
* Make sure that the aggregate table has one fact count column and that its
* type is numeric.
*/
protected void checkFactCount() {
msgRecorder.pushContextName("Recognizer.checkFactCount");
try {
Matcher factCountMatcher = getFactCountMatcher();
int nosOfFactCounts = 0;
for (Iterator it = aggTable.getColumns(); it.hasNext(); ) {
JdbcSchema.Table.Column aggColumn =
(JdbcSchema.Table.Column) it.next();
// if marked as ignore, then do not consider
if (aggColumn.hasUsage(JdbcSchema.IGNORE_COLUMN_USAGE)) {
continue;
}
if (factCountMatcher.matches(aggColumn.getName())) {
if (! aggColumn.isNumeric()) {
String msg = mres.NonNumericFactCountColumn.str(
aggTable.getName(),
dbFactTable.getName(),
aggColumn.getName(),
aggColumn.getTypeName());
msgRecorder.reportError(msg);
returnValue = false;
} else {
makeFactCount(aggColumn);
nosOfFactCounts++;
}
}
}
if (nosOfFactCounts == 0) {
String msg = mres.NoFactCountColumns.str(
aggTable.getName(),
dbFactTable.getName());
msgRecorder.reportError(msg);
returnValue = false;
} else if (nosOfFactCounts > 1) {
String msg = mres.TooManyFactCountColumns.str(
aggTable.getName(),
dbFactTable.getName(),
new Integer(nosOfFactCounts));
msgRecorder.reportError(msg);
returnValue = false;
}
} finally {
msgRecorder.popContextName();
}
}
/**
* Check all measure columns returning the number of measure columns.
*
* @return
*/
protected abstract int checkMeasures();
/**
* Create a fact count usage for the aggColumn.
*
* @param aggColumn
*/
protected void makeFactCount(final JdbcSchema.Table.Column aggColumn) {
JdbcSchema.Table.Column.Usage usage =
aggColumn.newUsage(JdbcSchema.FACT_COUNT_COLUMN_USAGE);
usage.setSymbolicName("Fact Count");
}
/**
* Make sure there was at least one measure column identified.
*
* @param nosMeasures
*/
protected void checkNosMeasures(int nosMeasures) {
msgRecorder.pushContextName("Recognizer.checkNosMeasures");
try {
if (nosMeasures == 0) {
String msg = mres.NoMeasureColumns.str(
aggTable.getName(),
dbFactTable.getName()
);
msgRecorder.reportError(msg);
returnValue = false;
}
} finally {
msgRecorder.popContextName();
}
}
/**
* An implied measure in an aggregate table is one where there is both a sum
* and average measures in the base fact table and the aggregate table has
* either a sum or average, the other measure is implied and can be
* generated from the measure and the fact_count column.
* <p>
* For each column in the fact table, get its measure usages. If there is
* both an average and sum aggregator associated with the column, then
* iterator over all of the column usage of type measure of the aggregator
* table. If only one aggregate column usage measure is found and this
* RolapStar.Measure measure instance variable is the same as the
* the fact table's usage's instance variable, then the other measure is
* implied and the measure is created for the aggregate table.
*/
protected void generateImpliedMeasures() {
for (Iterator it = dbFactTable.getColumns(); it.hasNext(); ) {
JdbcSchema.Table.Column factColumn =
(JdbcSchema.Table.Column) it.next();
JdbcSchema.Table.Column.Usage sumFactUsage = null;
JdbcSchema.Table.Column.Usage avgFactUsage = null;
for (Iterator mit =
factColumn.getUsages(JdbcSchema.MEASURE_COLUMN_USAGE);
mit.hasNext(); ) {
JdbcSchema.Table.Column.Usage factUsage =
(JdbcSchema.Table.Column.Usage) mit.next();
if (factUsage.getAggregator() == RolapAggregator.Avg) {
avgFactUsage = factUsage;
} else if (factUsage.getAggregator() == RolapAggregator.Sum) {
sumFactUsage = factUsage;
}
}
if ((avgFactUsage != null) && (sumFactUsage != null)) {
JdbcSchema.Table.Column.Usage sumAggUsage = null;
JdbcSchema.Table.Column.Usage avgAggUsage = null;
int nosSeen = 0;
for (Iterator mit =
aggTable.getColumnUsages(JdbcSchema.MEASURE_COLUMN_USAGE);
mit.hasNext(); ) {
JdbcSchema.Table.Column.Usage aggUsage =
(JdbcSchema.Table.Column.Usage) mit.next();
if (aggUsage.rMeasure == avgFactUsage.rMeasure) {
avgAggUsage = aggUsage;
nosSeen++;
} else if (aggUsage.rMeasure == sumFactUsage.rMeasure) {
sumAggUsage = aggUsage;
nosSeen++;
}
}
if (nosSeen == 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -