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