blob: 1e893ccdb114678f1baa1871785931880d204f0a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 RCP Vision (http://www.rcp-vision.com) and others.
* 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
*******************************************************************************/
package org.eclipse.emf.parsley.dsl.jvmmodel
import com.google.inject.Inject
import java.util.List
import org.eclipse.core.databinding.DataBindingContext
import org.eclipse.core.databinding.observable.value.IObservableValue
import org.eclipse.emf.common.notify.AdapterFactory
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider
import org.eclipse.emf.parsley.EmfParsleyGuiceModule
import org.eclipse.emf.parsley.binding.DialogControlFactory
import org.eclipse.emf.parsley.binding.FormControlFactory
import org.eclipse.emf.parsley.binding.ProposalCreator
import org.eclipse.emf.parsley.dsl.model.ControlFactorySpecification
import org.eclipse.emf.parsley.dsl.model.FeatureCaptionSpecification
import org.eclipse.emf.parsley.dsl.model.Module
import org.eclipse.emf.parsley.dsl.model.PartSpecification
import org.eclipse.emf.parsley.edit.ui.provider.ViewerContentProvider
import org.eclipse.emf.parsley.generator.common.EmfParsleyProjectFilesGenerator
import org.eclipse.emf.parsley.ui.provider.DialogFeatureCaptionProvider
import org.eclipse.emf.parsley.ui.provider.EClassToEStructuralFeatureAsStringsMap
import org.eclipse.emf.parsley.ui.provider.FeatureCaptionProvider
import org.eclipse.emf.parsley.ui.provider.FeaturesProvider
import org.eclipse.emf.parsley.ui.provider.FormFeatureCaptionProvider
import org.eclipse.emf.parsley.ui.provider.ViewerLabelProvider
import org.eclipse.jface.viewers.IContentProvider
import org.eclipse.jface.viewers.ILabelProvider
import org.eclipse.swt.widgets.Composite
import org.eclipse.swt.widgets.Control
import org.eclipse.swt.widgets.Label
import org.eclipse.ui.plugin.AbstractUIPlugin
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.common.types.JvmOperation
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.common.types.TypesFactory
import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import static extension org.eclipse.xtext.EcoreUtil2.*
/**
* <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 EmfParsleyDslJvmModelInferrer extends AbstractModelInferrer {
/**
* convenience API to build and initialize JVM types and their members.
*/
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
@Inject extension TypeReferences
@Inject extension TypesFactory
@Inject extension GeneratorUtils
@Inject EmfParsleyProjectFilesGenerator projectFilesGenerator
/**
* The dispatch method {@code infer} is called for each instance of the
* given element's type that is contained in a resource.
*
* @param element
* the model to create one or more
* {@link org.eclipse.xtext.common.types.JvmDeclaredType declared
* types} from.
* @param acceptor
* each created
* {@link org.eclipse.xtext.common.types.JvmDeclaredType type}
* without a container should be passed to the acceptor in order
* get attached to the current resource. The acceptor's
* {@link IJvmDeclaredTypeAcceptor#accept(org.eclipse.xtext.common.types.JvmDeclaredType)
* accept(..)} method takes the constructed empty type for the
* pre-indexing phase. This one is further initialized in the
* indexing phase using the closure you pass to the returned
* {@link org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor.IPostIndexingInitializing#initializeLater(org.eclipse.xtext.xbase.lib.Procedures.Procedure1)
* initializeLater(..)}.
* @param isPreIndexingPhase
* whether the method is called in a pre-indexing phase, i.e.
* when the global index is not yet fully updated. You must not
* rely on linking using the index if isPreIndexingPhase is
* <code>true</code>.
*/
def dispatch void infer(Module element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
if (element.name == null)
return
val moduleClass = element.toClass(element.moduleQN)
val labelProviderClass = element.inferLabelProvider(acceptor)
val featureCaptionProviderClass = element.inferFeatureCaptionProvider(acceptor)
val formFeatureCaptionProviderClass = element.inferFormFeatureCaptionProvider(acceptor)
val dialogFeatureCaptionProviderClass = element.inferDialogFeatureCaptionProvider(acceptor)
val featureProviderClass = element.inferFeatureProvider(acceptor)
val formControlFactoryClass = element.inferFormControlFactory(acceptor)
val dialogControlFactoryClass = element.inferDialogControlFactory(acceptor)
val viewerContentProviderClass = element.inferViewerContentProvider(acceptor)
val proposalCreatorClass = element.inferProposalCreator(acceptor)
acceptor.accept(moduleClass).initializeLater [
documentation = element.documentation
moduleClass.setSuperClassType(element, typeof(EmfParsleyGuiceModule))
members += element.toConstructor() [
parameters += element.toParameter("plugin", element.newTypeRef(typeof(AbstractUIPlugin)))
body = [it.append("super(plugin);")]
]
if (labelProviderClass != null)
members += element.labelProvider.genBindMethod(labelProviderClass, typeof(ILabelProvider))
if (featureCaptionProviderClass != null)
members += element.featureCaptionProvider.genBindMethod(featureCaptionProviderClass, typeof(FeatureCaptionProvider))
if (formFeatureCaptionProviderClass != null)
members += element.formFeatureCaptionProvider.genBindMethod(formFeatureCaptionProviderClass, typeof(FormFeatureCaptionProvider))
if (dialogFeatureCaptionProviderClass != null)
members += element.dialogFeatureCaptionProvider.genBindMethod(dialogFeatureCaptionProviderClass, typeof(DialogFeatureCaptionProvider))
if (featureProviderClass != null)
members += element.featuresProvider.genBindMethod(featureProviderClass, typeof(FeaturesProvider))
if (formControlFactoryClass != null)
members += element.formControlFactory.genBindMethod(formControlFactoryClass, typeof(FormControlFactory))
if (dialogControlFactoryClass != null)
members += element.dialogControlFactory.genBindMethod(dialogControlFactoryClass, typeof(DialogControlFactory))
if (viewerContentProviderClass != null)
members += element.viewerContentProvider.genBindMethod(viewerContentProviderClass, typeof(IContentProvider))
if (proposalCreatorClass != null)
members += element.proposalCreator.genBindMethod(proposalCreatorClass, typeof(ProposalCreator))
]
}
def setSuperClassType(JvmGenericType e, Module dslElement, Class<?> defaultSuperClass) {
if (dslElement.extendsClause != null)
e.superTypes += dslElement.extendsClause.superType.cloneWithProxies
else
e.superTypes += e.newTypeRef(defaultSuperClass)
}
def activatorQN(Module element) {
element.fullyQualifiedName + ".Activator"
}
def moduleQN(Module element) {
element.fullyQualifiedName + ".EmfParsleyGuiceModuleGen"
}
def executableExtensionFactoryQN(Module element) {
element.fullyQualifiedName.toString + "." +
projectFilesGenerator.extFactoryName(element.fullyQualifiedName.toString)
}
def executableExtensionFactoryQN(PartSpecification element) {
element.getContainerOfType(typeof(Module)).executableExtensionFactoryQN
}
def labelProviderQN(Module element) {
element.fullyQualifiedName + ".ui.provider.LabelProviderGen"
}
def featureCaptionProviderQN(Module element) {
element.fullyQualifiedName + ".ui.provider.FeatureCaptionProviderGen"
}
def formFeatureCaptionProviderQN(Module element) {
element.fullyQualifiedName + ".ui.provider.FormFeatureCaptionProviderGen"
}
def dialogFeatureCaptionProviderQN(Module element) {
element.fullyQualifiedName + ".ui.provider.DialogFeatureCaptionProviderGen"
}
def featuresProviderQN(Module element) {
element.fullyQualifiedName + ".ui.provider.FeaturesProviderGen"
}
def formControlFactoryQN(Module element) {
element.fullyQualifiedName + ".binding.FormControlFactoryGen"
}
def dialogControlFactoryQN(Module element) {
element.fullyQualifiedName + ".binding.DialogControlFactoryGen"
}
def viewerContentProviderQN(Module element) {
element.fullyQualifiedName + ".edit.ui.provider.ViewerContentProviderGen"
}
def proposalCreatorQN(Module element) {
element.fullyQualifiedName + ".binding.ProposalCreatorGen"
}
def partsClassQN(Module element) {
element.fullyQualifiedName + ".ui.parts.DummyPartsClass"
}
def inferLabelProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.labelProvider == null)
null
else {
val labelProviderClass = element.labelProvider.toClass(element.labelProviderQN)
acceptor.accept(labelProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(ViewerLabelProvider))
members += element.labelProvider.toConstructor() [
parameters += element.labelProvider.
toParameter("delegate",
element.newTypeRef(typeof(AdapterFactoryLabelProvider))
)
body = [it.append("super(delegate);")]
annotations += element.toAnnotation(typeof(Inject))
]
element.labelProvider.labelSpecifications.forEach [
labelSpecification |
members += labelSpecification.toMethod("text", element.newTypeRef(typeof(String))) [
parameters += labelSpecification.toParameter(
if (labelSpecification.name != null)
labelSpecification.name
else
"it"
, labelSpecification.parameterType
)
body = labelSpecification.expression
]
]
element.labelProvider.imageSpecifications.forEach [
imageSpecification |
members += imageSpecification.toMethod("image", element.newTypeRef(typeof(Object))) [
parameters += imageSpecification.toParameter(
if (imageSpecification.name != null)
imageSpecification.name
else
"it"
, imageSpecification.parameterType
)
body = imageSpecification.expression
]
]
]
labelProviderClass
}
}
def inferFeatureCaptionProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.featureCaptionProvider == null)
null
else {
val propertyDescriptionProviderClass = element.featureCaptionProvider.toClass(element.featureCaptionProviderQN)
acceptor.accept(propertyDescriptionProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(FeatureCaptionProvider))
inferMethodsForTextPropertyDescription(element, it, element.featureCaptionProvider.specifications)
]
propertyDescriptionProviderClass
}
}
def inferMethodsForTextPropertyDescription(Module element, JvmGenericType it, Iterable<FeatureCaptionSpecification> specifications) {
for (spec : specifications) {
if (spec.feature?.simpleName != null) {
// associate the method to the expression, not to the whole
// spec, otherwise the 'feature' is logically
// contained in a method which should return a string
// and the validator would complain
members += spec.expression.toMethod
("text_" +
spec.parameterType.simpleName + "_" +
spec.feature.simpleName.propertyNameForGetterSetterMethod,
element.newTypeRef(typeof(String))
) [
parameters += spec.toParameter(
"it", element.newTypeRef(typeof(EStructuralFeature))
)
body = spec.expression
]
}
}
}
def inferFormFeatureCaptionProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.formFeatureCaptionProvider == null)
null
else {
val descriptionProviderClass = element.formFeatureCaptionProvider.toClass(element.formFeatureCaptionProviderQN)
acceptor.accept(descriptionProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(FormFeatureCaptionProvider))
inferMethodsForTextPropertyDescription(element, it, element.formFeatureCaptionProvider.specifications)
inferMethodsForLabelPropertyDescription(element, it, element.formFeatureCaptionProvider.labelSpecifications)
]
descriptionProviderClass
}
}
def inferMethodsForLabelPropertyDescription(Module element, JvmGenericType it, Iterable<FeatureCaptionSpecification> specifications) {
for (spec : specifications) {
if (spec.feature?.simpleName != null) {
// associate the method to the expression, not to the whole
// spec, otherwise the 'feature' is logically
// contained in a method which should return a string
// and the validator would complain
members += spec.expression.toMethod
("label_" +
spec.parameterType.simpleName + "_" +
spec.feature.simpleName.propertyNameForGetterSetterMethod,
element.newTypeRef(typeof(Label))
) [
parameters += spec.toParameter(
"parent", element.newTypeRef(typeof(Composite))
)
parameters += spec.toParameter(
"it", element.newTypeRef(typeof(EStructuralFeature))
)
body = spec.expression
]
}
}
}
def inferDialogFeatureCaptionProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.dialogFeatureCaptionProvider == null)
null
else {
val descriptionProviderClass = element.dialogFeatureCaptionProvider.toClass(element.dialogFeatureCaptionProviderQN)
acceptor.accept(descriptionProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(DialogFeatureCaptionProvider))
inferMethodsForTextPropertyDescription(element, it, element.dialogFeatureCaptionProvider.specifications)
inferMethodsForLabelPropertyDescription(element, it, element.dialogFeatureCaptionProvider.labelSpecifications)
]
descriptionProviderClass
}
}
def inferFeatureProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.featuresProvider == null)
null
else {
val featureProviderClass = element.featuresProvider.toClass(element.featuresProviderQN)
acceptor.accept(featureProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(FeaturesProvider))
documentation = element.featuresProvider.documentation
members += element.featuresProvider.
toMethod("buildStringMap", Void::TYPE.getTypeForName(element)) [
annotations += element.toAnnotation(typeof(Override))
parameters += element.featuresProvider.toParameter("stringMap",
element.newTypeRef(
typeof(EClassToEStructuralFeatureAsStringsMap)
)
)
body = [
append("super.buildStringMap(stringMap);").newLine
element.featuresProvider.featureSpecifications.forEach [
featureSpecification |
newLine.
append('''stringMap.mapTo("«featureSpecification.parameterType.identifier»",''').
increaseIndentation.newLine
val fs = featureSpecification.features.map [
feature |
'"' + feature.simpleName.propertyNameForGetterSetterMethod
+ '"'
]
append(fs.join(", "))
append(");").decreaseIndentation
]
]
]
]
featureProviderClass
}
}
def inferFormControlFactory(Module e, IJvmDeclaredTypeAcceptor acceptor) {
if (e.formControlFactory == null)
null
else {
val formFeatureControlFactoryClass = e.formControlFactory.toClass(e.formControlFactoryQN)
acceptor.accept(formFeatureControlFactoryClass).initializeLater [
superTypes += e.newTypeRef(typeof(FormControlFactory))
documentation = e.formControlFactory.documentation
inferMethodsForControlFactory(e, it, e.formControlFactory.controlSpecifications)
]
formFeatureControlFactoryClass
}
}
def inferDialogControlFactory(Module e, IJvmDeclaredTypeAcceptor acceptor) {
if (e.dialogControlFactory == null)
null
else {
val controlFactoryClass = e.dialogControlFactory.toClass(e.dialogControlFactoryQN)
acceptor.accept(controlFactoryClass).initializeLater [
superTypes += e.newTypeRef(typeof(DialogControlFactory))
documentation = e.formControlFactory.documentation
inferMethodsForControlFactory(e, it, e.dialogControlFactory.controlSpecifications)
]
controlFactoryClass
}
}
def inferMethodsForControlFactory(Module e, JvmGenericType it, Iterable<ControlFactorySpecification> specifications) {
for (spec: specifications) {
if (spec.feature?.simpleName != null) {
// associate the method to the expression, not to the whole
// labelSpecification, otherwise the 'feature' is logically
// contained in a method which should return a string
// and the validator would complain
if (spec.target == null)
members += spec.
control_EClass_EStructuralFeature(spec.expression) [
parameters += spec.toParameter(
"it", spec.parameterType
)
body = spec.expression
]
else {
val createControlMethodName = spec.methodNameForFormFeatureSpecification("createControl_")
val createTargetMethodName = spec.methodNameForFormFeatureSpecification("createTarget_")
members += spec.
control_EClass_EStructuralFeature(spec.expression) [
parameters += spec.toParameter(
"dataBindingContext", e.newTypeRef(typeof(DataBindingContext))
)
parameters += spec.toParameter(
"observableValue", e.newTypeRef(typeof(IObservableValue))
)
body = [
append(spec.newTypeRef(typeof(Control)).type)
append(''' control = «createControlMethodName»();''').newLine
append(
'''
dataBindingContext.bindValue(
«createTargetMethodName»(control),
observableValue);'''
).newLine
append('''return control;''')
]
]
members += spec.toMethod
(createControlMethodName,
spec.newTypeRef(typeof(Control))) [
visibility = JvmVisibility::PROTECTED
body = spec.expression
]
members += spec.toMethod
(createTargetMethodName,
spec.newTypeRef(typeof(IObservableValue))) [
visibility = JvmVisibility::PROTECTED
parameters += spec.toParameter(
"it", e.newTypeRef(typeof(Control))
)
body = spec.target
]
}
}
}
}
def inferViewerContentProvider(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.viewerContentProvider == null)
null
else {
val viewerContentProviderClass = element.viewerContentProvider.toClass(element.viewerContentProviderQN)
acceptor.accept(viewerContentProviderClass).initializeLater [
superTypes += element.newTypeRef(typeof(ViewerContentProvider))
members += element.viewerContentProvider.toConstructor() [
parameters += element.viewerContentProvider.
toParameter("adapterFactory",
element.newTypeRef(typeof(AdapterFactory))
)
body = [it.append("super(adapterFactory);")]
annotations += element.toAnnotation(typeof(Inject))
]
element.viewerContentProvider.elementsSpecifications.forEach [
specification |
members += specification.toMethod("elements", element.newTypeRef(typeof(Object))) [
parameters += specification.toParameter(
if (specification.name != null)
specification.name
else
"it"
, specification.parameterType
)
body = specification.expression
]
]
element.viewerContentProvider.childrenSpecifications.forEach [
specification |
members += specification.toMethod("children", element.newTypeRef(typeof(Object))) [
parameters += specification.toParameter(
if (specification.name != null)
specification.name
else
"it"
, specification.parameterType
)
body = specification.expression
]
]
]
viewerContentProviderClass
}
}
def inferProposalCreator(Module element, IJvmDeclaredTypeAcceptor acceptor) {
if (element.proposalCreator == null)
null
else {
val proposalCreatorClass = element.proposalCreator.toClass(element.proposalCreatorQN)
acceptor.accept(proposalCreatorClass).initializeLater [
superTypes += element.newTypeRef(typeof(ProposalCreator))
element.proposalCreator.proposalsSpecifications.forEach [
spec |
if (spec.feature?.simpleName != null) {
// associate the method to the expression, not to the whole
// specification, otherwise the 'feature' is logically
// contained in a method which should return a string
// and the validator would complain
members += spec.expression.toMethod
("proposals_" +
spec.parameterType.simpleName + "_" +
spec.feature.simpleName.propertyNameForGetterSetterMethod,
element.newTypeRef(typeof(List)).type.createTypeRef(wildCard)
) [
parameters += spec.toParameter(
"it", spec.parameterType
)
parameters += spec.toParameter(
"feature", spec.newTypeRef(typeof(EStructuralFeature))
)
body = spec.expression
]
}
]
]
proposalCreatorClass
}
}
def control_EClass_EStructuralFeature(
ControlFactorySpecification spec, XExpression exp, (JvmOperation)=>void init
) {
exp.toMethod
(spec.methodNameForFormFeatureSpecification("control_"),
spec.newTypeRef(typeof(Control))
, init)
}
def methodNameForFormFeatureSpecification(ControlFactorySpecification spec, String prefix) {
prefix +
spec.parameterType.simpleName + "_" +
spec.feature.simpleName.propertyNameForGetterSetterMethod
}
def genBindMethod(EObject element, JvmGenericType type, Class<?> clazz) {
val wildCard = createJvmWildcardTypeReference
val upperBound = createJvmUpperBound
upperBound.typeReference = element.newTypeRef(clazz)
wildCard.constraints += upperBound
element.toMethod("bind" + clazz.simpleName,
element.newTypeRef(typeof(Class), wildCard) ) [
annotations += element.toAnnotation(typeof(Override))
body = [
append("return ")
append(type)
append(".class;")
]
]
}
}