| /** |
| * |
| * 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.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 |
| , 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); |
| } |
| ''') |
| } |
| } |