blob: ac5e0a2b7f6b9d9565f2d04bc2a0c90862e52fcf [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*
*
* This copyright notice shows up in the generated Java code
*
*/
package org.eclipse.osbp.xtext.chart.jvmmodel
import com.ejt.vaadin.sizereporter.ComponentResizeEvent
import com.ejt.vaadin.sizereporter.ComponentResizeListener
import com.ejt.vaadin.sizereporter.SizeReporter
import com.vaadin.data.Property.ValueChangeListener
import com.vaadin.server.FileDownloader
import com.vaadin.server.Page
import com.vaadin.server.Page.Styles
import com.vaadin.server.StreamResource
import com.vaadin.server.StreamResource.StreamSource
import com.vaadin.ui.Button
import com.vaadin.ui.HorizontalLayout
import com.vaadin.ui.JavaScriptFunction
import com.vaadin.ui.Label
import com.vaadin.ui.Panel
import com.vaadin.ui.TabSheet
import com.vaadin.ui.UI
import elemental.json.JsonArray
import elemental.json.JsonException
import elemental.json.JsonObject
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.util.ArrayList
import java.util.Collection
import java.util.Date
import java.util.HashMap
import java.util.List
import java.util.Locale
import java.util.Map
import java.util.ResourceBundle
import java.util.UUID
import java.util.concurrent.Executors
import javax.imageio.ImageIO
import javax.inject.Inject
import mondrian.olap.Member.MemberType
import org.dussan.vaadin.dcharts.DCharts
import org.dussan.vaadin.dcharts.DownloadButtonLocation
import org.dussan.vaadin.dcharts.base.elements.Trendline
import org.dussan.vaadin.dcharts.base.elements.XYaxis
import org.dussan.vaadin.dcharts.base.elements.XYseries
import org.dussan.vaadin.dcharts.data.DataSeries
import org.dussan.vaadin.dcharts.data.Ticks
import org.dussan.vaadin.dcharts.metadata.LegendPlacements
import org.dussan.vaadin.dcharts.metadata.PyramidSides
import org.dussan.vaadin.dcharts.metadata.SeriesToggles
import org.dussan.vaadin.dcharts.metadata.TooltipAxes
import org.dussan.vaadin.dcharts.metadata.XYaxes
import org.dussan.vaadin.dcharts.metadata.Xaxes
import org.dussan.vaadin.dcharts.metadata.Yaxes
import org.dussan.vaadin.dcharts.metadata.directions.BarDirections
import org.dussan.vaadin.dcharts.metadata.locations.TooltipLocations
import org.dussan.vaadin.dcharts.metadata.renderers.AxisRenderers
import org.dussan.vaadin.dcharts.metadata.renderers.SeriesRenderers
import org.dussan.vaadin.dcharts.options.Axes
import org.dussan.vaadin.dcharts.options.AxesDefaults
import org.dussan.vaadin.dcharts.options.Cursor
import org.dussan.vaadin.dcharts.options.Highlighter
import org.dussan.vaadin.dcharts.options.Legend
import org.dussan.vaadin.dcharts.options.Options
import org.dussan.vaadin.dcharts.options.Series
import org.dussan.vaadin.dcharts.options.SeriesDefaults
import org.dussan.vaadin.dcharts.options.Title
import org.dussan.vaadin.dcharts.renderers.axis.LinearAxisRenderer
import org.dussan.vaadin.dcharts.renderers.label.CanvasAxisLabelRenderer
import org.dussan.vaadin.dcharts.renderers.legend.EnhancedLegendRenderer
import org.dussan.vaadin.dcharts.renderers.series.BarRenderer
import org.dussan.vaadin.dcharts.renderers.series.BubbleRenderer
import org.dussan.vaadin.dcharts.renderers.series.DonutRenderer
import org.dussan.vaadin.dcharts.renderers.series.MeterGaugeRenderer
import org.dussan.vaadin.dcharts.renderers.series.PieRenderer
import org.dussan.vaadin.dcharts.renderers.series.PyramidRenderer
import org.dussan.vaadin.dcharts.renderers.tick.AxisTickRenderer
import org.dussan.vaadin.dcharts.renderers.tick.CanvasAxisTickRenderer
import org.eclipse.e4.core.di.extensions.EventUtils
import org.eclipse.e4.ui.model.application.ui.MUIElement
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack
import org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl
import org.eclipse.e4.ui.model.application.ui.impl.UiPackageImpl
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement
import org.eclipse.emf.common.notify.Adapter
import org.eclipse.emf.common.notify.Notification
import org.eclipse.emf.common.notify.impl.AdapterImpl
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.osbp.eventbroker.EventBrokerMsg
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent
import org.eclipse.osbp.ui.api.datamart.DatamartFilter
import org.eclipse.osbp.ui.api.datamart.IDatamartFilterGenerator
import org.eclipse.osbp.utils.vaadin.ViewLayoutManager
import org.eclipse.osbp.xtext.action.ChartActionEnum
import org.eclipse.osbp.xtext.basic.generator.BasicDslGeneratorUtils
import org.eclipse.osbp.xtext.chart.Chart
import org.eclipse.osbp.xtext.chart.ChartModel
import org.eclipse.osbp.xtext.chart.ChartTree
import org.eclipse.osbp.xtext.datamart.common.DatamartFilterGenerator
import org.eclipse.osbp.xtext.datamart.common.olap.CellSetToD3JsonConverter
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedAxis
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedCell
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedHierarchy
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedLevel
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedMember
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedPosition
import org.eclipse.osbp.xtext.i18n.I18NModelGenerator
import org.eclipse.xtext.generator.IFileSystemAccess
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.compiler.GeneratorConfig
import org.eclipse.xtext.xbase.compiler.ImportManager
import org.osgi.service.event.Event
import org.osgi.service.event.EventHandler
class ChartModelGenerator extends I18NModelGenerator {
@Inject extension IQualifiedNameProvider
@Inject extension D3JsJavaUtil
@Inject extension BasicDslGeneratorUtils
override createAppendable(EObject context, ImportManager importManager, GeneratorConfig config) {
// required to initialize the needed builder to avoid deprecated methods
builder = context.eResource
// ---------
importManager.addImportFor(_typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(String), _typeReferenceBuilder.typeRef(String)).type)
importManager.addImportFor(_typeReferenceBuilder.typeRef(Map, _typeReferenceBuilder.typeRef(String), _typeReferenceBuilder.typeRef(String)).type)
importManager.addImportFor(_typeReferenceBuilder.typeRef(Collection, _typeReferenceBuilder.typeRef(String)).type)
addImportFor(importManager, _typeReferenceBuilder
, ValueChangeListener
, DCharts
, Label
, List
, ArrayList
, Page
, Styles
, DerivedAxis
, DerivedCell
, DerivedPosition
, DerivedMember
, HorizontalLayout
, TabSheet
, Series
, XYseries
, Ticks
, DataSeries
, SeriesDefaults
, SeriesRenderers
, CanvasAxisTickRenderer
, CanvasAxisLabelRenderer
, DonutRenderer
, PieRenderer
, BubbleRenderer
, BarRenderer
, BarDirections
, MeterGaugeRenderer
, PyramidRenderer
, PyramidSides
, Trendline
, Cursor
, Legend
, LegendPlacements
, EnhancedLegendRenderer
, SeriesToggles
, Highlighter
, Title
, TooltipLocations
, TooltipAxes
, Axes
, XYaxes
, XYaxis
, Xaxes
, Yaxes
, AxisTickRenderer
, AxisRenderers
, LinearAxisRenderer
, AxesDefaults
, DownloadButtonLocation
, Options
, DerivedHierarchy
, DerivedLevel
, CellSetToD3JsonConverter
, Date
, JavaScriptFunction
, JsonArray
, JsonObject
, JsonException
, EventHandler
, Event
, EventUtils
, EventBrokerMsg
, Executors
, Notification
, EObject
, UIElementImpl
, UiPackageImpl
, MUIElement
, MPartStack
, UI
, ResourceBundle
, Locale
, DatamartFilterGenerator
, ViewLayoutManager
, IDatamartFilterGenerator.FilterChangeListener
, EventDispatcherEvent
, EventDispatcherEvent.EventDispatcherDataTag
, EventDispatcherEvent.EventDispatcherCommand
, DatamartFilter
, MemberType
, Panel
, MPerspective
, ChartActionEnum
, UUID
, StreamResource
, MToolBarElement
, Button
, FileDownloader
, StreamSource
, ByteArrayOutputStream
, ImageIO
, ByteArrayInputStream
, InputStream
, Adapter
, AdapterImpl
, SizeReporter
, ComponentResizeListener
, ComponentResizeEvent
)
super.createAppendable(context, importManager, config)
}
override void doGenerate(Resource input, IFileSystemAccess fsa) {
super.addTranslatables("download")
super.doGenerate(input, fsa )
for (obj : input.contents) {
obj.internalDoGenerate(fsa)
}
}
def dispatch void internalDoGenerate(ChartModel chartModel, IFileSystemAccess fsa) {
for (pkg : chartModel.packages) {
for (chart : pkg.charts) {
if ((chart.charttype instanceof ChartTree)) {
var pckgName = pkg.fullyQualifiedName.toString
var strBuilder = new StringBuilder
var pckgNamePath = pckgName.replaceAll("\\.","/")
var propOutputFile = '''«pckgNamePath»/«chart.createChartJsFilename»'''
chart.generateJsFile(strBuilder)
if (strBuilder.length > 0){
fsa.generateFile(propOutputFile, strBuilder)
}
}
}
}
}
def void generateJsFile(Chart chart,StringBuilder strBuilder){
var chartFilePath = chart.createfullyQualifiedChartFilename.replaceAll("\\.","_")
if (chart.charttype instanceof ChartTree){
var chartTree = chart.charttype as ChartTree
if (chartTree.map){
strBuilder.generateTreeMapJsFile(chartFilePath)
} else if (chartTree.collapsible) {
strBuilder.generateCollTreeJsFile(chartFilePath)
}
}
}
def void generateTreeMapJsFile(StringBuilder strBuilder, String chartJavaFileName){
strBuilder.append(
'''
«chartJavaFileName» = function() {
var margin = {
top: 10,
right: 10,
bottom: 40,
left: 10
};
var width = this.getState().panelWidth;
var height = this.getState().panelHeight;
var color = d3.scaleOrdinal(d3.schemeCategory10);
var divId = this.getState().htmlTagId+"_div";
var inputId = this.getState().htmlTagId+"_input";
var element = this.getElement();
var data = JSON.parse(this.getState().jsonData);
var checked = "";
var connector = this;
var dataColumn = "";
var preValueLabel = "";
for (var i=0;i < this.getState().dataColumnList.length;i++) {
if (i == 0){
checked = " checked";
preValueLabel = this.getState().dataColumnList[0];
} else {
checked = "";
}
dataColumn = dataColumn + "\t<label><input id='"+inputId+"' type='radio' name='mode' onChange='update(\""+this.getState().dataColumnList[i]+"\")' value='"+ this.getState().dataColumnList[i] + "'" + checked + "> "+ this.getState().dataColumnList[i] + "</label>\n";
}
var treemap = d3.treemap()
.size([width-margin.right-margin.left, height-margin.top-margin.bottom])
.paddingInner(1)
.round(true)
.tile(d3.treemapSquarify);
element.innerHTML = "<form>\n"+dataColumn+"</form>\n"+"<div id=\""+divId+"\">" + "</div>";
var div = d3.select(element).select("#"+divId);
var treeDiv = div.append("div")
.style("position", "relative")
.style("width", width + "px")
.style("height", height + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
update = function(valueLabel) {
const newRoot = d3.hierarchy(data);
newRoot.sum((d) =>
d.children).sum((d) =>
eval("d." + valueLabel));
newRoot.sort((a,b) =>
eval("b." + valueLabel)-eval("a." + valueLabel));
const node = treeDiv.datum(newRoot).selectAll(".node")
.data(treemap(newRoot).leaves())
.attr("class", "node")
.attr("title", function(d) { return d.children ? "" : d.data.tooltipName + "\n" + valueLabel + ": " + eval("d.data." + valueLabel); })
.transition()
.duration(1500)
.style("left", (d) => d.x0 + "px")
.style("top", (d) => d.y0 + "px")
.style("width", (d) => Math.max(0, d.x1 - d.x0 - 1) + "px")
.style("height", (d) => Math.max(0, d.y1 - d.y0 - 1) + "px");
}
render = function() {
const root = d3.hierarchy(data);
root.sum((d) =>
d.children).sum((d) =>
eval("d." + preValueLabel));
root.sort((a,b) =>
eval("b." + preValueLabel)-eval("a." + preValueLabel));
const node = treeDiv.datum(root).selectAll(".node")
.data(treemap(root).leaves())
.enter().append("div")
.attr("class", "node")
.attr("title", function(d) {
return d.data.tooltipName + "\n" + preValueLabel + ": " + eval("d.data." + preValueLabel);
})
.style("left", (d) => d.x0 + "px")
.style("top", (d) => d.y0 + "px")
.style("width", (d) => Math.max(0, d.x1 - d.x0 - 1) + "px")
.style("height", (d) => Math.max(0, d.y1 - d.y0 - 1) + "px")
.style("background", (d) => {
while (d.depth > 2) d = d.parent;
return color(d.data.name);})
.text(function(d) {
return d.data.name;
});
}
render();
}
''')
}
def void generateCollTreeJsFile(StringBuilder strBuilder, String chartJavaFileName){
strBuilder.append(
'''
«chartJavaFileName» = function() {
var margin = {
top: 10,
right: 10,
bottom: 40,
left: 10
};
var width = this.getState().panelWidth;
var height = this.getState().panelHeight;
var i = 0,
duration = 750,
root;
var divId = this.getState().htmlTagId+"_div";
var inputId = this.getState().htmlTagId+"_input";
var element = this.getElement();
var data = JSON.parse(this.getState().jsonData);
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
change = function(value) {
//alert("value = " + value);
if (value === "expand"){
data.children.forEach(expand);
} else {
data.children.forEach(collapse);
}
update(data);
}
element.innerHTML =
"<label><input id=\""+inputId+"\" type=\"radio\" name=\"mode\" onChange='change(\"expand\")' value=\"expand\">Expand</label>\n" +
"<label><input id=\""+inputId+"\" type=\"radio\" name=\"mode\" onChange='change(\"collapse\")' value=\"collapse\" checked>Collapse</label>\n" +
"<div id=\""+divId+"\">" + "</div>";
var div = d3.select(element).select("#"+divId);
var svg = div.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function update(source) {
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
var tree = d3.tree()
.size([width>height?height:width, width>height?height:width]);
var treeData = tree(root);
// Compute the new tree layout.
var nodes = treeData.descendants();
var links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// ****************** Nodes section ***************************
// Update the nodes
var node = svg.selectAll("g.node").data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("circle")
.attr("class", "node")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("dy", ".35em")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.data.name;
});
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition nodes to their new position.
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle.node")
.attr("r", 4.5)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle").attr("r", 1e-6);
nodeExit.select("text").style("fill-opacity", 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){
return diagonal(d, d.parent)
});
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
}
root = d3.hierarchy(data, function(d) {
return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
root.children.forEach(expand);
update(root);
}
''')
}
}