blob: cb02f19150cafea01e83d070f3e22ab07cba985c [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.topologydsl.jvmmodel
import com.ejt.vaadin.sizereporter.ComponentResizeListener
import com.ejt.vaadin.sizereporter.SizeReporter
import com.vaadin.annotations.JavaScript
import com.vaadin.shared.ui.JavaScriptComponentState
import com.vaadin.ui.AbstractJavaScriptComponent
import com.vaadin.ui.Component
import com.vaadin.ui.VerticalLayout
import java.util.Locale
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
import javax.inject.Inject
import org.eclipse.e4.core.contexts.IEclipseContext
import org.eclipse.e4.ui.di.Focus
import org.eclipse.e4.ui.model.application.MApplication
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective
import org.eclipse.emf.common.notify.Adapter
import org.eclipse.osbp.osgi.hybrid.api.AbstractHybridVaaclipseView
import org.eclipse.osbp.runtime.common.event.IEventDispatcher
import org.eclipse.osbp.ui.api.e4.IE4Focusable
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.annotation.CommonUtils
import org.eclipse.osbp.utils.vaadin.ViewLayoutManager
import org.eclipse.osbp.xtext.topologydsl.Topology
import org.eclipse.osbp.xtext.topologydsl.TopologyPackage
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.slf4j.Logger
/**
* <p>
* This inferrer infers models of extension .topology and generates code to be used by a e4 application as view. Underlying components
* are from the vaadin component repository and especially JavaScript components based on D3.
* </p>
*
* @author Jose Dominguez
*/
/**
* <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 TopologyDSLJvmModelInferrer extends AbstractModelInferrer {
/**
* convenience API to build and initialize JVM types and their members.
*/
@Inject extension JvmTypesBuilder
@Inject extension D3JsJavaUtil
@Inject extension CommonUtils
def dispatch void infer(TopologyPackage pckg, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
for (Topology topology : pckg.topologies) {
var cls = topology.toClass(pckg.name.concat(".").concat(topology.name).concat("Topology"))
cls.superTypes.add(_typeReferenceBuilder.typeRef(AbstractHybridVaaclipseView))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IUser.UserLocaleListener))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IE4Focusable))
acceptor.accept(cls, [
it.fileHeader = pckg.documentation
it.toFields(topology)
it.toConstructor(topology)
it.toOperations(topology)
])
val stateClassName = topology.createStateFilename
var topology_cls = topology.toClass(topology.createfullyQualifiedTopologyFilename)
// JavaScript-Java-State-Component creation
var state_cls = topology.toClass(topology.createfullyQualifiedStateFilename)
// JavaScript-Java-Component creation
acceptor.accept(topology_cls, [
superTypes += _typeReferenceBuilder.typeRef(AbstractJavaScriptComponent)
var jsAnnotationRef = _annotationTypesBuilder.annotationRef(JavaScript)
var jsAnnotationList = newArrayList('''theme://plugin/org.eclipse.osbp.utils.js/org/eclipse/osbp/utils/js/d3.min.js''', '''theme://plugin/org.eclipse.osbp.utils.js/org/eclipse/osbp/utils/js/topojson.v1.min.js''', '''js/«topology.createTopologyJsFilename»''')
jsAnnotationRef.addStringValuesToAnnotation(jsAnnotationList)
annotations += jsAnnotationRef
it.toTopologyConstructor(topology)
it.toTopologyFields(topology)
it.toTopologyOperations(topology, stateClassName)
])
// JavaScript-Java-State-Component creation
acceptor.accept(state_cls, [
superTypes += _typeReferenceBuilder.typeRef(JavaScriptComponentState)
it.toTopologyStateFields(topology)
])
}
}
/**
* <p>build the constructors to be used by an e4 application.</p>
*
*/
def void toConstructor(JvmDeclaredType type, Topology topology) {
type.members += topology.toConstructor(
[
annotations += _annotationTypesBuilder.annotationRef(Inject)
parameters += topology.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
parameters += topology.toParameter("context", _typeReferenceBuilder.typeRef(IEclipseContext))
parameters += topology.toParameter("app", _typeReferenceBuilder.typeRef(MApplication))
body = [append(
'''
super(parent,context,app);
perspective = context.get(MPerspective.class);''')]
])
}
/**
* <p>build the class variables.</p>
*
*/
def void toFields(JvmDeclaredType type, Topology topology) {
var JvmField field = null
// create logger
field = topology.toField("log", _typeReferenceBuilder.typeRef(Logger)) [
setInitializer([append('''org.slf4j.LoggerFactory.getLogger("topology")''')])
]
field.static = true
field.visibility = JvmVisibility::PRIVATE
type.members += field
field = topology.toField("layoutManager", _typeReferenceBuilder.typeRef(ViewLayoutManager))
type.members += field
field = topology.toField("dataComponent", _typeReferenceBuilder.typeRef(Component))
type.members += field
field = topology.toField("panelWidth", _typeReferenceBuilder.typeRef(int))
type.members += field
field = topology.toField("panelHeight", _typeReferenceBuilder.typeRef(int))
type.members += field
field = topology.toField("dslMetadataService", _typeReferenceBuilder.typeRef(IDSLMetadataService)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
]
type.members += field
field = topology.toField("locale", _typeReferenceBuilder.typeRef(Locale))
type.members += field
field = topology.toField("user", _typeReferenceBuilder.typeRef(IUser)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
]
type.members += field
field = topology.toField("eventDispatcher", _typeReferenceBuilder.typeRef(IEventDispatcher)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
]
type.members += field
field = topology.toField("splitAdapter", _typeReferenceBuilder.typeRef(Adapter.Internal))
type.members += field
field = topology.toField("resizeListener", _typeReferenceBuilder.typeRef(ComponentResizeListener))
type.members += field
field = topology.toField("sizeReporter", _typeReferenceBuilder.typeRef(SizeReporter));
type.members += field
field = topology.toField("perspective", _typeReferenceBuilder.typeRef(MPerspective));
type.members += field
}
/**
* <p>build the methods.</p>
*
*/
def void toOperations(JvmDeclaredType type, Topology topology) {
// activate
type.members += topology.toMethod("activate", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(PostConstruct)
body = [
append(
'''
user.addUserLocaleListener(this);
locale = user.getLocale();
// to resize the chart according to split positions of partsashcontainer - setContainerData is tracked
((EObject)getPart()).eAdapters().add(splitAdapter);
sizeReporter.addResizeListener(resizeListener);
''')
]
])
// deactivate
type.members += topology.toMethod("deactivate", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(PreDestroy)
body = [
append(
'''
user.removeUserLocaleListener(this);
((EObject)getPart()).eAdapters().remove(splitAdapter);
sizeReporter.removeResizeListener(resizeListener);
''')
]
])
// create view
type.members += topology.toMethod("createView", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += topology.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
body = [append('''«topology.createView»''')]
])
// create components
type.members += topology.toMethod("createComponents", _typeReferenceBuilder.typeRef(Void::TYPE), [
body = [append('''«topology.createComponents»''')]
])
// create D3 Topology
type.members += topology.toMethod("createD3Topology", _typeReferenceBuilder.typeRef(Component), [
body = [append('''«topology.createD3Topology»''')]
])
// focus
type.members += topology.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 += topology.toMethod("localeChanged", _typeReferenceBuilder.typeRef(Void::TYPE), [
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += topology.toParameter("locale", _typeReferenceBuilder.typeRef(Locale))
body = [append('''«topology.localeChanged»''')]
])
}
/**
* <p>build the main method to be called from e4.</p>
*
*/
def String createView(Topology topology) {
var body = '''
getContext().set(IE4Focusable.class, this);
layoutManager = new ViewLayoutManager();
layoutManager.init(parent);
splitAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification notification) {
if (notification.getEventType()==Notification.SET &&
notification.getFeatureID(UIElementImpl.class) == UiPackageImpl.UI_ELEMENT__CONTAINER_DATA)
{
log.debug("render due to split position changed");
renderData();
}
}
};
sizeReporter = new SizeReporter(layoutManager.getDataArea());
resizeListener = new ComponentResizeListener() {
@Override
public void sizeChanged(ComponentResizeEvent event) {
boolean changed = false;
if (panelWidth != event.getWidth()) {
changed = true;
panelWidth = event.getWidth();
}
if (panelHeight != event.getHeight()) {
changed = true;
panelHeight = event.getHeight();
}
if (changed) {
log.debug("Panel size changed : " + event.getWidth() + " x " + event.getHeight());
renderData();
}
}
};
'''
return body
}
/**
* <p>build the data components.</p>
*
*/
def String createComponents(Topology topology) {
var body = '''
if (panelWidth == 0 && panelHeight == 0) {
return;
}
log.debug("topology creates components");
try {
// remove any previous component
if (dataComponent != null) {
layoutManager.getDataArea().removeComponent(dataComponent);
dataComponent = null;
}
// generate a new result component
dataComponent = createD3Topology();
dataComponent.setSizeFull();
dataComponent.addStyleName("«EnumCssClass.DATA_COMPONENT.styleName»");
layoutManager.getDataArea().addComponent(dataComponent);
layoutManager.getDataArea().setExpandRatio(dataComponent, 1);
} catch (Exception e) {
log.error(e.getLocalizedMessage());
}
log.debug("topology creating components finished");
'''
return body
}
def localeChanged(Topology topology) '''
if(layoutManager != null) {
layoutManager.setLabelValue(dslMetadataService.translate(locale.toLanguageTag(), "«topology.titleValue»"));
}
'''
/**
* <p>build the class variables.</p>
*
*/
def void toTopologyFields(JvmDeclaredType type, Topology topology) {
var JvmField field = null
// create logger
field = topology.toField("log", _typeReferenceBuilder.typeRef(Logger)) [
setInitializer([append('''org.slf4j.LoggerFactory.getLogger("topology")''')])
]
field.static = true
field.visibility = JvmVisibility::PRIVATE
type.members += field
}
def void toTopologyOperations(JvmDeclaredType type, Topology topology, String stateClassName) {
// create view
type.members += topology.toMethod("getState", _typeReferenceBuilder.typeRef(stateClassName), [
body = [append('''return («stateClassName») super.getState();''')]
])
// create focus
type.members += topology.toMethod("focus", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [append('''super.focus();''')]
])
}
def void toTopologyConstructor(JvmDeclaredType type, Topology topology) {
type.members += topology.toConstructor(
[
parameters += topology.toParameter("_htmlTagId", _typeReferenceBuilder.typeRef(String))
parameters += topology.toParameter("eventDispatcher", _typeReferenceBuilder.typeRef(IEventDispatcher))
parameters += topology.toParameter("perspective", _typeReferenceBuilder.typeRef(MPerspective))
parameters += topology.toParameter("_jsonData", _typeReferenceBuilder.typeRef(String))
parameters += topology.toParameter("width", _typeReferenceBuilder.typeRef(int))
parameters += topology.toParameter("height", _typeReferenceBuilder.typeRef(int))
body = [
append(
'''
getState().jsonData = _jsonData;
getState().htmlTagId=_htmlTagId;
getState().panelWidth = new Integer(width).toString();
getState().panelHeight = new Integer(height).toString();
addFunction("on«topology.fileName»Click", new JavaScriptFunction() {
@Override
public void call(JsonArray arguments){
try {
EventDispatcherEvent evnt = new EventDispatcherEvent(perspective, EventDispatcherCommand.SELECT, "Topology", "«topology.name»");
if(arguments.get(0) instanceof JreJsonString && arguments.get(1) instanceof JreJsonString) {
evnt.addItem(EventDispatcherDataTag.LIST, arguments.getString(0)+"."+arguments.getString(1));
eventDispatcher.sendEvent(evnt);
}
} catch (JsonException jsonExp){
log.error("«topology.fileName»Click - JsonException: no property available");
}
}
});'''
)
]
])
}
def void toTopologyStateFields(JvmDeclaredType type, Topology topology) {
type.members += topology.toField("jsonData", _typeReferenceBuilder.typeRef(String)) [
visibility = JvmVisibility::PUBLIC
setInitializer([append('''""''')])
]
type.members += topology.toField("htmlTagId", _typeReferenceBuilder.typeRef(String)) [
visibility = JvmVisibility::PUBLIC
setInitializer([append('''""''')])
]
type.members += topology.toField("panelWidth", _typeReferenceBuilder.typeRef(String)) [
visibility = JvmVisibility::PUBLIC
setInitializer([append('''""''')])
]
type.members += topology.toField("panelHeight", _typeReferenceBuilder.typeRef(String)) [
visibility = JvmVisibility::PUBLIC
setInitializer([append('''""''')])
]
type.members += topology.toField("inputValue", _typeReferenceBuilder.typeRef(String)) [
visibility = JvmVisibility::PUBLIC
setInitializer([append('''""''')])
]
}
def createD3Topology(Topology topology) '''
InputStream json = getClass().getResourceAsStream("/topologies/«topology.fileName».json");
try {
return new «topology.createfullyQualifiedTopologyFilename»("«topology.createTopologyFilename»", eventDispatcher, perspective, IOUtils.toString(json), panelWidth, panelHeight);
} catch (IOException e) {
log.error("found no data for «topology.fileName» in folder topologies");
}
return null;
'''
}