/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.starmodeler;
import java.util.ArrayList;
import java.util.List;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.gui.GCInterface;
import org.pentaho.di.core.gui.Point;
import org.pentaho.di.core.gui.PrimitiveGCInterface.EColor;
import org.pentaho.metadata.model.LogicalModel;
import org.pentaho.metadata.model.LogicalRelationship;
import org.pentaho.metadata.model.LogicalTable;
import org.pentaho.metadata.model.concept.types.TableType;
import org.pentaho.pms.schema.concept.DefaultPropertyID;
public class StarModelPainter {
private LogicalModel logicalModel;
private GCInterface gc;
private Point area;
private String locale;
private List<LogicalRelationship> logicalRelationships;
/**
* @param gc The graphical context to draw the logical model on
* @param logicalModel The model to depict
* @param logicalRelationships
* @param relationships the relationships (dynamically derived from a UI)
* @param locale the locale to use to draw names of tables.
*
*/
public StarModelPainter(GCInterface gc, LogicalModel logicalModel, List<LogicalRelationship> logicalRelationships, String locale) {
this.gc = gc;
this.area = gc.getArea();
this.logicalModel = logicalModel;
this.logicalRelationships = logicalRelationships;
this.locale = locale;
}
public void draw() {
gc.setAntialias(true);
Point center = new Point(area.x/2, area.y/2);
gc.setBackground(EColor.BACKGROUND);
gc.setForeground(EColor.BLACK);
gc.fillRectangle(0,0,area.x, area.y);
// gc.drawText("bounds: x="+rect.x+", y="+rect.y+", height="+rect.height+", width="+rect.width, 10, 10);
List<LogicalTable> tableList = new ArrayList<LogicalTable>();
tableList.addAll(logicalModel.getLogicalTables());
// Find the fact...
//
LogicalTable fact = null;
for (LogicalTable logicalTable : tableList) {
if (TableType.FACT == ConceptUtil.getTableType(logicalTable)) {
fact=logicalTable;
}
}
if (fact!=null) {
tableList.remove(fact);
}
int maxWidth = Integer.MIN_VALUE;
for (LogicalTable table : tableList) {
String name = table.getName(locale);
if (!Utils.isEmpty(name)) {
Point p = gc.textExtent(name);
if (p.x>maxWidth) maxWidth=p.x;
}
}
List<TablePoint> points = new ArrayList<TablePoint>();
if (fact!=null) {
points.add(new TablePoint(fact, center));
}
// Draw the other dimensions around the fact...
//
if (!tableList.isEmpty()) {
List<TablePoint> dimPoints = getCirclePoints(center, center.x-maxWidth/2-20, center.y-20, tableList);
points.addAll(dimPoints);
}
// Draw relationships first...
//
for (LogicalRelationship rel : logicalRelationships) {
LogicalTable fromTable = rel.getFromTable();
LogicalTable toTable = rel.getToTable();
Point from = findPointOfTable(points, fromTable);
Point to = findPointOfTable(points, toTable);
if (from!=null && to!=null) {
gc.drawLine(from.x, from.y, to.x, to.y);
}
}
// Then fill and draw all the ovals.
//
for (TablePoint tablePoint : points) {
LogicalTable table = tablePoint.logicalTable;
Point point = tablePoint.point;
drawCircleName(point, table);
}
}
private class TablePoint {
public LogicalTable logicalTable;
public Point point;
private TablePoint(LogicalTable logicalTable, Point point) {
this.logicalTable = logicalTable;
this.point = point;
}
}
private List<TablePoint> getCirclePoints(Point center, int width, int heigth, List<LogicalTable> tableList) {
List<TablePoint> points = new ArrayList<TablePoint>();
int nrPoints = tableList.size();
double alpha = Math.PI * 2 / nrPoints;
for (int i=0;i<nrPoints;i++) {
double tetha = alpha*i;
Point point = new Point(center.x, center.y);
point.x += (int)Math.round(Math.cos(tetha)*width);
point.y += (int)Math.round(Math.sin(tetha)*(heigth-5));
points.add(new TablePoint(tableList.get(i), point));
}
return points;
}
private Point findPointOfTable(List<TablePoint> points, LogicalTable fromTable) {
for (TablePoint tablePoint : points) {
if (tablePoint.logicalTable.equals(fromTable)) {
return tablePoint.point;
}
}
return null;
}
private void drawCircleName(Point center, LogicalTable logicalTable) {
String name = ConceptUtil.getName(logicalTable, locale);
if (!Utils.isEmpty(name)) {
Point nameSize = gc.textExtent(name);
int margin=20;
nameSize.x+=margin;
nameSize.y+=margin;
EColor bg = EColor.BACKGROUND;
TableType tableType = (TableType) logicalTable.getProperty(DefaultPropertyID.TABLE_TYPE.getId());
if (tableType!=null && TableType.FACT==tableType) {
bg = EColor.LIGHTGRAY;
}
gc.setBackground(bg);
gc.fillRoundRectangle(center.x-nameSize.x/2, center.y-nameSize.y/2, nameSize.x, nameSize.y, 20, 20);
gc.drawRoundRectangle(center.x-nameSize.x/2, center.y-nameSize.y/2, nameSize.x, nameSize.y, 20, 20);
gc.drawText(name, center.x-nameSize.x/2+margin/2, center.y-nameSize.y/2+margin/2, true);
gc.setBackground(EColor.BACKGROUND);
}
}
/**
* @return the logicalModel
*/
public LogicalModel getLogicalModel() {
return logicalModel;
}
/**
* @param logicalModel the logicalModel to set
*/
public void setLogicalModel(LogicalModel logicalModel) {
this.logicalModel = logicalModel;
}
/**
* @return the gc
*/
public GCInterface getGc() {
return gc;
}
/**
* @param gc the gc to set
*/
public void setGc(GCInterface gc) {
this.gc = gc;
}
}