blob: 58d885aca9f551845dd60e91e1333a0210a97063 [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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.github.wolfie.refresher.Refresher
import com.vaadin.server.ClientConnector.AttachEvent
import com.vaadin.server.ClientConnector.AttachListener
import com.vaadin.server.ClientConnector.DetachEvent
import com.vaadin.server.ClientConnector.DetachListener
import com.vaadin.ui.Component
import com.vaadin.ui.VerticalLayout
import elemental.json.JsonException
import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
import java.util.Locale
import java.util.Set
import javax.inject.Inject
import org.eclipse.e4.core.contexts.IEclipseContext
import org.eclipse.e4.core.services.events.IEventBroker
import org.eclipse.e4.ui.di.Focus
import org.eclipse.e4.ui.model.application.MApplication
import org.eclipse.osbp.dsl.semantic.common.types.LAttribute
import org.eclipse.osbp.dsl.semantic.entity.LEntity
import org.eclipse.osbp.osgi.hybrid.api.AbstractHybridVaaclipseView
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent
import org.eclipse.osbp.runtime.common.event.IEventDispatcher
import org.eclipse.osbp.ui.api.datamart.DatamartFilter
import org.eclipse.osbp.ui.api.datamart.IDatamartFilterGenerator
import org.eclipse.osbp.ui.api.e4.IE4Focusable
import org.eclipse.osbp.ui.api.layout.IViewLayoutManager
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService
import org.eclipse.osbp.ui.api.themes.EnumCssClass
import org.eclipse.osbp.ui.api.user.IUser
import org.eclipse.osbp.utils.vaadin.PropertyLookup
import org.eclipse.osbp.xtext.chart.Chart
import org.eclipse.osbp.xtext.chart.ChartAxis
import org.eclipse.osbp.xtext.chart.ChartBar
import org.eclipse.osbp.xtext.chart.ChartBubble
import org.eclipse.osbp.xtext.chart.ChartDatamart
import org.eclipse.osbp.xtext.chart.ChartDonut
import org.eclipse.osbp.xtext.chart.ChartGauge
import org.eclipse.osbp.xtext.chart.ChartHighlighter
import org.eclipse.osbp.xtext.chart.ChartLegend
import org.eclipse.osbp.xtext.chart.ChartLine
import org.eclipse.osbp.xtext.chart.ChartNumberInterval
import org.eclipse.osbp.xtext.chart.ChartPackage
import org.eclipse.osbp.xtext.chart.ChartPie
import org.eclipse.osbp.xtext.chart.ChartSegmentColor
import org.eclipse.osbp.xtext.chart.ChartTree
import org.eclipse.osbp.xtext.cubedsl.CubeLevel
import org.eclipse.osbp.xtext.datamart.common.olap.DerivedCellSet
import org.eclipse.osbp.xtext.datamartdsl.DatamartAttribute
import org.eclipse.osbp.xtext.datamartdsl.DatamartCube
import org.eclipse.osbp.xtext.datamartdsl.DatamartCubeAxis
import org.eclipse.osbp.xtext.datamartdsl.DatamartDerivedMeasure
import org.eclipse.osbp.xtext.datamartdsl.DatamartEntity
import org.eclipse.osbp.xtext.datamartdsl.DatamartMeasure
import org.eclipse.osbp.xtext.datamartdsl.DatamartMember
import org.eclipse.osbp.xtext.datamartdsl.DatamartOwner
import org.eclipse.osbp.xtext.datamartdsl.DatamartPackage
import org.eclipse.osbp.xtext.datamartdsl.ValueScaleEnum
import org.eclipse.osbp.xtext.datamartdsl.jvmmodel.DatamartDSLJvmModelInferrer
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmOperation
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.osgi.service.event.EventHandler
import org.slf4j.Logger
import org.dussan.vaadin.dcharts.DCharts
/**
* <p>
* This inferrer infers models of extension .chart and generates code to be used by a e4 application as view. Underlying components
* are from the vaadin component repository and especially the vaadin addon dChart.
* </p>
*
* @author Joerg Riegel
*/
/**
* <p>Infers a JVM model from the source model.</p>
*
* <p>The JVM model should contain all elements that would appear in the Java code
* which is generated from the source model. Other models link against the JVM model rather than the source model.</p>
*/
class ChartDSLJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
@Inject extension DatamartDSLJvmModelInferrer datamartInferrer
@Inject extension ChartDSLD3JavaComponentsCreator
/**
* <p>infer method dispatches the necessary routines to build fields, setter, getter, constructors and methods of the generated code.</p>
*
*/
def dispatch void infer(ChartPackage pckg, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
for (chart:pckg.charts) {
var cls = chart.toClass(pckg.fullyQualifiedName+"."+chart.name+"Chart")
cls.superTypes.add(_typeReferenceBuilder.typeRef(AbstractHybridVaaclipseView))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IUser.UserLocaleListener))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IDatamartFilterGenerator.FilterChangeListener))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IEventDispatcher.Receiver))
cls.superTypes.add(_typeReferenceBuilder.typeRef(DetachListener))
cls.superTypes.add(_typeReferenceBuilder.typeRef(AttachListener))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IE4Focusable))
acceptor.accept(cls,
[
it.fileHeader = pckg.documentation
it.toFields(chart)
it.toConstructor(chart)
it.toGetterSetter(chart)
it.toOperations(chart)
])
if ((chart.charttype instanceof ChartTree)) {
chart.createJsJavaComponent(acceptor)
}
}
}
/**
* <p>build the constructors to be used by an e4 application.</p>
*
*/
def void toConstructor(JvmDeclaredType type, Chart chart) {
type.members += chart.toConstructor([
annotations += _annotationTypesBuilder.annotationRef(Inject)
parameters += chart.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
parameters += chart.toParameter("context", _typeReferenceBuilder.typeRef(IEclipseContext))
parameters += chart.toParameter("app", _typeReferenceBuilder.typeRef(MApplication))
body = [ append(
'''
super(parent,context,app);
parent.addAttachListener(this);
parent.addDetachListener(this);''')]
])
}
/**
* <p>build the class variables.</p>
*
*/
def void toFields(JvmDeclaredType type, Chart chart) {
var JvmField field = null
// create logger
field = chart.toField("log", _typeReferenceBuilder.typeRef(Logger))[setInitializer([append('''org.slf4j.LoggerFactory.getLogger("charts")''')])]
field.static = true
field.visibility = JvmVisibility::PRIVATE
type.members += field
field = chart.toField("user", _typeReferenceBuilder.typeRef(IUser)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = chart.toField("eclipseContext", _typeReferenceBuilder.typeRef(IEclipseContext)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = chart.toField("coordinateSystem", _typeReferenceBuilder.typeRef(ArrayList, _typeReferenceBuilder.typeRef(Integer)))
type.members += field
field = chart.toField("filterGenerator", _typeReferenceBuilder.typeRef(IDatamartFilterGenerator))
type.members += field
field = chart.toField("layoutManager", _typeReferenceBuilder.typeRef(IViewLayoutManager))
type.members += field
// the package name of the referenced datamart
if (getSourceDataMartRefName(chart) != null) {
var packageName = (chart.source.datamartRef.eContainer as DatamartPackage).fullyQualifiedName.toString
field = chart.toField("datamartInstance", _typeReferenceBuilder.typeRef('''«packageName».«chart.source.datamartRef.name.toString»Datamart'''))
type.members += field
}
field = chart.toField("chartRefresher", _typeReferenceBuilder.typeRef(Refresher))
type.members += field
field = chart.toField("chartRefresherDone", _typeReferenceBuilder.typeRef(boolean))
type.members += field
field = chart.toField("chartRenderDelay", _typeReferenceBuilder.typeRef(Refresher))
type.members += field
field = chart.toField("dataComponent", _typeReferenceBuilder.typeRef(Component))
type.members += field
field = chart.toField("attributeLookupMap", _typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(String), _typeReferenceBuilder.typeRef(PropertyLookup)))
type.members += field
field = chart.toField("charts", _typeReferenceBuilder.typeRef(ArrayList, _typeReferenceBuilder.typeRef(DCharts)))
type.members += field
field = chart.toField("eventBroker", _typeReferenceBuilder.typeRef(IEventBroker)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = chart.toField("refreshView", _typeReferenceBuilder.typeRef(EventHandler))
type.members += field
field = chart.toField("dslMetadataService", _typeReferenceBuilder.typeRef(IDSLMetadataService)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = chart.toField("eventDispatcher", _typeReferenceBuilder.typeRef(IEventDispatcher))[annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
}
/**
* <p>build the getters and setters from class variables.</p>
*
*/
def void toGetterSetter(JvmDeclaredType type, Chart chart) {
var JvmOperation operation = null
operation = chart.toGetter("coordinateSystem", _typeReferenceBuilder.typeRef(ArrayList, _typeReferenceBuilder.typeRef(Integer)))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
}
/**
* <p>build the methods.</p>
*
*/
def void toOperations(JvmDeclaredType type, Chart chart) {
// attach
type.members += chart.toMethod("attach", _typeReferenceBuilder.typeRef(Void::TYPE),
[
parameters += chart.toParameter("event", _typeReferenceBuilder.typeRef(AttachEvent))
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [append(
'''
user.addUserLocaleListener(this);
filterGenerator.addFilterChangeListener(this);
eventDispatcher.addEventReceiver(this);''')]
])
// detach
type.members += chart.toMethod("detach", _typeReferenceBuilder.typeRef(Void::TYPE),
[
parameters += chart.toParameter("event", _typeReferenceBuilder.typeRef(DetachEvent))
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [append(
'''
user.removeUserLocaleListener(this);
filterGenerator.removeFilterChangeListener(this);
eventDispatcher.removeEventReceiver(this);''')]
])
// create view
type.members += chart.toMethod("createView", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += chart.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
body = [ append('''«chart.createView»''')]
])
// create components
type.members += chart.toMethod("createComponents", _typeReferenceBuilder.typeRef(Void::TYPE), [
body = [ append('''«chart.createComponents»''')]
])
// create tab
type.members += chart.toMethod("createTabSheet", _typeReferenceBuilder.typeRef(Component), [
parameters += chart.toParameter("cellSet", _typeReferenceBuilder.typeRef(DerivedCellSet))
parameters += chart.toParameter("axisNo", _typeReferenceBuilder.typeRef(Integer))
body = [ append('''«chart.createTabSheet»''')]
])
// create chart
if (!((chart.charttype instanceof ChartTree))) {
type.members += chart.toMethod("createChart", _typeReferenceBuilder.typeRef(Component), [
parameters += chart.toParameter("cellSet", _typeReferenceBuilder.typeRef(DerivedCellSet))
body = [ append('''«chart.createChart»''')]
])
}
// create D3 chart
if ((chart.charttype instanceof ChartTree)) {
type.members += chart.toMethod("createD3Chart", _typeReferenceBuilder.typeRef(Component), [
exceptions += _typeReferenceBuilder.typeRef(JsonException)
parameters += chart.toParameter("cxCellSet", _typeReferenceBuilder.typeRef(DerivedCellSet))
body = [ append('''«chart.createD3Chart»''')]
])
}
// focus
type.members += chart.toMethod("setFocus", _typeReferenceBuilder.typeRef(Void::TYPE),
[
annotations += _annotationTypesBuilder.annotationRef(Focus)
body = [append(
'''
Component parent = getParent();
while(!(parent instanceof Panel) && parent != null) {
parent = parent.getParent();
}
if(parent != null) {
((Panel)parent).focus();
}''')]
])
// locale notification
type.members += chart.toMethod("localeChanged", _typeReferenceBuilder.typeRef(Void::TYPE), [
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += chart.toParameter("locale", _typeReferenceBuilder.typeRef(Locale))
body = [append('''«chart.localeChanged»''')]
])
// filter notification
type.members += chart.toMethod("filterChanged", _typeReferenceBuilder.typeRef(Void::TYPE),
[
parameters += chart.toParameter("changedFilter", _typeReferenceBuilder.typeRef(DatamartFilter))
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [append('''renderData();''')]
])
// event notification
type.members += chart.toMethod("receiveEvent", _typeReferenceBuilder.typeRef(Void::TYPE),
[
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += chart.toParameter("event", _typeReferenceBuilder.typeRef(EventDispatcherEvent))
body = [append('''«chart.receiveEvent»''')]
])
}
def String getSourceDataMartRefName(Chart chart) {
if (chart.source != null && chart.source.datamartRef != null && chart.source.datamartRef.name != null) {
return chart.source.datamartRef.name.toString
}
else {
return null;
}
}
/**
* <p>build the main method to be called from e4.</p>
*
*/
def String createView(Chart chart) {
var body =
'''
eclipseContext.set(IE4Focusable.class, this);
chartRefresherDone = false;
charts = new ArrayList<DCharts>();
coordinateSystem = new ArrayList<Integer>();
parent.setPrimaryStyleName("osbp"); // <== is THIS necessary any more???
parent.addStyleName("«EnumCssClass.VIEW.styleName»");
parent.addStyleName("«EnumCssClass.CHART_VIEW.styleName»");
parent.setSizeFull();
layoutManager = new ViewLayoutManager();
layoutManager.init(parent);
datamartInstance = new «getSourceDataMartRefName(chart)»Datamart();
datamartInstance.setUser(user);
filterGenerator = new DatamartFilterGenerator(datamartInstance, dslMetadataService);
filterGenerator.createUIFilters(layoutManager);
chartRefresher = new Refresher();
chartRenderDelay = new Refresher();
attributeLookupMap = new HashMap<String,PropertyLookup>();
'''
body = '''
«body»
refreshView = «chart.refreshView»
'''
// initial refresh due to a bug in DChart
body = '''
«body»
chartRenderDelay.setRefreshInterval(100);
chartRenderDelay.addListener(new com.github.wolfie.refresher.Refresher.RefreshListener() {
@Override
public void refresh(final Refresher source) {
// refresh only once
layoutManager.removeExtension(source);
log.debug("initial - show delayed");
for(DCharts chart : charts) {
chart.show();
}
chartRefresherDone = true;
}
});
layoutManager.addExtension(chartRenderDelay);
'''
if (chart.polling) {
body = '''
«body»
chartRefresher.setRefreshInterval(«chart.pollingTime»*1000);
chartRefresher.addListener(new com.github.wolfie.refresher.Refresher.RefreshListener() {
@Override
public void refresh(final Refresher source) {
renderData();
}
});
layoutManager.addExtension(chartRefresher);
'''
}
body = '''
«body»// to resize the chart according to browser
Page.getCurrent().addBrowserWindowResizeListener(new BrowserWindowResizeListener() {
@Override
public void browserWindowResized(BrowserWindowResizeEvent event) {
log.debug("browserWindowResized - mark dirty");
getParent().markAsDirtyRecursive();
}
});
// to resize the chart according to split positions of partsashcontainer - setContainerData is tracked
EObject container = ((EObject)eclipseContext.get(MUIElement.class)).eContainer();
if(!(container instanceof MPartStack)) {
container = (EObject)eclipseContext.get(MUIElement.class);
}
container.eAdapters().add(new AdapterImpl() {
public void notifyChanged(Notification notification) {
if (notification.getEventType()==Notification.SET &&
notification.getFeatureID(UIElementImpl.class) == UiPackageImpl.UI_ELEMENT__CONTAINER_DATA)
{
log.debug("split position changed - mark dirty");
getParent().markAsDirtyRecursive();
}
}
});
'''
return body
}
/**
* <p>create a handler for external triggered refresh requests.</p>
*
*/
def String refreshView(Chart chart) {
var body = ""
body = '''
«body»
new EventHandler() {
@Override
public void handleEvent(Event event) {
renderData();
}
};
'''
return body
}
/**
* <p>build the data components.</p>
*
*/
def String createComponents(Chart chart) {
var body = ""
if (chart.source != null) {
body = '''
«body»// get the results
final DerivedCellSet cellSet = datamartInstance.getResults(getTaskOperativeDtoClass(), getTaskInitialOperativeDtos());
getCoordinateSystem().clear();
if (cellSet == null) {
promptSecurityMessage(dslMetadataService.translate(user.getLocale().toLanguageTag(), "securityMessage"), layoutManager.getDataArea());
return;
} else {
layoutManager.getDataArea().removeAllComponents();
}
// generate a new result component
if (cellSet != null) {
// create a multidimensional coordinate system against the cellSet
for (int axis = 0; axis < cellSet.getAxes().size(); axis++) {
getCoordinateSystem().add(0);
}
// remove any previous component
if (dataComponent != null) {
layoutManager.getDataArea().removeComponent(dataComponent);
dataComponent = null;
}
if (cellSet.getAxes().size() < 2) {
log.error("at least 2 axes from referenced datamart «chart.source.datamartRef.name» are needed to render «chart.name»");
} else {
dataComponent = createTabSheet(cellSet, cellSet.getAxes().size());
if(dataComponent != null) {
dataComponent.setSizeFull();
dataComponent.setId("dataComponent");
dataComponent.addStyleName("«EnumCssClass.DATA_COMPONENT.styleName»");
layoutManager.getDataArea().addComponent(dataComponent);
layoutManager.getDataArea().setExpandRatio(dataComponent, 1);
}
}
}
else {
log.error("referenced datamart «chart.source.datamartRef.name» generates no results");
}
'''
} else {
body = '''
«body»
// generate a new result component
try {
// remove any previous component
if (dataComponent != null) {
layoutManager.getDataArea().removeComponent(dataComponent);
dataComponent = null;
}
dataComponent = createTabSheet(null, 2);
dataComponent.setSizeFull();
dataComponent.setId("dataComponent");
dataComponent.addStyleName("«EnumCssClass.DATA_COMPONENT.styleName»");
layoutManager.getDataArea().addComponent(dataComponent);
layoutManager.getDataArea().setExpandRatio(dataComponent, 1);
} catch (DerivedOlapException e) {
log.error(e.getLocalizedMessage());
}
'''
}
return body
}
/**
* <p>if more than 2 axes (dimensions) are found in the cellSet, build recursive tabsheets as long as the remaining axes are more than 2.</p>
*
*/
def String createTabSheet(Chart chart) {
var body =
'''
charts.clear();
// either create a recursive tabsheet or a chart
Component component = null;
if (axisNo == 2) {
'''
if ((chart.charttype instanceof ChartTree)) {
body = '''
«body»
try {
component = createD3Chart(cellSet);
} catch (JsonException e) {
log.error(e.getLocalizedMessage());
}
'''
}
else {
body = '''
«body»
component = createChart(cellSet);
'''
}
body = '''
«body»
}
else {
Integer axis = axisNo-1;
TabSheet tabsheet = new TabSheet();
tabsheet.setSizeFull();
DerivedAxis tabAxis = cellSet.getAxes().get(axis);
// create a tab page for all tab axis members
int tabNo = 0;
for (DerivedPosition column : tabAxis.getPositions()) {
// create the title for the axis
String title = null;
for (DerivedMember member : column.getMembers()) {
if (title == null) {
title = dslMetadataService.translate(user.getLocale().toLanguageTag(), member.getCaption());
}
else {
title += " / "+dslMetadataService.translate(user.getLocale().toLanguageTag(), member.getCaption());
}
}
// position the data to this coordinate
getCoordinateSystem().set(axis, tabNo);
component = createTabSheet(cellSet, axis);
// set the caption
if (component != null) {
component.setCaption(title);
// add the component as a tab page
tabsheet.addComponent(component);
}
tabNo++;
}
component = tabsheet;
}
return component;'''
return body
}
/**
* <p>build the chart components.</p>
*
*/
def String createChart(Chart chart) {
var body = ""
var postfix = ""
var chartType = ""
var hasAxis = true
var multipleAxes = <String, String>newHashMap
var ChartAxis category_axis = null
var ChartAxis data_axis = null
var ChartAxis inner_axis = null
var ChartAxis outer_axis = null
var axisSwitch = false;
var axisPrefix = 'XY'
if (chart.charttype instanceof ChartBar) {
if ((chart.charttype as ChartBar).swapped) {
axisPrefix = 'YX'
axisSwitch = true;
}
}
for (element : chart.source.elements) {
if (element instanceof ChartAxis) {
var axis = element as ChartAxis
if (axis.axis.name.getName.equals('COLUMNS') || axis.axis.name.getName.equals('ROWS')) {
if ((axis.renderType.getName.equals("LOG") || axis.renderType.getName.equals("LINEAR") || axis.renderType.getName.equals("PYRAMID"))) {
data_axis = axis
}
if ((axis.renderType.getName.equals("CATEGORY") || axis.renderType.getName.equals("DATE"))) {
category_axis = axis
}
}
}
}
if (chart.charttype instanceof ChartBar) {
chartType = "BAR"
postfix = '''_«chartType»'''
inner_axis = category_axis
outer_axis = data_axis
}
else if (chart.charttype instanceof ChartBubble) {
chartType = "BUBBLE"
postfix = '''_«chartType»'''
hasAxis = true
inner_axis = data_axis
outer_axis = category_axis
}
else if (chart.charttype instanceof ChartDonut) {
chartType = "DONUT"
hasAxis = false
inner_axis = category_axis
outer_axis = data_axis
}
else if (chart.charttype instanceof ChartLine) {
chartType = "LINE"
inner_axis = category_axis
outer_axis = data_axis
}
else if (chart.charttype instanceof ChartPie) {
chartType = "PIE"
hasAxis = false
inner_axis = data_axis
outer_axis = category_axis
}
else if (chart.charttype instanceof ChartGauge) {
chartType = "METER_GAUGE"
hasAxis = false
inner_axis = data_axis
outer_axis = category_axis
}
body =
'''
List<Integer> coordinate = new ArrayList<Integer>(getCoordinateSystem());
'''
body = '''
«body»// create a label series
List<String> «data_axis.axis.name.literal»TitlesArray = new ArrayList<String>();
for (DerivedPosition pos : cellSet.getAxes().get(«data_axis.axis.name.value»).getPositions()) {
'''
body = '''
«body»
String title = null;
for (DerivedMember member : pos.getMembers()) {
if(member.getType() == MemberType.MEASURE) {
if (title == null) {
title = dslMetadataService.translate(user.getLocale().toLanguageTag(), member.getCaption());
}
else {
title += " "+dslMetadataService.translate(user.getLocale().toLanguageTag(), member.getCaption());
}
}
}
if(title != null) {
«data_axis.axis.name.literal»TitlesArray.add(title);
}
}
'''
if (hasAxis) {
body = '''
«body»Series «data_axis.axis.name.literal»Series = new Series();
'''
body = '''
«body»if («data_axis.axis.name.literal»TitlesArray.size() > 0) {
'''
// evaluate multiple scales
var showMarker = false
if (chart.charttype instanceof ChartLine) {
showMarker = (chart.charttype as ChartLine).showMarker
}
body = '''«body»«chart.createLabelSeries(data_axis, multipleAxes, axisPrefix, showMarker)»'''
body = '''
«body»}
'''
}
body = '''
«body»// create the data series ticks
String «category_axis.axis.name.literal»AxisLabel = "";
Boolean «category_axis.axis.name.literal»AxisLabelSet = false;
List<String> «category_axis.axis.name.literal»TitlesArray = new ArrayList<String>();
for (DerivedPosition pos : cellSet.getAxes().get(«category_axis.axis.name.value»).getPositions()) {
String title = "";
for (DerivedMember member : pos.getMembers()) {
if(member.getType() == MemberType.REGULAR) {
String[] tokens = member.getUniqueName().split("\\]\\.\\[");
if (!«category_axis.axis.name.literal»AxisLabelSet) {
if («category_axis.axis.name.literal»AxisLabel.length() > 0) {
«category_axis.axis.name.literal»AxisLabel += " / ";
}
«category_axis.axis.name.literal»AxisLabel += dslMetadataService.translate(user.getLocale().toLanguageTag(), member.getLevel().getName());
}
int start = 1;
// if shortlabel is configured, use only last level name
if («category_axis.shortLabel.booleanValue») {
start = tokens.length-1;
}
for (int token = start; token < tokens.length; token++) {
title += dslMetadataService.translate(user.getLocale().toLanguageTag(), tokens[token])+" ";
}
}
}
if(title != null) {
«category_axis.axis.name.literal»AxisLabelSet = true;
«category_axis.axis.name.literal»TitlesArray.add(title.trim().replace("[", "").replace("]", ""));
}
}
'''
if (hasAxis) {
body = '''
«body»Ticks «category_axis.axis.name.literal»Ticks = new Ticks();
«category_axis.axis.name.literal»Ticks.add(«category_axis.axis.name.literal»TitlesArray.toArray());
'''
}
body = '''
«body»
if(«category_axis.axis.name.literal»TitlesArray.isEmpty()) {
return null;
}
// copy cellset data to data series
DataSeries dataSeries = new DataSeries();
'''
if (chart.charttype instanceof ChartBubble) {
body = '''
«body»dataSeries.newSeries();
'''
}
body = '''
«body»int «outer_axis.axis.name.literal»No = 0;
'''
body = '''
«body»for (DerivedPosition «outer_axis.axis.name.literal»Pos : cellSet.getAxes().get(«outer_axis.axis.name.value»).getPositions()) {
'''
body = '''
«body» coordinate.set(«outer_axis.axis.name.value», «outer_axis.axis.name.literal»No);
'''
if (chart.charttype instanceof ChartDonut) {
body = '''
«body» dataSeries.newSeries();
'''
}
if (!(chart.charttype instanceof ChartPie) && !(chart.charttype instanceof ChartDonut)) {
body = '''
«body» ArrayList<Object> «inner_axis.axis.name.literal»DataSeries = new ArrayList<Object>();
'''
}
body = '''
«body» if(«outer_axis.axis.name.literal»Pos.getMembers().get(0).getType() == MemberType.«IF outer_axis==data_axis»MEASURE«ELSE»REGULAR«ENDIF») {
'''
body = '''
«body» int «inner_axis.axis.name.literal»No = 0;
'''
body = '''
«body» for (DerivedPosition «inner_axis.axis.name.literal»Pos : cellSet.getAxes().get(«inner_axis.axis.name.value»).getPositions()) {
'''
body = '''
«body» if(«inner_axis.axis.name.literal»Pos.getMembers().get(0).getType() == MemberType.«IF inner_axis==data_axis»MEASURE«ELSE»REGULAR«ENDIF») {
'''
if (chart.charttype instanceof ChartPie) {
body = '''
«body» dataSeries.newSeries();
'''
}
body = '''
«body» coordinate.set(«inner_axis.axis.name.value», «inner_axis.axis.name.literal»No);
'''
body = '''
«body»
Object value = null;
DerivedCell cell = cellSet.getCell(coordinate);
if (cell != null) {
value = cell.getValue();
}
'''
if (chart.charttype instanceof ChartPie || chart.charttype instanceof ChartDonut) {
body = '''
«body» dataSeries.add(«outer_axis.axis.name.literal»TitlesArray.get(«outer_axis.axis.name.literal»No)+" "+«inner_axis.axis.name.literal»TitlesArray.get(«inner_axis.axis.name.literal»No),(value == null ? 0.0 : value));
'''
}
else {
body = '''
«body» «inner_axis.axis.name.literal»DataSeries.add(value == null ? 0.0 : value);
'''
}
body = '''
«body» «inner_axis.axis.name.literal»No ++;
}
}
'''
if (!(chart.charttype instanceof ChartPie) && !(chart.charttype instanceof ChartDonut)) {
if (chart.charttype instanceof ChartBubble) {
body = '''
«body» «inner_axis.axis.name.literal»DataSeries.add(«outer_axis.axis.name.literal»TitlesArray.get(«outer_axis.axis.name.literal»No));
'''
}
body = '''
«body» dataSeries.add(«inner_axis.axis.name.literal»DataSeries.toArray());
'''
}
body = '''
«body» }
«outer_axis.axis.name.literal»No ++;
}
'''
body = '''
«body»SeriesDefaults seriesDefaults = new SeriesDefaults()
.setRenderer(SeriesRenderers.«chartType»);
'''
if (chart.charttype instanceof ChartLine) {
body = '''
«body»seriesDefaults.setFillToZero(«(chart.charttype as ChartLine).fillToZero.booleanValue.toString»).setFill(«(chart.charttype as ChartLine).fillArea.toString»);
'''
if ((chart.charttype as ChartLine).trendLine) {
body = '''
«body»seriesDefaults.setTrendline(
new Trendline().setShow(true).setLineWidth(1));
'''
}
}
// options
body = '''
«body»Options options = new Options()
.setSeriesDefaults(seriesDefaults);
'''
if (chart.charttype instanceof ChartPie) {
body = '''
«body»seriesDefaults.setRendererOptions(new PieRenderer()
.setShowDataLabels(true).setFill(«(!(chart.charttype as ChartPie).empty).toString»));
'''
}
else if (chart.charttype instanceof ChartDonut) {
body = '''
«body»seriesDefaults.setRendererOptions(new DonutRenderer()
.setShowDataLabels(true)
.setStartAngle(-90)
.setSliceMargin(3));
'''
}
else if (chart.charttype instanceof ChartBubble) {
body = '''
«body»seriesDefaults.setRendererOptions(new BubbleRenderer()
.setVaryBubbleColors(true)
«IF (chart.charttype as ChartBubble).multiplier».setAutoscaleMultiplier(«(chart.charttype as ChartBubble).multiplierValue»f)«ENDIF»
«IF (chart.charttype as ChartBubble).transparent».setHighlightAlpha(0.8f).setBubbleAlpha(0.6f)«ENDIF»
«IF (chart.charttype as ChartBubble).gradient».setBubbleGradients(true)«ENDIF»
.setShowLabels(false)
.setAutoscaleBubbles(true));
'''
}
else if (chart.charttype instanceof ChartGauge) {
var intervalValues = ""
var intervalColors = ""
if ((chart.charttype as ChartGauge).hasIntervals) {
for (interval : (chart.charttype as ChartGauge).intervals) {
if (interval instanceof ChartNumberInterval) {
intervalValues = '''«intervalValues»«IF intervalValues.length>0»,«ENDIF»«(interval as ChartNumberInterval).numberIntervalValue»f'''
if ((interval as ChartNumberInterval).numberRange instanceof ChartSegmentColor) {
intervalColors = '''«intervalColors»«IF intervalColors.length>0»,«ENDIF»"«((interval as ChartNumberInterval).numberRange as ChartSegmentColor).rgb.toHex»"'''
}
}
}
}
body = '''
«body»seriesDefaults.setRendererOptions(new MeterGaugeRenderer()
.setShowTickLabels(«(chart.charttype as ChartGauge).hasTicks.toString»)
«IF (chart.charttype as ChartGauge).labeled».setLabel("«(chart.charttype as ChartGauge).labelValue»")«ENDIF»
«IF (chart.charttype as ChartGauge).hasIntervals».setIntervals(«intervalValues»)«ENDIF»
«IF (chart.charttype as ChartGauge).hasIntervals».setIntervalColors(«intervalColors»)«ENDIF»);
'''
}
else if (chart.charttype instanceof ChartBar) {
body = '''
«body»seriesDefaults.setFillToZero(true);
seriesDefaults.setRendererOptions(new BarRenderer()
«IF axisSwitch».setBarDirection(BarDirections.HOTIZONTAL));«ELSE».setBarDirection(BarDirections.VERTICAL));«ENDIF»
'''
if ((chart.charttype as ChartBar).stacked) {
body = '''
«body»options.setStackSeries(true);
'''
}
if ((chart.charttype as ChartBar).shadow) {
body = '''
«body»seriesDefaults.setShadow(true).setShadowAlpha(0.05f);
'''
}
if ((chart.charttype as ChartBar).animated) {
body = '''
«body»options.setAnimate(true);
'''
}
}
body = '''
«body»
String[] seriesColors = {"#98E958","#3090F0","#EC6464","#F9DD51","#24DCD4","#EC64A5","#685CB0","#FF7D42","#AA514D","#7FB053","#BBA85B","#247981"};
««« String[] seriesColors = {"#0000FF","#FF0000","#00FF00","#FFFF00","#FF00FF","#00FFFF","#FF4500","#008000","#00BFFF","#FF69B4","#FFD700","#000080"};
options.setSeriesColors(seriesColors);
'''
if (hasAxis) {
// axes
body = '''
«body»Axes axes = new Axes();
'''
if (chart.charttype instanceof ChartBubble) {
// x
body = '''
«body»CanvasAxisTickRenderer axisTickRenderer = new CanvasAxisTickRenderer();
axisTickRenderer.setTextColor("#202020");
if («data_axis.axis.name.literal»TitlesArray.size() > 0) {
axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(0)»).setLabel(«data_axis.axis.name.literal»TitlesArray.get(0)).setTickOptions(axisTickRenderer«IF category_axis.angle!=0».setAngle(«data_axis.angle»)«ENDIF»));
axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(1)»).setLabel(«data_axis.axis.name.literal»TitlesArray.get(1)).setTickOptions(axisTickRenderer«IF category_axis.angle!=0».setAngle(«data_axis.angle»)«ENDIF»));
axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(1)»2).setLabel(«data_axis.axis.name.literal»TitlesArray.get(2)).setTickOptions(axisTickRenderer«IF category_axis.angle!=0».setAngle(«data_axis.angle»)«ENDIF»));
}
'''
}
else {
// x
body = '''
«body»CanvasAxisTickRenderer tickRenderer = new CanvasAxisTickRenderer();
tickRenderer.setTextColor("#202020");
axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(0)»)
.setRenderer(AxisRenderers.«category_axis.renderType.name().toString»)
.setLabel(«category_axis.axis.name.literal»AxisLabel)
.setTicks(«category_axis.axis.name.literal»Ticks)
.setTickOptions(tickRenderer«IF category_axis.angle!=0».setAngle(«category_axis.angle»)«ENDIF»));
'''
// y .. y9
body = '''
«body»CanvasAxisTickRenderer axisTickRenderer = new CanvasAxisTickRenderer();
axisTickRenderer.setTextColor("#202020");
axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(1)»)
.setPad(1.05f)
.setTickOptions(axisTickRenderer«IF data_axis.angle!=0».setAngle(«data_axis.angle»)«ENDIF»));
'''
// additional axes
for (axisKey : multipleAxes.keySet) {
if (!axisKey.equals("1")) {
body = '''«body»«multipleAxes.get(axisKey)»'''
}
}
if (multipleAxes.keySet.size > 0) {
body = '''
«body»LinearAxisRenderer linearAxisRenderer = new LinearAxisRenderer();
linearAxisRenderer.setAlignTicks(true);
AxesDefaults axesDefaults = new AxesDefaults()
.setUseSeriesColor(true)
.setBorderWidth(3)
.setRendererOptions(linearAxisRenderer);
options.setAxesDefaults(axesDefaults);
'''
}
}
body = '''
«body»options.setAxes(axes);
'''
}
// legend
for (element : chart.source.elements) {
if (element instanceof ChartLegend) {
var legend = element as ChartLegend
body = '''
«body»Legend legend = new Legend().setShow(«(legend != null).booleanValue.toString»);
'''
body = '''
«body»legend.setPlacement(LegendPlacements.«legend.placement.name()»);
'''
if (legend.toggle) {
body = '''
«body»EnhancedLegendRenderer renderer = new EnhancedLegendRenderer();
renderer.setSeriesToggle(SeriesToggles.«legend.toggleType.getName»);
renderer.setSeriesToggleReplot(«legend.replot.booleanValue.toString»);
legend.setRendererOptions(renderer);
'''
}
body = '''
«body»options.setLegend(legend);
'''
}
}
// highlighter
for (element : chart.source.elements) {
if (element instanceof ChartHighlighter) {
var highlighter = element as ChartHighlighter
body = '''
«body»Highlighter highlighter = new Highlighter().setShow(«(highlighter != null).booleanValue.toString»);
'''
body = '''
«body»highlighter.setShowTooltip(true)
.setTooltipAlwaysVisible(«highlighter.tooltipAlways.booleanValue.toString»)
.setKeepTooltipInsideChart(«highlighter.insideChart.booleanValue.toString»)
.setTooltipLocation(TooltipLocations.«highlighter.location.name()»)
.setBringSeriesToFront(true)
.setFadeTooltip(true)
.setShowMarker(true);
'''
body = '''
«body»highlighter.setTooltipAxes(TooltipAxes.«axisPrefix»«postfix»);
'''
body = '''
«body»options.setHighlighter(highlighter);
'''
}
}
// cursor
if (chart.charttype instanceof ChartLine) {
if ((chart.charttype as ChartLine).cursor) {
body = '''
«body»Cursor cursor = new Cursor()
.setShow(true)
.setZoom(«(chart.charttype as ChartLine).zoom.toString»)
.setShowTooltip(«(chart.charttype as ChartLine).tooltip.toString»);
options.setCursor(cursor);
'''
}
if ((chart.charttype as ChartLine).animated) {
body = '''
«body»options.setAnimate(true);
'''
}
}
if (hasAxis) {
// series
body = '''
«body»options.setSeries(«data_axis.axis.name.literal»Series);
'''
}
body = '''
«body»DCharts chart = new DCharts();
chart.setDataSeries(dataSeries);
chart.setOptions(options);
chart.setEnableDownload(true);
chart.setDownloadButtonCaption(dslMetadataService.translate(user.getLocale().toLanguageTag(), "download"));
chart.setDownloadFilename("«chart.name»");
chart.setDownloadButtonLocation(DownloadButtonLocation.TOP_RIGHT);
'''
body = '''
«body»chart.setId("chart");
chart.setImmediate(true);
if(chartRefresherDone) {
chart.show();
}
charts.add(chart);
return chart;'''
return body
}
/**
* <p>helper method to convert a rgb string to a hex string.</p>
*
*/
def String toHex(String rgb) {
var colorHex = "#"
for (color : rgb.split(",")) {
var i = new Integer(color);
colorHex = '''«colorHex»«String::format("%02x", i)»'''
}
return colorHex
}
/**
* <p>build the label series. Create multiple y-axes when the underlying datamart presents different scales.</p>
*
* <p>scales are used to display line charts together for comparison reason but they are of different scalings.</p>
*/
def String createLabelSeries(Chart chart, ChartAxis data_axis, HashMap<String,String>multipleAxes, String axisPrefix, boolean showMarker) {
var body = ""
var msrCnt = 0
// find multiple axes defined in datamart
if ((chart.source as ChartDatamart).datamartRef.source instanceof DatamartCube) {
for (axisOrSlicer : ((chart.source as ChartDatamart).datamartRef.source as DatamartCube).axisslicer) {
if (axisOrSlicer instanceof DatamartCubeAxis) {
for (element : (axisOrSlicer as DatamartCubeAxis).elements) {
if (element instanceof DatamartDerivedMeasure) {
if ((element as DatamartDerivedMeasure).scaled) {
body = '''«body»«(element as DatamartDerivedMeasure).scale.createScale(data_axis, multipleAxes, msrCnt, axisPrefix, showMarker)»'''
msrCnt = msrCnt + 1
}
}
if (element instanceof DatamartMeasure) {
if ((element as DatamartMeasure).scaled) {
body = '''«body»«(element as DatamartMeasure).scale.createScale(data_axis, multipleAxes, msrCnt, axisPrefix, showMarker)»'''
msrCnt = msrCnt + 1
}
}
}
}
}
}
else if ((chart.source as ChartDatamart).datamartRef.source instanceof DatamartEntity) {
var entity = ((chart.source as ChartDatamart).datamartRef.source as DatamartEntity)
body = '''«body»«entity.entityScale(data_axis, multipleAxes, msrCnt, axisPrefix, showMarker)»'''
}
// no multiple axes defined in datamart
if (multipleAxes.keySet.size == 0) {
body = '''
«body» for (String title : «data_axis.axis.name.literal»TitlesArray) {
«data_axis.axis.name.literal»Series.addSeries(new XYseries().setLabel(title).setShowMarker(«showMarker.toString»));
}
'''
}
return body
}
/**
* <p>helper method for scales coming from datamart entities not from cubes.</p>
*
*/
def String entityScale(DatamartEntity entity, ChartAxis data_axis, HashMap<String,String> multipleAxes, int msrCnt, String axisPrefix, boolean showMarker) {
var counter = msrCnt
var body = ""
for (attribute : entity.attributes) {
if (attribute instanceof DatamartAttribute) {
if (attribute.scaled) {
body = '''«body»«attribute.scale.createScale(data_axis, multipleAxes, counter, axisPrefix, showMarker)»'''
counter = counter + 1
}
}
}
for (navigation : entity.navigations) {
if (navigation instanceof DatamartMember) {
body = '''«body»«(navigation as DatamartMember).datamartEntity.entityScale(data_axis, multipleAxes, counter, axisPrefix, showMarker)»'''
}
if (navigation instanceof DatamartOwner) {
body = '''«body»«(navigation as DatamartOwner).datamartEntity.entityScale(data_axis, multipleAxes, counter, axisPrefix, showMarker)»'''
}
}
return body
}
/**
* <p>helper method for scales.</p>
*
*/
def String createScale(ValueScaleEnum scale, ChartAxis data_axis, HashMap<String,String> multipleAxes, int msrCnt, String axisPrefix, boolean showMarker) {
var multipleBody = ""
var body = ""
var scaleOrdinal = scale.ordinal
var scaleName = scale.toString
body = '''
«body»
«data_axis.axis.name.literal»Series.addSeries(new XYseries(«axisPrefix.charAt(1)»axes.«axisPrefix.charAt(1)»«IF scaleOrdinal != 0»«(scaleOrdinal+1)»«ENDIF»).setLabel(«data_axis.axis.name.literal»TitlesArray.get(«msrCnt»)).setShowMarker(«showMarker.toString»));
'''
if (!multipleAxes.keySet.contains(scaleName)) {
// axis 1 is never expressed, but 2..9
multipleBody =
'''axes.addAxis(new XYaxis(XYaxes.«axisPrefix.charAt(1)»«IF scaleOrdinal != 0»«(scaleOrdinal+1)»«ENDIF»).setTickOptions(axisTickRenderer«IF data_axis.angle!=0».setAngle(«data_axis.angle»)«ENDIF»));
'''
multipleAxes.put(scaleName, multipleBody)
}
return body
}
/**
* <p>helper method to get a attribute name or its alias if present of a entity's attribute object.</p>
*
*/
def String getAttributeName(DatamartAttribute attribute) {
return (attribute.attributeRef as LAttribute).name.toUpperCase
}
/**
* <p>helper method to get a level name of a hierarchy's level object.</p>
*
*/
def String getLevelName(CubeLevel level) {
return level.name
}
def String filterChanged(Chart chart) {
var body = ""
body = '''
«body»
if(changedFilter != null) {
EventDispatcherEvent evnt = new EventDispatcherEvent(EventDispatcherCommand.SELECT, changedFilter.getName(), "«chart.name»");
evnt.addData(changedFilter.getSelectedData());
eventDispatcher.sendEvent(evnt);
}
renderData();
'''
return body
}
def Set<LEntity> findRequestedEntities(Chart chart) {
var entities = new HashSet<LEntity>()
entities.addAll((chart.source as ChartDatamart).datamartRef.findAllEntities())
return entities
}
def String receiveEvent(Chart chart) {
var body = ""
body = '''
«body»
switch(event.getCommand()) {
case SELECT:
if(!event.getSender().equals("«chart.fullyQualifiedName»")) {
if(filterGenerator.selectItem(event, «chart.selectById.booleanValue»)) {
renderData();
}
}
break;
case SAVE:
case DELETE:
if(!event.getSender().equals("«chart.fullyQualifiedName»")) {
«FOR e:findRequestedEntities(chart)»
if(event.getTopic().equals("«e.fullyQualifiedName»")){
datamartInstance.clearCache();
renderData();
}
«ENDFOR»
}
break;
}
'''
return body
}
def localeChanged(Chart chart)
'''
if(charts != null) {
for(DCharts chart:charts) {
chart.setLocale(locale);
«IF chart.source.datamartRef.description &&
chart.source.datamartRef.descriptionValue != null»
chart.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "«chart.source.datamartRef.descriptionValue»"));
chart.setDownloadButtonCaption(dslMetadataService.translate(locale.toLanguageTag(), "download"));
«ENDIF»
}
}
if(layoutManager != null) {
layoutManager.setLabelValue(dslMetadataService.translate(locale.toLanguageTag(), "«chart.name»"));
}
'''
}