public class AggregateTable {
	
	public String table;
	public List<TableJoin> joins;
	public List<TableMeasure> measures;
	
	
	private DbTable dbTable;
	
	protected void index(DbSchema dbSchema, CatalogDefinition catalogDefinition) throws OlapException {
		dbTable = dbSchema.addTable(table);

		for(TableMeasure t : measures){
			if(t.formula==null){
				DbColumn dbColumn = dbTable.addColumn(t.column);
				t.setDbColumn(dbColumn);
				t.setExpression(FunctionCall.sum().addColumnParams(dbColumn));
			}
			
		}
		
		for(TableMeasure t : measures){
			if(t.formula!=null){
				Formula formula = new Formula(t.formula);
				t.setExpression(formula.render(this));	
			}				
		}
		
		
		List<DimensionTable> joinedDimensions = new ArrayList<DimensionTable>();
		for(TableJoin j : joins){
			
			 DbColumn fkey = dbTable.addColumn(j.key);
			 
			 DimensionTable dtable = catalogDefinition.getPhysicalSchema().findDimensionTable(j.table);
			 if(dtable==null)
				 throw new OlapException("Unknown table name used "+j.table+" in aggregate table "+ table);
			 
			 if(joinedDimensions.contains(dtable))
				 dtable = dtable.createAlias(dbSchema);
			 
			 joinedDimensions.add(dtable);
			 
			 DbJoin join = dbSchema.getSpec().createJoin(
					 dbTable, 
					 dtable.getDbTable(), 
					 new String[]{j.key}, 
					 new String[]{dtable.key});
			 
			    	    
			j.setDimensionTable(dtable); 
			j.setDbJoin(join);
			j.setForeign_key(fkey);
		}
		
	}

	public DbTable getDbTable() {
		return dbTable;
	}
} public class DimensionTable {
	
	public String table, key;
	
	public List<TableColumn> columns;
	
	private DbTable dbTable;
	private DbColumn dbKey;
	
	protected void index(DbSchema dbSchema) {
		dbTable = dbSchema.addTable(table);
		dbKey = dbTable.addColumn(key);
		
		for(TableColumn t : columns){
			t.setDbColumn(dbTable.addColumn(t.column));
		}
		
	}

	public DbTable getDbTable() {
		return dbTable;
	}

	public DbColumn getDbKey() {
		return dbKey;
	}
	
	public TableColumn findColumnByAttribute(String attribute_name){
		for(TableColumn c : columns){
			if(c.attribute!=null && attribute_name.equals(c.attribute))
				return c;
		}
		return null;
	}

	
	public TableColumn findColumnByName(String name){
		for(TableColumn c : columns){
			if( name.equals(c.column))
				return c;
		}
		return null;
	}

	public DimensionTable createAlias(DbSchema dbSchema) {
		
		DimensionTable alias = new DimensionTable();
		
		alias.key = key;
		alias.table = table;
		
		alias.columns = new ArrayList<TableColumn>();
		
		for(TableColumn tc : columns){
			TableColumn alias_tc = new TableColumn();
			
			alias_tc.attribute = tc.attribute;
			alias_tc.column = tc.column;
			alias_tc.identified_by = tc.identified_by;
			alias_tc.sort_direction = tc.sort_direction;
			alias_tc.sorted_by = tc.sorted_by;
			
			alias.columns.add(alias_tc);
		}
		
		alias.index(dbSchema);
		
		return alias;
	}
	
	
} public class PhysicalSchema {
	
	public String schema;
	public String dataSource;
	
	public List<DimensionTable> dimensions;
	public List<AggregateTable> aggregates;
	
	private DbSpec dbSpec;
	private DbSchema dbSchema;

	public void index(CatalogDefinition catalogDefinition) throws OlapException {
		
		dbSpec = new DbSpec();
		dbSchema = dbSpec.addSchema(schema);
		
		for(DimensionTable t : dimensions){
			t.index(dbSchema);
		}
		
		for(AggregateTable t : aggregates){
			t.index(dbSchema, catalogDefinition);
		}
		
		
	}

	public DbSpec getDbSpec() {
		return dbSpec;
	}

	public DbSchema getDbSchema() {
		return dbSchema;
	}
	
	public DimensionTable findDimensionTable(String name){
		for(DimensionTable dt : dimensions){
			if(dt.table.equals(name))
				return dt;
		}
		return null;
	}
	
	public DimensionTable findDimensionTable(Dimension dimension){
		
		for(AggregateTable at : aggregates){
			for(TableJoin join: at.joins){
				if(join.dimension.equals(dimension.getName()))
					return findDimensionTable(join.table);
			}
		}
		
		return null;
	}

} public class TableColumn {
	public String column, attribute, identified_by, sorted_by;
	public String sort_direction = "ASC";
	
	private DbColumn dbColumn;

	public DbColumn getDbColumn() {
		return dbColumn;
	}

	public void setDbColumn(DbColumn dbColumn) {
		this.dbColumn = dbColumn;
	}
} public class TableJoin {

	public String dimension, table, key;

	private DbJoin dbJoin;
	private DimensionTable dimensionTable;
	private DbColumn foreign_key;
	
	public DbJoin getDbJoin() {
		return dbJoin;
	}

	public void setDbJoin(DbJoin dbJoin) {
		this.dbJoin = dbJoin;
	}

	public DimensionTable getDimensionTable() {
		return dimensionTable;
	}

	public void setDimensionTable(DimensionTable dimensionTable) {
		this.dimensionTable = dimensionTable;
	}

	public DbColumn getForeign_key() {
		return foreign_key;
	}

	public void setForeign_key(DbColumn foreign_key) {
		this.foreign_key = foreign_key;
	}
	
} public class TableMeasure {

	public String measure, column, formula;
	private DbColumn dbColumn;
	
	private Expression expression;

	public Expression getExpression() {
		return expression;
	}

	public void setExpression(Expression expression) {
		this.expression = expression;
	}

	public DbColumn getDbColumn() {
		return dbColumn;
	}

	public void setDbColumn(DbColumn dbColumn) {
		this.dbColumn = dbColumn;
	}
	
} public class LiteralMeasure extends ServerMeasure {

	private LiteralNode node;
	public LiteralMeasure(Dimension measureDimension, LiteralNode node) {
		super(measureDimension, definition(node));
		this.node = node;
	}

	private static CubeMeasureDefinition definition(LiteralNode node) {
		CubeMeasureDefinition def = new CubeMeasureDefinition();
		def.name = node.getValue().toString();
		return def;
	}
	
	public boolean isNumeric(){
		return node.getType() instanceof NumericType;
	}
	
	public Object getLiteralValue(){
		return isNumeric() ? Float.parseFloat(node.getValue().toString()) : node.getValue();
	}
	
	

} public class ServerDimension implements Dimension{

	private SharedDimensionDefinition definition;
	private NamedList<Hierarchy> hierarchies;
	private ServerSchema serverSchema;
	
	public ServerDimension( SharedDimensionDefinition def, ServerSchema serverSchema) throws OlapException {
		this.definition = def;
		this.serverSchema = serverSchema;
		readHierarchies();
	}

	@Override
	public String getName() {
		return definition.name;
	}

	@Override
	public String getUniqueName() {
		return definition.name;
	}

	@Override
	public String getCaption() {
		return definition.caption;
	}

	@Override
	public String getDescription() {
		return definition.caption;
	}

	@Override
	public boolean isVisible() {
		return true;
	}

	@Override
	public NamedList<Hierarchy> getHierarchies() {
		return hierarchies;
	}

	private void readHierarchies() throws OlapException {
		
		hierarchies = MetadataUtils.metadataNamedList(new ArrayList<Hierarchy>());  
		
		Map<String, AttributeDefinition> attributeMap = new HashMap<String, AttributeDefinition>();
		
	    if(definition.attributes!=null)        
	    for(AttributeDefinition cdef: definition.attributes){
	    	attributeMap.put(cdef.name, cdef);
	    	if(cdef.auto_hierarchy){
	    		hierarchies.add(new ServerHierarchy(this, cdef,  Collections.singletonList((NamedElement)cdef)));
	    	}
	    }
	    
	    if(definition.hierarchies!=null)
	    for(HierarchyDefinition hdef : definition.hierarchies){
	    	
	    	List<NamedElement> levelDefs = new ArrayList<NamedElement>();
	    	for(String level : hdef.levels){	
	    		AttributeDefinition attr_def = attributeMap.get(level);
	    		if(attr_def==null)
	    			throw new OlapException("Fail to find attribute "+level+" used in dimension "+ getName());
	    		levelDefs.add(attr_def);
	    	}
	    	
	    	hierarchies.add(new ServerHierarchy(this, hdef, levelDefs));
	    }
	}

	@Override
	public Type getDimensionType() throws OlapException {
		return Type.UNKNOWN;
	}

	@Override
	public Hierarchy getDefaultHierarchy() {
		throw new UnsupportedOperationException("getDefaultHierarchy");
	}

	public ServerSchema getServerSchema() {
		return serverSchema;
	}

} public class ServerLevel implements Level {

	private ServerHierarchy hierarchy;
	private NamedElement definition;
	private int depth;
	
	protected ServerLevel(ServerHierarchy hierarchy, NamedElement def, int depth){
		this.hierarchy = hierarchy;
		this.definition = def;
		this.depth = depth;
	}
	
	
	@Override
	public String getName() {
		return definition.name;
	}

	@Override
	public String getUniqueName() {
		return hierarchy.getUniqueName() +".["+definition.name+"]";
	}

	@Override
	public String getCaption() {
		return definition.caption;
	}

	@Override
	public String getDescription() {
		return definition.caption;
	}

	@Override
	public boolean isVisible() {
		return true;
	}

	@Override
	public int getDepth() {
		return depth;
	}

	@Override
	public Hierarchy getHierarchy() {
		return hierarchy;
	}

	@Override
	public Dimension getDimension() {
		return hierarchy.getDimension();
	}

	@Override
	public Type getLevelType() {
		return Type.REGULAR;
	}

	@Override
	public boolean isCalculated() {
		return false;
	}

	@Override
	public NamedList<Property> getProperties() {
		return Olap4jUtil.emptyNamedList();
	}

	@Override
	public List<Member> getMembers() throws OlapException {
		ServerDimension dim = (ServerDimension) getDimension();		
		MemberQuery query = new MemberQuery(dim.getServerSchema().getCatalog(), this);
		return query.getMembers();
	}

	@Override
	public int getCardinality() {
		return 0;
	}
} public class ServerMeasure implements Measure {

	private CubeMeasureDefinition definition;
	private Dimension measureDimension;
	private DecimalFormat format;
	
	public ServerMeasure(Dimension measureDimension, CubeMeasureDefinition def) {
		this.definition = def;
		this.measureDimension = measureDimension;
		this.format = def.format==null ? new DecimalFormat("#,##0") : new DecimalFormat(def.format);
	}

	@Override
	public NamedList<? extends Member> getChildMembers() throws OlapException {
		return Olap4jUtil.emptyNamedList();
	}

	@Override
	public int getChildMemberCount() throws OlapException {
		return 0;
	}

	@Override
	public Member getParentMember() {
		return null;
	}

	@Override
	public Level getLevel() {
		return measureDimension.getDefaultHierarchy().getLevels().get(0);
	}

	@Override
	public Hierarchy getHierarchy() {
		return measureDimension.getDefaultHierarchy();
	}

	@Override
	public Dimension getDimension() {
		return measureDimension;
	}

	@Override
	public Type getMemberType() {
		return Type.MEASURE;
	}

	@Override
	public boolean isAll() {
		return false;
	}

	@Override
	public boolean isChildOrEqualTo(Member member) {
		return member==this;
	}

	@Override
	public boolean isCalculated() {
		return false;
	}

	@Override
	public int getSolveOrder() {
		return 0;
	}

	@Override
	public ParseTreeNode getExpression() {
		return null;
	}

	@Override
	public List<Member> getAncestorMembers() {
		return Collections.emptyList();
	}

	@Override
	public boolean isCalculatedInQuery() {
		return false;
	}

	@Override
	public Object getPropertyValue(Property property) throws OlapException {
		if(property==Property.StandardMemberProperty.$visible){
			return new Boolean(isVisible());
		}else if(property==Property.StandardMemberProperty.CHILDREN_CARDINALITY){
			return 0;
		}else if(property==Property.StandardCellProperty.DATATYPE){
			return definition.datatype;
		}else if(property==Property.StandardMemberProperty.MEMBER_UNIQUE_NAME){
			return getUniqueName();
		}else if(property==Property.StandardMemberProperty.MEMBER_CAPTION){
			return getCaption();
		}else if(property==Property.StandardMemberProperty.LEVEL_UNIQUE_NAME){
			return getLevel().getUniqueName();
		}else if(property==Property.StandardMemberProperty.LEVEL_NUMBER){
			return getLevel().getDepth();
		}else{
			return null;
		}
		
	}

	@Override
	public String getPropertyFormattedValue(Property property)
			throws OlapException {
		throw new UnsupportedOperationException("getPropertyFormattedValue");
	}

	@Override
	public void setProperty(Property property, Object value)
			throws OlapException {
		throw new UnsupportedOperationException("setProperty");
	}

	@Override
	public NamedList<Property> getProperties() {
		return Olap4jUtil.emptyNamedList();
	}

	@Override
	public int getOrdinal() {
		return 0;
	}

	@Override
	public boolean isHidden() {
		return false;
	}

	@Override
	public int getDepth() {
		return 0;
	}

	@Override
	public Member getDataMember() {
		throw new UnsupportedOperationException("getDataMember");
	}

	@Override
	public String getName() {
		return definition.name;
	}

	@Override
	public String getUniqueName() {
		return "["+ measureDimension.getName() + "].["+ definition.name +"]";
	}

	@Override
	public String getCaption() {
		return definition.caption;
	}

	@Override
	public String getDescription() {
		return definition.caption;
	}

	@Override
	public Aggregator getAggregator() {
		return Aggregator.valueOf(definition.aggregator);
	}

	@Override
	public Datatype getDatatype() {
		return Datatype.valueOf(definition.datatype);
	}

	@Override
	public boolean isVisible() {
		return true;
	}

	public DecimalFormat getFormat() {
		return format;
	}

} public class LevelMemberSet {
	
	private Level level;
	
	private ParseTreeNode node;
	private OlapOp function;
	private List<Member> members = new ArrayList<Member>();
	private Map<String, Member> map = new HashMap<String, Member>();
	
	private Sorter sorter;
	private int topLimit;
	
	public LevelMemberSet(Level level, ParseTreeNode node, OlapOp function) {
		this.level = level;
		this.node = node;
		this.function = function;
	}

	public LevelMemberSet(Member member, ParseTreeNode node, OlapOp function) {		
		this.level = member.getLevel();
		this.node = node;
		this.function = function;
		members.add(member);		
	}

	public LevelMemberSet(ParseTreeNode node, OlapOp function) {
		this.node = node;
		this.function = function;
	}

	public Level getLevel() {
		return level;
	}
	
	public boolean isMeasure(){
		return level.getDimension() instanceof ServerMeasureDimension;
	}

	public ParseTreeNode getNode() {
		return node;
	}

	public List<Member> getMembers() {
		return members;
	}

		
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer(level.getUniqueName());
		
		if(!members.isEmpty()){
			sb.append(" with ").append(members.size()).append(" member(s): [");
			for(Member m: members){
				if(m!=members.get(0))
					sb.append(",");
				sb.append(m.getName());
			}
			sb.append("]");
		}
		
		sb.append(" node: ");
		sb.append(ParseUtils.toString(node));

		return sb.toString();
	}

	public OlapOp getFunction() {
		return function;
	}

	public Member findMember(String key, String name) {
		
		Member m = map.get(key);
		if(m!=null)
			return m;
		
		if(topLimit>0 && members.size()>=topLimit)
			return null;
		
		LevelMember member = new LevelMember(level, key, name, members.size());
		members.add(member);
		map.put(key, member);
		
		return member;
	}

	public void setFunction(OlapOp function) {
		this.function = function;
	}

	public Sorter getSorter() {
		return sorter;
	}

	public void setSorter(Sorter sorter) {
		this.sorter = sorter;
	}

	public int getTopLimit() {
		return topLimit;
	}

	public void setTopLimit(int topLimit) {
		this.topLimit = topLimit;
	}
	
	
	
} public class ResultAxis extends ServerCellSetAxis {

	private List<LevelMemberSet> layers;
	private Map<List<Member>, Position> member_map = new HashMap<List<Member>, Position>();
	private List<Position> positions = new ArrayList<Position>();
	
	public ResultAxis(CellSet cellSet, AxisNode axisNode) throws OlapException {
		super(cellSet, axisNode.getAxis());		
		this.layers = OlapFunctionFactory.INSTANCE.function( axisNode.getExpression(), cellSet.getMetaData().getCube()).memberSet();
	}

	@Override
	public List<Position> getPositions() {
		return positions;
	}

	@Override
	protected List<Hierarchy> getHierarchies() {
		List<Hierarchy> hierarchies = new ArrayList<Hierarchy>();

		for(LevelMemberSet layer : layers){
			Hierarchy h = layer.getLevel().getHierarchy();
			if(!hierarchies.contains(h))
				hierarchies.add(h);
		}
		
		return hierarchies;
	}



	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer(getAxisMetaData().getAxisOrdinal().name());
		
		sb.append(" with ").append(layers.size()).append(" layer(s): ");
		for(LevelMemberSet set: layers){
			sb.append("\n");
			sb.append(set.toString());
		}
		
		return sb.toString();
	}

	public List<LevelMemberSet> getLayers() {
		return layers;
	}


	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<ServerMeasure> collectAllUsedMeasures() throws OlapException{
		
		Set measures = new HashSet();
		for(LevelMemberSet layer : layers){
			if(layer.isMeasure()){
				measures.addAll(layer.getMembers());
			}
			measures.addAll(layer.getFunction().getAllMeasuresUsed());
		}
		
		return measures;
		
	}
	
	public Set<ServerDimension> collectAllUsedDimensions(){
		Set<ServerDimension> dims = new HashSet<ServerDimension>();
		for(LevelMemberSet layer : layers){
			if(!layer.isMeasure()){
				dims.add((ServerDimension) layer.getLevel().getDimension());
			}
			
		}
		
		return dims;		
	}

	public int positionOrdinal(final List<Member> members) {
		
		Position p = member_map.get(members);
		 
		if(p==null){
			final int ordinal = positions.size();
			p = new Position(){

				@Override
				public List<Member> getMembers() {
					return members;
				}

				@Override
				public int getOrdinal() {				
					return ordinal;
				}
				
			};
			member_map.put(members, p);
			positions.add(p);
		}
		
		return p.getOrdinal();
		
	}
	
	
	
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.olap.server.database.physical.AggregateTable;
import org.olap.server.database.physical.DimensionTable;
import org.olap.server.database.physical.PhysicalSchema;
import org.olap.server.database.physical.TableColumn;
import org.olap.server.database.physical.TableJoin;
import org.olap.server.database.physical.TableMeasure;
import org.olap.server.driver.metadata.LiteralMeasure;
import org.olap.server.driver.metadata.ServerDimension;
import org.olap.server.driver.metadata.ServerLevel;
import org.olap.server.driver.metadata.ServerMeasure;
import org.olap.server.processor.LevelMemberSet;
import org.olap.server.processor.ResultAxis;
import org.olap4j.OlapException;
import org.olap4j.metadata.Dimension;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Member;
import com.healthmarketscience.sqlbuilder.NumberValueObject;
import com.healthmarketscience.sqlbuilder.ValueObject;
package org.olap.server.processor.sql;




public class TableMapping {
	
	private static Log log = LogFactory.getLog(SqlConnector.class);
	
	public class MeasureMapping {
		public ServerMeasure measure;
		public TableMeasure column;
		public String alias;
	}

	public class LevelMapping {
		public TableJoin join;
		public DimensionTable table;
		public TableColumn name_column;
		public String name_alias;
		
		public TableColumn key_column;
		public String key_alias;
		
		public TableColumn order_column;
	}
		
	private AggregateTable aggregate;
	
	private List<MeasureMapping> measures = new ArrayList<MeasureMapping>();
	
	private Map<ServerMeasure, MeasureMapping> measures_mapping = new IdentityHashMap<ServerMeasure, MeasureMapping>();
	private Map<Level, LevelMapping> levels_mapping = new IdentityHashMap<Level, LevelMapping>();	
	
	public TableMapping(AggregateTable t) {
		this.aggregate = t;
	}


	public static TableMapping mapToDatabase(PhysicalSchema schema, List<ResultAxis> resultAxes, ResultAxis filterAxis) throws OlapException {
		
		Set<ServerMeasure> measures = new HashSet<ServerMeasure>();
		Set<ServerDimension> dimensions = new HashSet<ServerDimension>();
		
		for(ResultAxis axis : resultAxes){
			measures.addAll(axis.collectAllUsedMeasures());
			dimensions.addAll(axis.collectAllUsedDimensions());
		}

		measures.addAll(filterAxis.collectAllUsedMeasures());
		dimensions.addAll(filterAxis.collectAllUsedDimensions());

		List<TableMapping> candidates = new ArrayList<TableMapping>();
		
		for(AggregateTable t : schema.aggregates){
			TableMapping mapping = new TableMapping(t);
			
			boolean all_mapped = true;
			for(ServerMeasure m : measures){
				if(mapping.map(m)==null){
					all_mapped = false;
					log.info("Can not use "+t.table + " missing "+m.getName()+" mapping");
					break;
				}
			}
			
			if(all_mapped){
				for(ServerDimension d : dimensions){
					if(mapping.map(d)==null){
						all_mapped = false;
						log.info("Can not use "+t.table + " missing "+d.getName()+" mapping");
						break;
					}
				}
				
			}
			
			if(all_mapped)
				candidates.add(mapping);
			
			
		}
		
		
		Collections.sort(candidates, new Comparator<TableMapping>(){

			@Override
			public int compare(TableMapping o1, TableMapping o2) {
				if(o1.scale() > o2.scale())
					return 1;
				else if(o1.scale() < o2.scale())
					return -1;
				else
					return 0;
			}
			
		});
		
		TableMapping mapping = candidates.get(0);
		log.info("Using "+ mapping.aggregate.table+" aggregate table");
		
		for(ResultAxis axis : resultAxes){
			for( LevelMemberSet layer : axis.getLayers() ){