/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.sarl.lang.jvmmodel; import java.io.Serializable; import java.lang.annotation.Annotation; import java.text.MessageFormat; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Generated; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.inject.Inject; import com.google.inject.MembersInjector; import com.google.inject.Singleton; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtend.core.jvmmodel.SyntheticNameClashResolver; import org.eclipse.xtend.core.jvmmodel.XtendJvmModelInferrer; import org.eclipse.xtend.core.xtend.AnonymousClass; import org.eclipse.xtend.core.xtend.XtendAnnotationType; import org.eclipse.xtend.core.xtend.XtendClass; import org.eclipse.xtend.core.xtend.XtendConstructor; import org.eclipse.xtend.core.xtend.XtendEnum; import org.eclipse.xtend.core.xtend.XtendExecutable; import org.eclipse.xtend.core.xtend.XtendField; import org.eclipse.xtend.core.xtend.XtendFile; import org.eclipse.xtend.core.xtend.XtendFunction; import org.eclipse.xtend.core.xtend.XtendInterface; import org.eclipse.xtend.core.xtend.XtendMember; import org.eclipse.xtend.core.xtend.XtendPackage; import org.eclipse.xtend.core.xtend.XtendParameter; import org.eclipse.xtend.core.xtend.XtendTypeDeclaration; import org.eclipse.xtend2.lib.StringConcatenationClient; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.LanguageInfo; import org.eclipse.xtext.common.types.JvmAnnotationReference; import org.eclipse.xtext.common.types.JvmAnnotationTarget; import org.eclipse.xtext.common.types.JvmAnnotationType; import org.eclipse.xtext.common.types.JvmAnnotationValue; import org.eclipse.xtext.common.types.JvmConstructor; import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmEnumerationType; import org.eclipse.xtext.common.types.JvmExecutable; import org.eclipse.xtext.common.types.JvmFeature; import org.eclipse.xtext.common.types.JvmField; import org.eclipse.xtext.common.types.JvmFormalParameter; import org.eclipse.xtext.common.types.JvmGenericType; import org.eclipse.xtext.common.types.JvmIdentifiableElement; import org.eclipse.xtext.common.types.JvmIntAnnotationValue; import org.eclipse.xtext.common.types.JvmOperation; import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.common.types.JvmTypeAnnotationValue; import org.eclipse.xtext.common.types.JvmTypeConstraint; import org.eclipse.xtext.common.types.JvmTypeParameter; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.common.types.JvmUpperBound; import org.eclipse.xtext.common.types.JvmVisibility; import org.eclipse.xtext.common.types.JvmWildcardTypeReference; import org.eclipse.xtext.common.types.TypesFactory; import org.eclipse.xtext.common.types.util.AnnotationLookup; import org.eclipse.xtext.common.types.util.TypeReferences; import org.eclipse.xtext.serializer.ISerializer; import org.eclipse.xtext.serializer.sequencer.IContextFinder; import org.eclipse.xtext.xbase.XBlockExpression; import org.eclipse.xtext.xbase.XBooleanLiteral; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.compiler.GeneratorConfig; import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable; import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor; import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociator; import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder; import org.eclipse.xtext.xbase.lib.CollectionLiterals; import org.eclipse.xtext.xbase.lib.Extension; import org.eclipse.xtext.xbase.lib.Inline; import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.lib.Pair; import org.eclipse.xtext.xbase.lib.Procedures; import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import org.eclipse.xtext.xbase.lib.Procedures.Procedure2; import org.eclipse.xtext.xbase.lib.Pure; import org.eclipse.xtext.xbase.typesystem.InferredTypeIndicator; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices; import org.eclipse.xtext.xbase.validation.ReadAndWriteTracking; import io.sarl.lang.SARLVersion; import io.sarl.lang.actionprototype.ActionParameterTypes; import io.sarl.lang.actionprototype.ActionPrototype; import io.sarl.lang.actionprototype.IActionPrototypeProvider; import io.sarl.lang.actionprototype.InferredPrototype; import io.sarl.lang.actionprototype.InferredStandardParameter; import io.sarl.lang.actionprototype.InferredValuedParameter; import io.sarl.lang.actionprototype.QualifiedActionName; import io.sarl.lang.annotation.DefaultValue; import io.sarl.lang.annotation.DefaultValueSource; import io.sarl.lang.annotation.DefaultValueUse; import io.sarl.lang.annotation.EarlyExit; import io.sarl.lang.annotation.FiredEvent; import io.sarl.lang.annotation.ImportedCapacityFeature; import io.sarl.lang.annotation.PerceptGuardEvaluator; import io.sarl.lang.annotation.SarlElementType; import io.sarl.lang.annotation.SarlSourceCode; import io.sarl.lang.annotation.SarlSpecification; import io.sarl.lang.annotation.SyntheticMember; import io.sarl.lang.compiler.IInlineExpressionCompiler; import io.sarl.lang.controlflow.ISarlEarlyExitComputer; import io.sarl.lang.core.Agent; import io.sarl.lang.core.AgentTrait; import io.sarl.lang.core.Behavior; import io.sarl.lang.core.Capacity; import io.sarl.lang.core.Event; import io.sarl.lang.core.Skill; import io.sarl.lang.sarl.SarlAction; import io.sarl.lang.sarl.SarlAgent; import io.sarl.lang.sarl.SarlArtifact; import io.sarl.lang.sarl.SarlBehavior; import io.sarl.lang.sarl.SarlBehaviorUnit; import io.sarl.lang.sarl.SarlCapacity; import io.sarl.lang.sarl.SarlCapacityUses; import io.sarl.lang.sarl.SarlConstructor; import io.sarl.lang.sarl.SarlEnumLiteral; import io.sarl.lang.sarl.SarlEvent; import io.sarl.lang.sarl.SarlField; import io.sarl.lang.sarl.SarlFormalParameter; import io.sarl.lang.sarl.SarlRequiredCapacity; import io.sarl.lang.sarl.SarlSkill; import io.sarl.lang.sarl.SarlSpace; import io.sarl.lang.services.SARLGrammarKeywordAccess; import io.sarl.lang.typesystem.InheritanceHelper; import io.sarl.lang.typesystem.SARLAnnotationUtil; import io.sarl.lang.typesystem.SARLExpressionHelper; import io.sarl.lang.typesystem.SARLReentrantTypeResolver; import io.sarl.lang.util.ClearableReference; import io.sarl.lang.util.JvmVisibilityComparator; import io.sarl.lang.util.Utils; /** Infers a JVM model from the source model. * * <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. * * @author $Author: srodriguez$ * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ @SuppressWarnings({"checkstyle:classfanoutcomplexity", "checkstyle:methodcount"}) @Singleton public class SARLJvmModelInferrer extends XtendJvmModelInferrer { private static final String RUNNABLE_COLLECTION = Utils.createNameForHiddenLocalVariable("runnableCollection"); //$NON-NLS-1$ private static final String HASHCODE_FUNCTION_NAME = "hashCode"; //$NON-NLS-1$ private static final String EQUALS_FUNCTION_NAME = "equals"; //$NON-NLS-1$ private static final String CLONE_FUNCTION_NAME = "clone"; //$NON-NLS-1$ private static final String SERIAL_FIELD_NAME = "serialVersionUID"; //$NON-NLS-1$ /** See the filter in the super class. */ private static final Predicate<JvmAnnotationReference> ANNOTATION_TRANSLATION_FILTER = (annotation) -> { if (annotation == null || annotation.getAnnotation() == null) { return false; } //JvmType annotationType = annotation.getAnnotation(); //if (annotationType instanceof JvmAnnotationType // && DisableCodeGenerationAdapter.isDisabled((JvmDeclaredType) annotationType)) { // return false; //} return true; }; private static final Set<Class<?>> EQUALITY_TEST_TYPES = new TreeSet<>((ele1, ele2) -> ele1.getName().compareTo(ele2.getName())); static { EQUALITY_TEST_TYPES.add(Boolean.TYPE); EQUALITY_TEST_TYPES.add(Boolean.class); EQUALITY_TEST_TYPES.add(Integer.TYPE); EQUALITY_TEST_TYPES.add(Integer.class); EQUALITY_TEST_TYPES.add(Character.TYPE); EQUALITY_TEST_TYPES.add(Character.class); EQUALITY_TEST_TYPES.add(Byte.TYPE); EQUALITY_TEST_TYPES.add(Byte.class); EQUALITY_TEST_TYPES.add(Short.TYPE); EQUALITY_TEST_TYPES.add(Short.class); EQUALITY_TEST_TYPES.add(Long.TYPE); EQUALITY_TEST_TYPES.add(Long.class); EQUALITY_TEST_TYPES.add(Float.TYPE); EQUALITY_TEST_TYPES.add(Float.class); EQUALITY_TEST_TYPES.add(Double.TYPE); EQUALITY_TEST_TYPES.add(Double.class); EQUALITY_TEST_TYPES.add(String.class); EQUALITY_TEST_TYPES.add(UUID.class); } /** The injector for generation contexts. */ @Inject private MembersInjector<GenerationContext> contextInjector; /** Generator of JVM elements. */ @Inject private JvmTypesBuilder typeBuilder; /** Generator's logger. */ @Inject private Logger log; /** Manager of SARL action signatures. */ @Inject private IActionPrototypeProvider sarlSignatureProvider; /** Tracker of field initialization. */ @Inject private ReadAndWriteTracking readAndWriteTracking; /** Several generation services. */ @Inject private CommonTypeComputationServices services; /** Extended helper for using XExpressions. */ @Inject private SARLExpressionHelper expressionHelper; /** Computer of early-exits for SARL. */ @Inject private ISarlEarlyExitComputer earlyExitComputer; /** Finder of annotations. */ @Inject private AnnotationLookup annotationFinder; /** Utilities for JVM annotations. */ @Inject private SARLAnnotationUtil annotationUtils; /** SARL Serializer. */ @Inject private ISerializer sarlSerializer; @Inject private TypesFactory typesFactory; @Inject private TypeReferences typeReferences; @Inject private SyntheticNameClashResolver nameClashResolver; @Inject private IJvmModelAssociator associator; @Inject private SarlJvmModelAssociations sarlAssociations; @Inject private JvmVisibilityComparator visibilityComparator; @Inject private LanguageInfo languageInfo; @Inject private IContextFinder contextFinder; @Inject private SARLGrammarKeywordAccess grammarKeywordAccess; @Inject private IInlineExpressionCompiler inlineExpressionCompiler; @Inject private InheritanceHelper inheritanceHelper; /** Generation contexts. */ private LinkedList<GenerationContext> bufferedContexes = new LinkedList<>(); /** Log an internal error but do not fail. * * @param exception the exception to log. */ protected void logInternalError(Throwable exception) { if (exception != null && this.log.isLoggable(Level.FINEST)) { this.log.log(Level.FINEST, Messages.SARLJvmModelInferrer_0, exception); } } /** Log an internal error but do not fail. * * @param message the internal message. */ protected void logInternalError(String message) { if (this.log.isLoggable(Level.FINEST) && !Strings.isNullOrEmpty(message)) { this.log.log(Level.FINEST, MessageFormat.format(Messages.SARLJvmModelInferrer_1, Messages.SARLJvmModelInferrer_0, message)); } } /** Add annotation safely. * * <p>This function creates an annotation reference. If the type for the annotation is not found; * no annotation is added. * * @param target the receiver of the annotation. * @param annotationType the type of the annotation. * @param values the annotations values. * @return the annotation reference or <code>null</code> if the annotation cannot be added. */ private JvmAnnotationReference addAnnotationSafe(JvmAnnotationTarget target, Class<?> annotationType, String... values) { assert target != null; assert annotationType != null; try { final JvmAnnotationReference annotationRef = this._annotationTypesBuilder.annotationRef(annotationType, values); if (annotationRef != null) { if (target.getAnnotations().add(annotationRef)) { return annotationRef; } } } catch (IllegalArgumentException exception) { // Ignore } return null; } /** Add annotation safely. * * <p>This function creates an annotation reference. If the type for the annotation is not found; * no annotation is added. * * @param target the receiver of the annotation. * @param annotationType the type of the annotation. * @param value the annotations value. * @return the annotation reference or <code>null</code> if the annotation cannot be added. */ private JvmAnnotationReference addAnnotationSafe(JvmAnnotationTarget target, Class<?> annotationType, int value) { assert target != null; assert annotationType != null; try { final JvmAnnotationReference result = this.typesFactory.createJvmAnnotationReference(); final JvmType jvmType = this.typeReferences.findDeclaredType(annotationType, target); if (jvmType == null) { return null; } if (!(jvmType instanceof JvmAnnotationType)) { return null; } result.setAnnotation((JvmAnnotationType) jvmType); final JvmIntAnnotationValue annotationValue = this.typesFactory.createJvmIntAnnotationValue(); annotationValue.getValues().add(value); result.getExplicitValues().add(annotationValue); if (target.getAnnotations().add(result)) { return result; } } catch (IllegalArgumentException exception) { // Ignore } return null; } /** Create an annotation with classes as values. * * @param type - the type of the annotation. * @param values - the values. * @return the reference to the JVM annotation. */ private JvmAnnotationReference annotationClassRef(Class<? extends Annotation> type, List<? extends JvmTypeReference> values) { try { final JvmAnnotationReference annot = this._annotationTypesBuilder.annotationRef(type); final JvmTypeAnnotationValue annotationValue = this.services.getTypesFactory().createJvmTypeAnnotationValue(); for (final JvmTypeReference value : values) { annotationValue.getValues().add(this.typeBuilder.cloneWithProxies(value)); } annot.getExplicitValues().add(annotationValue); return annot; } catch (IllegalArgumentException exception) { // ignore } return null; } /** Initialize the local class to the given expression. * * @param context the generation context. * @param feature the feature which contains the expression. * @param expression the expression which contains the local class. */ protected void initializeLocalTypes(GenerationContext context, JvmFeature feature, XExpression expression) { if (expression != null) { int localTypeIndex = context.getLocalTypeIndex(); final TreeIterator<EObject> iterator = EcoreUtil2.getAllNonDerivedContents(expression, true); final String nameStub = "__" + feature.getDeclaringType().getSimpleName() + "_"; //$NON-NLS-1$ //$NON-NLS-2$ while (iterator.hasNext()) { final EObject next = iterator.next(); if (next.eClass() == XtendPackage.Literals.ANONYMOUS_CLASS) { inferLocalClass((AnonymousClass) next, nameStub + localTypeIndex, feature); iterator.prune(); ++localTypeIndex; } } context.setLocalTypeIndex(localTypeIndex); } } /** {@inheritDoc}. * * <p>Overridden for: removing the existing associated body, and delaying the local type inferrence. */ @Override protected void setBody(JvmExecutable executable, XExpression expression) { final GenerationContext context = getContext( EcoreUtil2.getContainerOfType(executable, JvmType.class)); this.typeBuilder.removeExistingBody(executable); this.associator.associateLogicalContainer(expression, executable); if (expression != null) { if (context.getParentContext() == null) { context.getPostFinalizationElements().add(() -> { initializeLocalTypes(context, executable, expression); }); } else { initializeLocalTypes(context, executable, expression); } } else { initializeLocalTypes(context, executable, expression); } } /** Set the body of the executable. * * @param executable the executable. * @param expression the body definition. */ protected void setBody(JvmExecutable executable, StringConcatenationClient expression) { this.typeBuilder.setBody(executable, expression); } /** Set the body of the executable. * * @param executable the executable. * @param expression the body definition. */ protected void setBody(JvmExecutable executable, Procedure1<ITreeAppendable> expression) { this.typeBuilder.setBody(executable, expression); } /** Open the context for the generation of a SARL-specific element. * * @param sarlObject the SARL object that is the cause of the generation. * @param type - the generated type. * @param supportedMemberTypes the types of the supported members. * @return the created context. */ protected final synchronized GenerationContext openContext(EObject sarlObject, JvmIdentifiableElement type, final Iterable<Class<? extends XtendMember>> supportedMemberTypes) { assert type != null; assert supportedMemberTypes != null; this.sarlSignatureProvider.clear(type); final GenerationContext context = new GenerationContext(sarlObject, type.getIdentifier()) { @Override public boolean isSupportedMember(XtendMember member) { for (final Class<? extends XtendMember> supportedMemberType : supportedMemberTypes) { if (supportedMemberType.isInstance(member)) { return true; } } return false; } }; this.contextInjector.injectMembers(context); this.bufferedContexes.push(context); return context; } /** Close a generation context. * * @param context the context to be closed. */ protected final void closeContext(GenerationContext context) { boolean runPostElements = false; GenerationContext selectedContext = null; synchronized (this) { final Iterator<GenerationContext> iterator = this.bufferedContexes.iterator(); while (selectedContext == null && iterator.hasNext()) { final GenerationContext candidate = iterator.next(); if (Objects.equal(candidate.getTypeIdentifier(), context.getTypeIdentifier())) { runPostElements = candidate.getParentContext() == null; selectedContext = candidate; } } } if (selectedContext == null) { throw new IllegalStateException("Not same contexts when closing"); //$NON-NLS-1$ } if (runPostElements) { for (final Runnable handler : selectedContext.getPostFinalizationElements()) { handler.run(); } } synchronized (this) { final Iterator<GenerationContext> iterator = this.bufferedContexes.iterator(); while (iterator.hasNext()) { final GenerationContext candidate = iterator.next(); if (selectedContext == candidate) { candidate.setParentContext(null); iterator.remove(); return; } } } } /** Replies the SARL-specific generation context. * * @param type - the generated type. * @return the SARL-specific generation context. */ protected final synchronized GenerationContext getContext(JvmIdentifiableElement type) { for (final GenerationContext candidate : this.bufferedContexes) { if (Objects.equal(candidate.getTypeIdentifier(), type.getIdentifier())) { return candidate; } } throw new GenerationContextNotFoundInternalError(type); } @Override @SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"}) protected JvmDeclaredType doInferTypeSceleton( XtendTypeDeclaration declaration, IJvmDeclaredTypeAcceptor acceptor, boolean preIndexingPhase, XtendFile xtendFile, List<Runnable> doLater) { if (Strings.isNullOrEmpty(declaration.getName())) { return null; } try { // Autowrap the provided runnable elements in order to avoid the internal exceptions // to stop the JVM generation too early. final List<Runnable> doLaterExceptionSafe = new AbstractList<Runnable>() { @Override public void add(int index, Runnable element) { doLater.add(index, wrap(element)); } @Override public Runnable set(int index, Runnable element) { return unwrap(doLater.set(index, wrap(element))); } @Override public Runnable remove(int index) { return unwrap(doLater.remove(index)); } @Override public Runnable get(int index) { return unwrap(doLater.get(index)); } @Override public int size() { return doLater.size(); } private Runnable wrap(Runnable runnable) { return new SafeRunnable(runnable); } private Runnable unwrap(Runnable runnable) { final SafeRunnable wrapper = (SafeRunnable) runnable; return wrapper == null ? null : wrapper.wrapped; } /** Safe runnable. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ final class SafeRunnable implements Runnable { @SuppressWarnings("checkstyle:visibilitymodifier") public final Runnable wrapped; SafeRunnable(Runnable wrapped) { this.wrapped = wrapped; } @Override public void run() { try { this.wrapped.run(); } catch (InternalError internalError) { throw internalError; } catch (Exception exception) { logInternalError(exception); } } } }; if (declaration instanceof SarlAgent) { final SarlAgent sarlAgent = (SarlAgent) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlAgent, javaType)); } return javaType; } if (declaration instanceof SarlBehavior) { final SarlBehavior sarlBehavior = (SarlBehavior) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlBehavior, javaType)); } return javaType; } if (declaration instanceof SarlEvent) { final SarlEvent sarlEvent = (SarlEvent) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlEvent, javaType)); } return javaType; } if (declaration instanceof SarlSkill) { final SarlSkill sarlSkill = (SarlSkill) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlSkill, javaType)); } return javaType; } if (declaration instanceof SarlCapacity) { final SarlCapacity sarlCapacity = (SarlCapacity) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlCapacity, javaType)); } return javaType; } if (declaration instanceof SarlSpace) { final SarlSpace sarlSpace = (SarlSpace) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlSpace, javaType)); } return javaType; } if (declaration instanceof SarlArtifact) { final SarlArtifact sarlArtifact = (SarlArtifact) declaration; final JvmGenericType javaType = this.typesFactory.createJvmGenericType(); if (!preIndexingPhase) { doLaterExceptionSafe.add(() -> initialize(sarlArtifact, javaType)); } return javaType; } return super.doInferTypeSceleton(declaration, acceptor, preIndexingPhase, xtendFile, doLaterExceptionSafe); } catch (InternalError internalError) { throw internalError; } catch (Exception exception) { logInternalError(exception); return null; } } @Override protected final void addDefaultConstructor(XtendClass source, JvmGenericType target) { // This function is called for adding the default constructor in a class. // This function does nothing, and should not be call from the SARL model inferrer. } /** Add the default constructors. * * <p>The default constructors have the same signature as the constructors of the super class. * * <p>This function adds the default constructors if no constructor was already added. This condition * is determined with a call to {@link GenerationContext#hasConstructor()}. * * @param source the SARL element in which no constructor was specified. This SARL element should be * associated to the {@code target} element. * @param target the JVM type that is receiving the default constructor. * @see GenerationContext#hasConstructor() */ protected void addDefaultConstructors(XtendTypeDeclaration source, JvmGenericType target) { final GenerationContext context = getContext(target); if (!context.hasConstructor()) { // Special case: if a value was not set, we cannot create implicit constructors // in order to have the issue message "The blank final field may not have been initialized". final boolean notInitializedValueField = Iterables.any(source.getMembers(), (it) -> { if (it instanceof XtendField) { final XtendField op = (XtendField) it; if (op.isFinal() && op.getInitialValue() == null) { return true; } } return false; }); if (!notInitializedValueField) { // Add the default constructors for the agent, if not already added final JvmTypeReference reference = target.getExtendedClass(); if (reference != null) { final JvmType type = reference.getType(); if (type instanceof JvmGenericType) { copyVisibleJvmConstructors( (JvmGenericType) type, target, source, Sets.newTreeSet()); } } } } } /** {@inheritDoc}. * * <p>The function is overridden in order to interleave the instructions from Xtend and the ones needed * for SARL. */ @Override public void inferLocalClass(AnonymousClass anonymousClass, String localClassName, JvmFeature container) { // Issue #356: do not generate if the class has no name. assert anonymousClass != null; assert container != null; if (Strings.isNullOrEmpty(localClassName)) { return; } // Issue #363: do not generate the class if the SARL library is incompatible. if (!Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, anonymousClass)) { return; } // Create the inner type // --- Begin Xtend Part try { final JvmGenericType inferredJvmType = this.typesFactory.createJvmGenericType(); inferredJvmType.setSimpleName(localClassName); inferredJvmType.setAnonymous(!hasAdditionalMembers(anonymousClass)); inferredJvmType.setFinal(true); inferredJvmType.setVisibility(JvmVisibility.DEFAULT); inferredJvmType.getSuperTypes().add(this.typeBuilder.inferredType(anonymousClass)); container.getLocalClasses().add(inferredJvmType); this.associator.associatePrimary(anonymousClass, inferredJvmType); // --- End Xtend Part // Create the generation context that is used by the other transformation functions. final GenerationContext parentContext = getContext( EcoreUtil2.getContainerOfType(container, JvmType.class)); final GenerationContext context = openContext(anonymousClass, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class, SarlAction.class)); context.setParentContext(parentContext); try { // --- Begin Xtend Part for (final XtendMember member : anonymousClass.getMembers()) { if (context.isSupportedMember(member)) { transform(member, inferredJvmType, true); } } appendSyntheticDispatchMethods(anonymousClass, inferredJvmType); this.nameClashResolver.resolveNameClashes(inferredJvmType); // --- End Xtend Part // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( anonymousClass, inferredJvmType, context); } finally { closeContext(context); } } catch (InternalError internalError) { throw internalError; } catch (Exception exception) { logInternalError(exception); } } @Override protected void initialize(XtendClass source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the class has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Issue #363: do not generate the class if the SARL library is incompatible. if (!Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class, SarlAction.class)); try { // Initialize the context with inheriting features Utils.populateInheritanceContext( inferredJvmType, context.getInheritedFinalOperations(), context.getInheritedOverridableOperations(), null, context.getInheritedOperationsToImplement(), null, this.sarlSignatureProvider); // Standard OOP generation super.initialize(source, inferredJvmType); // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( source, inferredJvmType, context); // Add functions dedicated to comparisons (equals, hashCode, etc.) appendComparisonFunctions(context, source, inferredJvmType); // Add clone functions if the generated type is cloneable appendCloneFunctionIfCloneable(context, source, inferredJvmType); // Add the default constructors for the behavior, if not already added addDefaultConstructors(source, inferredJvmType); // Add serialVersionUID field if the generated type is serializable appendSerialNumberIfSerializable(context, source, inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); } finally { closeContext(context); } } @Override protected void initialize(XtendInterface source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the interface has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Issue #363: do not generate the interface if the SARL library is incompatible. if (!Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlAction.class)); try { // Initialize the context with inheriting features Utils.populateInheritanceContext( inferredJvmType, context.getInheritedFinalOperations(), context.getInheritedOverridableOperations(), null, context.getInheritedOperationsToImplement(), null, this.sarlSignatureProvider); // Standard OOP generation super.initialize(source, inferredJvmType); // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( source, inferredJvmType, context); // Add the @FunctionalInterface appendFunctionalInterfaceAnnotation(inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); } finally { closeContext(context); } } @Override protected void initialize(XtendAnnotationType source, JvmAnnotationType inferredJvmType) { // Issue #356: do not generate if the annotation type has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Issue #363: do not generate the annotation if the SARL library is incompatible. if (!Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Collections.singleton(SarlEnumLiteral.class)); try { // Initialize the context with inheriting features Utils.populateInheritanceContext( inferredJvmType, context.getInheritedFinalOperations(), context.getInheritedOverridableOperations(), null, context.getInheritedOperationsToImplement(), null, this.sarlSignatureProvider); // Standard OOP generation super.initialize(source, inferredJvmType); // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( source, inferredJvmType, context); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); } finally { closeContext(context); } } @Override protected void initialize(XtendEnum source, JvmEnumerationType inferredJvmType) { // Issue #356: do not generate if the enumeration has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Issue #363: do not generate the enumeration if the SARL library is incompatible. if (!Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Collections.singleton(SarlField.class)); try { // Initialize the context with inheriting features Utils.populateInheritanceContext( inferredJvmType, context.getInheritedFinalOperations(), context.getInheritedOverridableOperations(), null, context.getInheritedOperationsToImplement(), null, this.sarlSignatureProvider); // Standard OOP generation super.initialize(source, inferredJvmType); // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( source, inferredJvmType, context); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); } finally { closeContext(context); } } /** Initialize the SARL agent type. * * @param source the source. * @param inferredJvmType the JVM type. */ protected void initialize(SarlAgent source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the agent has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class, SarlAction.class, SarlBehaviorUnit.class, SarlCapacityUses.class, SarlRequiredCapacity.class)); try { // Copy the documentation this.typeBuilder.copyDocumentationTo(source, inferredJvmType); // Change the modifiers on the generated type. inferredJvmType.setStatic(false); inferredJvmType.setStrictFloatingPoint(false); inferredJvmType.setVisibility(source.getVisibility()); final boolean isAbstract = source.isAbstract() || Utils.hasAbstractMember(source); inferredJvmType.setAbstract(isAbstract); inferredJvmType.setFinal(!isAbstract && source.isFinal()); // Generate the annotations. translateAnnotationsTo(source.getAnnotations(), inferredJvmType); // Generate the extended types. appendConstrainedExtends(context, inferredJvmType, Agent.class, SarlAgent.class, source.getExtends()); // Issue #363: do not generate the agent if the SARL library is incompatible. if (Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { // Generate the members of the generated type. appendAOPMembers( inferredJvmType, source, context); } // Add functions dedicated to comparisons (equals, hashCode, etc.) appendComparisonFunctions(context, source, inferredJvmType); // Add clone functions if the generated type is cloneable appendCloneFunctionIfCloneable(context, source, inferredJvmType); // Add the default constructors for the behavior, if not already added addDefaultConstructors(source, inferredJvmType); // Add serialVersionUID field if the generated type is serializable appendSerialNumberIfSerializable(context, source, inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); // Resolving any name conflict with the generated JVM type this.nameClashResolver.resolveNameClashes(inferredJvmType); } finally { closeContext(context); } } /** Initialize the SARL behavior type. * * @param source the source. * @param inferredJvmType the JVM type. */ protected void initialize(SarlBehavior source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the behavior has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class, SarlAction.class, SarlBehaviorUnit.class, SarlCapacityUses.class, SarlRequiredCapacity.class)); try { // Copy the documentation this.typeBuilder.copyDocumentationTo(source, inferredJvmType); // Change the modifiers on the generated type. inferredJvmType.setVisibility(source.getVisibility()); inferredJvmType.setStatic(false); final boolean isAbstract = source.isAbstract() || Utils.hasAbstractMember(source); inferredJvmType.setAbstract(isAbstract); inferredJvmType.setStrictFloatingPoint(false); inferredJvmType.setFinal(!isAbstract && source.isFinal()); // Generate the annotations. translateAnnotationsTo(source.getAnnotations(), inferredJvmType); // Generate the extended types. appendConstrainedExtends(context, inferredJvmType, Behavior.class, SarlBehavior.class, source.getExtends()); // Issue #363: do not generate the behavior if the SARL library is incompatible. if (Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { // Generate the members of the generated type. appendAOPMembers( inferredJvmType, source, context); } // Add functions dedicated to comparisons (equals, hashCode, etc.) appendComparisonFunctions(context, source, inferredJvmType); // Add clone functions if the generated type is cloneable appendCloneFunctionIfCloneable(context, source, inferredJvmType); // Add the default constructors for the behavior, if not already added addDefaultConstructors(source, inferredJvmType); // Add serialVersionUID field if the generated type is serializable appendSerialNumberIfSerializable(context, source, inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); // Resolving any name conflict with the generated JVM type this.nameClashResolver.resolveNameClashes(inferredJvmType); } finally { closeContext(context); } } /** Initialize the SARL event type. * * @param source the source. * @param inferredJvmType the JVM type. */ protected void initialize(SarlEvent source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the event has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class)); try { // Copy the documentation this.typeBuilder.copyDocumentationTo(source, inferredJvmType); // Change the modifiers on the generated type. inferredJvmType.setVisibility(source.getVisibility()); inferredJvmType.setStatic(false); inferredJvmType.setAbstract(false); inferredJvmType.setStrictFloatingPoint(false); if (!inferredJvmType.isAbstract()) { inferredJvmType.setFinal(source.isFinal()); } // Generate the annotations. translateAnnotationsTo(source.getAnnotations(), inferredJvmType); // Generate the extended types. appendConstrainedExtends(context, inferredJvmType, Event.class, SarlEvent.class, source.getExtends()); // Issue #363: do not generate the event if the SARL library is incompatible. if (Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { // Generate the members of the generated type. appendAOPMembers( inferredJvmType, source, context); } // Add the default constructors for the behavior, if not already added addDefaultConstructors(source, inferredJvmType); // Add functions dedicated to comparisons (equals, hashCode, etc.) appendComparisonFunctions(context, source, inferredJvmType); // Add functions dedicated to String representation(toString, etc.) appendToStringFunctions(context, source, inferredJvmType); // Add clone functions if the generated type is cloneable appendCloneFunctionIfCloneable(context, source, inferredJvmType); // Add serialVersionUID field if the generated type is serializable appendSerialNumberIfSerializable(context, source, inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); // Resolving any name conflict with the generated JVM type this.nameClashResolver.resolveNameClashes(inferredJvmType); } finally { closeContext(context); } } /** Initialize the SARL skill type. * * @param source the source. * @param inferredJvmType the JVM type. */ protected void initialize(SarlSkill source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the skill has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Arrays.asList( SarlField.class, SarlConstructor.class, SarlAction.class, SarlBehaviorUnit.class, SarlCapacityUses.class, SarlRequiredCapacity.class)); try { // Copy the documentation this.typeBuilder.copyDocumentationTo(source, inferredJvmType); // Change the modifiers on the generated type. inferredJvmType.setVisibility(source.getVisibility()); inferredJvmType.setStatic(false); final boolean isAbstract = source.isAbstract() || Utils.hasAbstractMember(source); inferredJvmType.setAbstract(isAbstract); inferredJvmType.setStrictFloatingPoint(false); inferredJvmType.setFinal(!isAbstract && source.isFinal()); // Generate the annotations. translateAnnotationsTo(source.getAnnotations(), inferredJvmType); // Generate the extended types. appendConstrainedExtends(context, inferredJvmType, Skill.class, SarlSkill.class, source.getExtends()); appendConstrainedImplements(context, inferredJvmType, Capacity.class, SarlCapacity.class, source.getImplements()); // Issue #363: do not generate the skill if the SARL library is incompatible. if (Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { // Generate the members of the generated type. appendAOPMembers( inferredJvmType, source, context); } // Add functions dedicated to comparisons (equals, hashCode, etc.) appendComparisonFunctions(context, source, inferredJvmType); // Add clone functions if the generated type is cloneable appendCloneFunctionIfCloneable(context, source, inferredJvmType); // Add the default constructors for the behavior, if not already added addDefaultConstructors(source, inferredJvmType); // Add serialVersionUID field if the generated type is serializable appendSerialNumberIfSerializable(context, source, inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); // Resolving any name conflict with the generated JVM type this.nameClashResolver.resolveNameClashes(inferredJvmType); } finally { closeContext(context); } } /** Initialize the SARL capacity type. * * @param source the source. * @param inferredJvmType the JVM type. */ protected void initialize(SarlCapacity source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the capacity has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // Create the generation context that is used by the other transformation functions. final GenerationContext context = openContext(source, inferredJvmType, Collections.singleton(SarlAction.class)); try { // Copy the documentation this.typeBuilder.copyDocumentationTo(source, inferredJvmType); // Change the modifiers on the generated type. inferredJvmType.setInterface(true); inferredJvmType.setAbstract(true); inferredJvmType.setVisibility(source.getVisibility()); inferredJvmType.setStatic(false); inferredJvmType.setStrictFloatingPoint(false); inferredJvmType.setFinal(false); // Generate the annotations. translateAnnotationsTo(source.getAnnotations(), inferredJvmType); // Generate the extended types. appendConstrainedExtends(context, inferredJvmType, Capacity.class, SarlCapacity.class, source.getExtends()); // Issue #363: do not generate the capacity if the SARL library is incompatible. if (Utils.isCompatibleSARLLibraryOnClasspath(this.typeReferences, source)) { // Generate the members of the generated type. appendAOPMembers( inferredJvmType, source, context); } // Add the @FunctionalInterface appendFunctionalInterfaceAnnotation(inferredJvmType); // Add the specification version of SARL appendSARLSpecificationVersion(context, source, inferredJvmType); // Add the type of SARL Element appendSARLElementType(source, inferredJvmType); // Resolving any name conflict with the generated JVM type this.nameClashResolver.resolveNameClashes(inferredJvmType); } finally { closeContext(context); } // Generate the internal class for context-aware wrappers // Change the modifiers on the generated type. final JvmGenericType innerType = this.typesFactory.createJvmGenericType(); innerType.setInterface(false); innerType.setAbstract(false); innerType.setVisibility(JvmVisibility.PUBLIC); innerType.setStatic(true); innerType.setStrictFloatingPoint(false); innerType.setFinal(false); final String innerTypeName = Capacity.ContextAwareCapacityWrapper.class.getSimpleName(); innerType.setSimpleName(innerTypeName); inferredJvmType.getMembers().add(innerType); final JvmTypeParameter typeParameter = this.typesFactory.createJvmTypeParameter(); typeParameter.setName("C"); //$NON-NLS-1$ final JvmUpperBound constraint = this.typesFactory.createJvmUpperBound(); constraint.setTypeReference(this._typeReferenceBuilder.typeRef(inferredJvmType)); typeParameter.getConstraints().add(constraint); innerType.getTypeParameters().add(typeParameter); final Iterator<JvmTypeReference> extendedTypeIterator = inferredJvmType.getExtendedInterfaces().iterator(); if (extendedTypeIterator.hasNext()) { final JvmTypeReference extendedType = extendedTypeIterator.next(); final JvmTypeReference superType = this._typeReferenceBuilder.typeRef( extendedType.getQualifiedName() + "$" + innerTypeName, //$NON-NLS-1$ this._typeReferenceBuilder.typeRef(typeParameter)); innerType.getSuperTypes().add(superType); } innerType.getSuperTypes().add(this._typeReferenceBuilder.typeRef(inferredJvmType)); final JvmConstructor constructor = this.typesFactory.createJvmConstructor(); constructor.setVisibility(JvmVisibility.PUBLIC); innerType.getMembers().add(constructor); final JvmFormalParameter parameter1 = this.typesFactory.createJvmFormalParameter(); parameter1.setName("capacity"); //$NON-NLS-1$ parameter1.setParameterType(this._typeReferenceBuilder.typeRef(typeParameter)); constructor.getParameters().add(parameter1); final JvmFormalParameter parameter2 = this.typesFactory.createJvmFormalParameter(); parameter2.setName("caller"); //$NON-NLS-1$ parameter2.setParameterType(this._typeReferenceBuilder.typeRef(AgentTrait.class)); constructor.getParameters().add(parameter2); setBody(constructor, (it) -> { it.append("super(capacity, caller);"); //$NON-NLS-1$ }); final Set<ActionPrototype> createdActions = new TreeSet<>(); for (final JvmGenericType sourceType : Iterables.concat( Collections.singletonList(inferredJvmType), Iterables.transform(Iterables.skip(inferredJvmType.getExtendedInterfaces(), 1), (it) -> { return (JvmGenericType) it.getType(); }))) { copyNonStaticPublicJvmOperations(sourceType, innerType, createdActions, (operation, it) -> { it.append("try {"); //$NON-NLS-1$ it.newLine(); it.append(" ensureCallerInLocalThread();"); //$NON-NLS-1$ it.newLine(); it.append(" "); //$NON-NLS-1$ if (operation.getReturnType() != null && !Objects.equal("void", operation.getReturnType().getIdentifier())) { //$NON-NLS-1$ it.append("return "); //$NON-NLS-1$ } it.append("this.capacity."); //$NON-NLS-1$ it.append(operation.getSimpleName()); it.append("("); //$NON-NLS-1$ boolean first = true; for (final JvmFormalParameter fparam : operation.getParameters()) { if (first) { first = false; } else { it.append(", "); //$NON-NLS-1$ } it.append(fparam.getName()); } it.append(");"); //$NON-NLS-1$ it.newLine(); it.append("} finally {"); //$NON-NLS-1$ it.newLine(); it.append(" resetCallerInLocalThread();"); //$NON-NLS-1$ it.newLine(); it.append("}"); //$NON-NLS-1$ }); } } /** Initialize the SARL space type. * * @param source the source. * @param inferredJvmType the JVM type. */ @SuppressWarnings("static-method") protected void initialize(SarlSpace source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the space has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // TODO: Generate the space } /** Initialize the SARL artifact type. * * @param source the source. * @param inferredJvmType the JVM type. */ @SuppressWarnings("static-method") protected void initialize(SarlArtifact source, JvmGenericType inferredJvmType) { // Issue #356: do not generate if the space has no name. assert source != null; assert inferredJvmType != null; if (Strings.isNullOrEmpty(source.getName())) { return; } // TODO: Generate the artifact } @Override protected void transform(XtendMember sourceMember, JvmGenericType container, boolean allowDispatch) { try { if (sourceMember instanceof SarlBehaviorUnit) { transform((SarlBehaviorUnit) sourceMember, container); } else if (sourceMember instanceof SarlCapacityUses) { transform((SarlCapacityUses) sourceMember, container); } else if (sourceMember instanceof SarlRequiredCapacity) { transform((SarlRequiredCapacity) sourceMember, container); } else { super.transform(sourceMember, container, allowDispatch); } } catch (InternalError internalError) { throw internalError; } catch (Exception exception) { logInternalError(exception); } } /** Transform the constructor. * * @param source the feature to transform. * @param container the target container of the transformation result. */ @Override protected void transform(final XtendConstructor source, final JvmGenericType container) { final GenerationContext context = getContext(container); final boolean isVarArgs = Utils.isVarArg(source.getParameters()); // Generate the unique identifier of the constructor. final QualifiedActionName actionKey = this.sarlSignatureProvider.createConstructorQualifiedName(container); // Generate all the constructor signatures related to the constructor to create. final InferredPrototype constructorSignatures = this.sarlSignatureProvider.createPrototypeFromSarlModel( actionKey, Utils.isVarArg(source.getParameters()), source.getParameters()); // Generate the main Java constructor. final JvmConstructor constructor = this.typesFactory.createJvmConstructor(); container.getMembers().add(constructor); this.associator.associatePrimary(source, constructor); final JvmVisibility visibility = source.getVisibility(); constructor.setSimpleName(container.getSimpleName()); constructor.setVisibility(visibility); constructor.setVarArgs(isVarArgs); // Generate the parameters final List<InferredStandardParameter> paramList = constructorSignatures.getOriginalParameterTypes(); translateSarlFormalParameters( context, constructor, container, isVarArgs, source.getParameters(), false, paramList); // Generate additional information (type parameters, exceptions...) copyAndFixTypeParameters(source.getTypeParameters(), constructor); for (final JvmTypeReference exception : source.getExceptions()) { constructor.getExceptions().add(this.typeBuilder.cloneWithProxies(exception)); } translateAnnotationsTo(source.getAnnotations(), constructor); // Set the body. setBody(constructor, source.getExpression()); // The signature definition of the constructor. final ActionParameterTypes sigKey = this.sarlSignatureProvider.createParameterTypesFromJvmModel( isVarArgs, constructor.getParameters()); // Update the list of generated constructors if (context != null) { context.getGeneratedConstructors().put(sigKey, constructor); } copyAndCleanDocumentationTo(source, constructor); Runnable differedGeneration = () -> { // Generate the Java functions that correspond to the action with the parameter default values applied. for (final Entry<ActionParameterTypes, List<InferredStandardParameter>> entry : constructorSignatures.getInferredParameterTypes().entrySet()) { if (context == null || !context.getGeneratedConstructors().containsKey(entry.getKey())) { final List<InferredStandardParameter> otherSignature = entry.getValue(); // Generate the additional constructor that is invoke the main constructor previously generated. final JvmConstructor constructor2 = SARLJvmModelInferrer.this.typesFactory.createJvmConstructor(); container.getMembers().add(constructor2); copyAndCleanDocumentationTo(source, constructor2); final JvmVisibility vis = source.getVisibility(); constructor2.setSimpleName(container.getSimpleName()); constructor2.setVisibility(vis); constructor2.setVarArgs(isVarArgs); final List<String> args = translateSarlFormalParametersForSyntheticOperation( constructor2, container, isVarArgs, otherSignature); addAnnotationSafe( constructor2, DefaultValueUse.class, constructorSignatures.getFormalParameterTypes().toString()); appendGeneratedAnnotation(constructor2, context); setBody(constructor2, toStringConcatenation( "this(" //$NON-NLS-1$ + IterableExtensions.join(args, ", ") //$NON-NLS-1$ + ");")); //$NON-NLS-1$ // Update the list of the generated constructors. if (context != null) { context.getGeneratedConstructors().put(entry.getKey(), constructor2); } } } }; if (context != null) { context.getPreFinalizationElements().add(differedGeneration); context.setActionIndex(context.getActionIndex() + 1); context.incrementSerial(sigKey.hashCode()); } else { differedGeneration.run(); } } /** Transform the field. * * @param source the feature to transform. * @param container the target container of the transformation result. */ @Override protected void transform(XtendField source, JvmGenericType container) { super.transform(source, container); final GenerationContext context = getContext(container); if (context != null) { final String name = source.getName(); if (!Strings.isNullOrEmpty(name)) { context.incrementSerial(name.hashCode()); } final JvmTypeReference type = source.getType(); if (type != null) { context.incrementSerial(type.getIdentifier().hashCode()); } } } /** Transform the function. * * @param source the feature to transform. * @param container the target container of the transformation result. */ @Override @SuppressWarnings({"checkstyle:methodlength", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"}) protected void transform(final XtendFunction source, final JvmGenericType container, boolean allowDispatch) { final GenerationContext context = getContext(container); // Compute the operation name // Issue #355: null or empty name is possible. final String originalFunctionName = source.getName(); if (!Strings.isNullOrEmpty(originalFunctionName)) { final StringBuilder sourceNameBuffer = new StringBuilder(originalFunctionName); if (allowDispatch && source.isDispatch()) { sourceNameBuffer.insert(0, "_"); //$NON-NLS-1$ } final String sourceName = sourceNameBuffer.toString(); JvmVisibility visibility = source.getVisibility(); if (visibility == null) { visibility = JvmVisibility.DEFAULT; } // Create the main function final JvmOperation operation = this.typesFactory.createJvmOperation(); container.getMembers().add(operation); operation.setSimpleName(sourceName); operation.setVisibility(visibility); operation.setStrictFloatingPoint(source.isStrictFloatingPoint()); operation.setStatic(source.isStatic()); operation.setSynchronized(source.isSynchonized()); operation.setNative(source.isNative()); final boolean enableFunctionBody; if (container.isInterface()) { enableFunctionBody = context != null && context.isAtLeastJava8() && source.getExpression() != null && !operation.isAbstract() && !operation.isStatic() && !Utils.toLightweightTypeReference(container, this.services).isSubtypeOf(Capacity.class); operation.setDefault(enableFunctionBody); operation.setAbstract(!enableFunctionBody); operation.setFinal(false); } else { operation.setDefault(false); enableFunctionBody = context != null && !source.isAbstract(); operation.setAbstract(!enableFunctionBody); operation.setFinal(enableFunctionBody && source.isFinal()); } //final boolean isAbstractOperation = source.isAbstract() || container.isInterface(); //operation.setAbstract(isAbstractOperation); this.associator.associatePrimary(source, operation); // Type parameters copyAndFixTypeParameters(source.getTypeParameters(), operation); // Compute the identifier of the action. final QualifiedActionName actionKey = this.sarlSignatureProvider.createQualifiedActionName( container, sourceName); // Compute the different action prototypes associated to the action to create. final boolean isVarArgs = Utils.isVarArg(source.getParameters()); final InferredPrototype actionSignatures = this.sarlSignatureProvider.createPrototypeFromSarlModel( actionKey, isVarArgs, source.getParameters()); // Compute the action prototype of the action without optional parameter final ActionPrototype actSigKey = this.sarlSignatureProvider.createActionPrototype( sourceName, actionSignatures.getFormalParameterTypes()); // Generate the parameters final List<InferredStandardParameter> paramList = actionSignatures.getOriginalParameterTypes(); translateSarlFormalParameters( context, operation, container, isVarArgs, source.getParameters(), container.isInterface(), paramList); // Infer the return type final XExpression expression = source.getExpression(); JvmTypeReference returnType = null; if (source.getReturnType() != null) { returnType = source.getReturnType(); } else if (context != null) { JvmOperation inheritedOperation = context.getInheritedFinalOperations().get(actSigKey); if (inheritedOperation == null) { inheritedOperation = context.getInheritedOverridableOperations().get(actSigKey); } if (inheritedOperation == null) { inheritedOperation = context.getInheritedOperationsToImplement().get(actSigKey); } if (inheritedOperation != null) { returnType = inheritedOperation.getReturnType(); } if (returnType == null && expression != null && ((!(expression instanceof XBlockExpression)) || (!((XBlockExpression) expression).getExpressions().isEmpty()))) { returnType = this.typeBuilder.inferredType(expression); } } else if (expression != null && ((!(expression instanceof XBlockExpression)) || (!((XBlockExpression) expression).getExpressions().isEmpty()))) { returnType = this.typeBuilder.inferredType(expression); } final JvmTypeReference selectedReturnType; if (returnType == null) { selectedReturnType = this._typeReferenceBuilder.typeRef(Void.TYPE); } else if (InferredTypeIndicator.isInferred(returnType)) { selectedReturnType = returnType; } else { selectedReturnType = this.typeBuilder.cloneWithProxies(returnType); } operation.setReturnType(selectedReturnType); // Exceptions for (final JvmTypeReference exception : source.getExceptions()) { operation.getExceptions().add(this.typeBuilder.cloneWithProxies(exception)); } // Add the body if (enableFunctionBody) { setBody(operation, expression); } // User Annotations translateAnnotationsTo(source.getAnnotations(), operation); // Add @Inline annotation if (context != null && context.isAtLeastJava8() && context.getGeneratorConfig2().isGenerateInlineAnnotation() && !source.isAbstract() && !container.isInterface() && context.getParentContext() == null && this.annotationFinder.findAnnotation(operation, Inline.class) == null) { context.getPostFinalizationElements().add( () -> this.inlineExpressionCompiler.appendInlineAnnotation(operation, source)); } // Standard annotations if (source.isOverride() && this.annotationFinder.findAnnotation(operation, Override.class) == null && this.typeReferences.findDeclaredType(Override.class, source) != null) { addAnnotationSafe(operation, Override.class); } if (this.expressionHelper.isPurableOperation(operation, expression) && this.annotationFinder.findAnnotation(operation, Pure.class) == null && this.typeReferences.findDeclaredType(Pure.class, source) != null) { // The function is pure addAnnotationSafe(operation, Pure.class); } final List<JvmTypeReference> firedEvents; if (source instanceof SarlAction) { firedEvents = ((SarlAction) source).getFiredEvents(); } else { firedEvents = Collections.emptyList(); } // Detecting if the action is an early-exit action. // If true, the Java code is annotated to be usable by the SARL validator. //TODO: Generalize the detection of the EarlyExit boolean isEarlyExitTmp = false; final Iterator<JvmTypeReference> eventIterator = firedEvents.iterator(); while (!isEarlyExitTmp && eventIterator.hasNext()) { if (this.earlyExitComputer.isEarlyExitEvent(eventIterator.next())) { addAnnotationSafe(operation, EarlyExit.class); isEarlyExitTmp = true; } } final boolean isEarlyExit = isEarlyExitTmp; // Put the fired SARL events as Java annotations for beeing usable by the SARL validator. if (!firedEvents.isEmpty()) { operation.getAnnotations().add(annotationClassRef(FiredEvent.class, firedEvents)); } // 1. Ensure that the Java annotations related to the default value are really present. // They may be not present if the generated action is a specific version of an inherited // action with default values for parameters. // 2. Update the two collections that describes the implemented and implementable operations. if (context != null) { final JvmOperation implementedOperation = context.getInheritedOperationsToImplement().remove(actSigKey); // Put the annotations that were defined in the implemented operation if (implementedOperation != null) { if (this.annotationFinder.findAnnotation(implementedOperation, DefaultValueSource.class) != null && this.annotationFinder.findAnnotation(operation, DefaultValueSource.class) == null) { addAnnotationSafe(operation, DefaultValueSource.class); } // Reinject the @DefaultValue annotations final List<JvmFormalParameter> oparams = implementedOperation.getParameters(); final List<JvmFormalParameter> cparams = operation.getParameters(); assert oparams.size() == cparams.size(); for (int i = 0; i < oparams.size(); ++i) { final JvmFormalParameter op = oparams.get(i); final JvmFormalParameter cp = cparams.get(i); final String ovalue = this.annotationUtils.findStringValue(op, DefaultValue.class); if (ovalue != null && this.annotationFinder.findAnnotation(cp, DefaultValue.class) == null) { addAnnotationSafe(cp, DefaultValue.class, this.sarlSignatureProvider.qualifyDefaultValueID( implementedOperation.getDeclaringType().getIdentifier(), ovalue)); } } } // Add the main operation into the list of overridable operations context.getInheritedOverridableOperations().put(actSigKey, operation); } copyAndCleanDocumentationTo(source, operation); @SuppressWarnings("checkstyle:anoninnerlength") Runnable differedGeneration = () -> { // Generate the Java functions that correspond to the action with the parameter default values applied. for (final Entry<ActionParameterTypes, List<InferredStandardParameter>> otherSignature : actionSignatures.getInferredParameterTypes().entrySet()) { final ActionPrototype ak = SARLJvmModelInferrer.this.sarlSignatureProvider.createActionPrototype( sourceName, otherSignature.getKey()); if (ak != null && (context == null || (!context.getInheritedFinalOperations().containsKey(ak) && !context.getInheritedOverridableOperations().containsKey(ak)))) { // Generate the additional constructor that is invoke the main constructor previously generated. final JvmOperation operation2 = SARLJvmModelInferrer.this.typesFactory.createJvmOperation(); container.getMembers().add(operation2); operation2.setSimpleName(operation.getSimpleName()); operation2.setVisibility(operation.getVisibility()); operation2.setVarArgs(operation.isVarArgs()); operation2.setAbstract(operation.isAbstract()); operation2.setDeprecated(operation.isDeprecated()); operation2.setReturnType( SARLJvmModelInferrer.this.typeBuilder.cloneWithProxies(selectedReturnType)); operation2.setStatic(operation.isStatic()); operation2.setFinal(!operation.isStatic() && !container.isInterface()); operation2.setNative(false); operation2.setStrictFloatingPoint(false); operation2.setSynchronized(false); for (final JvmTypeReference exception : operation.getExceptions()) { operation2.getExceptions().add(SARLJvmModelInferrer.this.typeBuilder .cloneWithProxies(exception)); } translateAnnotationsTo(source.getAnnotations(), operation2); if (source.isOverride() && SARLJvmModelInferrer.this.annotationFinder.findAnnotation(operation, Override.class) == null && SARLJvmModelInferrer.this.typeReferences.findDeclaredType( Override.class, source) != null) { addAnnotationSafe( operation, Override.class); } final List<String> args = translateSarlFormalParametersForSyntheticOperation( operation2, container, isVarArgs, otherSignature.getValue()); if (context != null && context.isAtLeastJava8()) { operation2.setDefault(container.isInterface()); operation2.setAbstract(false); setBody(operation2, (it) -> { final JvmTypeReference type = operation2.getReturnType(); if (!SARLJvmModelInferrer.this.typeReferences.is(type, void.class)) { it.append("return "); //$NON-NLS-1$ } it.append(sourceName); it.append("("); //$NON-NLS-1$ it.append(IterableExtensions.join(args, ", ")); //$NON-NLS-1$ it.append(");"); //$NON-NLS-1$ }); } else { operation2.setDefault(false); operation2.setAbstract(true); } addAnnotationSafe( operation2, DefaultValueUse.class, actionSignatures.getFormalParameterTypes().toString()); appendGeneratedAnnotation(operation2, context); // If the main action is an early-exit action, the additional operation // is also an early-exit operation. //TODO: Generalize the detection of the EarlyExit if (isEarlyExit) { addAnnotationSafe( operation2, EarlyExit.class); } // Put the fired SARL events as Java annotations for beeing usable by the SARL validator. if (!firedEvents.isEmpty()) { operation2.getAnnotations().add( annotationClassRef(FiredEvent.class, firedEvents)); } // Copy the other annotations from the original operations for (final JvmAnnotationReference annotation : operation.getAnnotations()) { final String id = annotation.getAnnotation().getIdentifier(); if ((!DefaultValueSource.class.getName().equals(id) && (!EarlyExit.class.getName().equals(id))) && (!FiredEvent.class.getName().equals(id)) && (!Inline.class.getName().equals(id)) && (!Generated.class.getName().equals(id))) { try { final JvmAnnotationReference clone = SARLJvmModelInferrer.this._annotationTypesBuilder .annotationRef(id); for (final JvmAnnotationValue annotationValue : annotation.getExplicitValues()) { clone.getExplicitValues().add(EcoreUtil.copy(annotationValue)); } operation2.getAnnotations().add(clone); } catch (IllegalArgumentException exception) { // ignore } } } // Copy and clean the documentation copyAndCleanDocumentationTo(operation, operation2); // Update the two collections that describes the implemented and implementable operations. if (context != null) { context.getInheritedOperationsToImplement().remove(ak); context.getInheritedOverridableOperations().put(ak, operation2); } } } }; if (context != null) { context.getPreFinalizationElements().add(differedGeneration); context.setActionIndex(context.getActionIndex() + 1); context.incrementSerial(actSigKey.hashCode()); } else { differedGeneration.run(); } } } /** Transform the given behavior unit. * * @param source the feature to transform. * @param container the target container of the transformation result. */ protected void transform(final SarlBehaviorUnit source, JvmGenericType container) { final GenerationContext context = getContext(container); if (source.getName() != null && !Strings.isNullOrEmpty(source.getName().getSimpleName()) && context != null) { final XExpression guard = source.getGuard(); final boolean isTrueGuard; // Check the guard value if (guard == null) { isTrueGuard = true; } else if (guard instanceof XBooleanLiteral) { final XBooleanLiteral literal = (XBooleanLiteral) guard; if (literal.isIsTrue()) { isTrueGuard = true; } else { // The guard is always false => no need to generate the code return; } } else { isTrueGuard = false; } final JvmTypeReference voidType = this._typeReferenceBuilder.typeRef(Void.TYPE); //---------------- // Body function //---------------- // Name final String bodyMethodName = Utils.createNameForHiddenEventHandlerMethod(source.getName().getSimpleName(), context.getBehaviorUnitIndex()); // Operation final JvmOperation bodyOperation = this.typesFactory.createJvmOperation(); bodyOperation.setAbstract(false); bodyOperation.setNative(false); bodyOperation.setSynchronized(false); bodyOperation.setStrictFloatingPoint(false); bodyOperation.setFinal(false); bodyOperation.setVisibility(JvmVisibility.PRIVATE); bodyOperation.setStatic(false); bodyOperation.setSimpleName(bodyMethodName); bodyOperation.setReturnType(voidType); // Add to container container.getMembers().add(bodyOperation); this.associator.associatePrimary(source, bodyOperation); // First parameter: occurrence JvmFormalParameter jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(this.grammarKeywordAccess.getOccurrenceKeyword()); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(source.getName())); this.associator.associate(source, jvmParam); bodyOperation.getParameters().add(jvmParam); // Body setBody(bodyOperation, source.getExpression()); // Annotations translateAnnotationsTo(source.getAnnotations(), bodyOperation); appendGeneratedAnnotation(bodyOperation, context); if (!this.services.getExpressionHelper().hasSideEffects(source.getExpression())) { addAnnotationSafe(bodyOperation, Pure.class); } final Collection<Procedure1<ITreeAppendable>> evaluators = context.getGuardEvalationCodeFor(source); assert evaluators != null; if (isTrueGuard) { evaluators.add((it) -> { it.append(RUNNABLE_COLLECTION); it.append(".add(() -> "); //$NON-NLS-1$ it.append(bodyMethodName); it.append("("); //$NON-NLS-1$ it.append(this.grammarKeywordAccess.getOccurrenceKeyword()); it.append("));"); //$NON-NLS-1$ }); } else { assert guard != null; //---------------- // Guard function //---------------- // Name final String guardMethodName = Utils.createNameForHiddenGuardEvaluatorMethod(source.getName().getSimpleName(), context.getBehaviorUnitIndex()); // Operation final JvmOperation guardOperation = this.typesFactory.createJvmOperation(); guardOperation.setAbstract(false); guardOperation.setNative(false); guardOperation.setSynchronized(false); guardOperation.setStrictFloatingPoint(false); guardOperation.setFinal(false); guardOperation.setVisibility(JvmVisibility.PRIVATE); guardOperation.setStatic(false); guardOperation.setSimpleName(guardMethodName); guardOperation.setReturnType(this._typeReferenceBuilder.typeRef(Boolean.TYPE)); // Add to container container.getMembers().add(guardOperation); this.associator.associate(source, guardOperation); this.associator.associatePrimary(guard, guardOperation); // First parameter: it jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(this.grammarKeywordAccess.getItKeyword()); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(source.getName())); this.associator.associate(source, jvmParam); guardOperation.getParameters().add(jvmParam); // Second parameter: occurrence jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(this.grammarKeywordAccess.getOccurrenceKeyword()); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(source.getName())); this.associator.associate(source, jvmParam); guardOperation.getParameters().add(jvmParam); // Body setBody(guardOperation, guard); // Annotations appendGeneratedAnnotation(guardOperation, context); addAnnotationSafe(guardOperation, Pure.class); //------------------ // Handler function //------------------ evaluators.add((it) -> { it.append("if ("); //$NON-NLS-1$ it.append(guardMethodName); it.append("("); //$NON-NLS-1$ it.append(this.grammarKeywordAccess.getOccurrenceKeyword()); it.append(", "); //$NON-NLS-1$ it.append(this.grammarKeywordAccess.getOccurrenceKeyword()); it.append(")) {"); //$NON-NLS-1$ it.increaseIndentation(); it.newLine(); it.append(RUNNABLE_COLLECTION); it.append(".add(() -> "); //$NON-NLS-1$ it.append(bodyMethodName); it.append("("); //$NON-NLS-1$ it.append(this.grammarKeywordAccess.getOccurrenceKeyword()); it.append("));"); //$NON-NLS-1$ it.decreaseIndentation(); it.newLine(); it.append("}"); //$NON-NLS-1$ }); } context.setBehaviorUnitIndex(context.getBehaviorUnitIndex() + 1); context.incrementSerial(bodyMethodName.hashCode()); } else { logInternalError(Messages.SARLJvmModelInferrer_10); } } /** Transform the uses of SARL capacities. * * <p>Resolving the calls to the capacities' functions is done in {@link SARLReentrantTypeResolver}. * * @param source the feature to transform. * @param container the target container of the transformation result. */ protected void transform(SarlCapacityUses source, JvmGenericType container) { final GenerationContext context = getContext(container); if (context == null) { return; } for (final JvmTypeReference capacityType : source.getCapacities()) { final JvmType type = capacityType.getType(); if (type instanceof JvmGenericType /*&& this.inheritanceHelper.isSubTypeOf(capacityType, Capacity.class, SarlCapacity.class)*/ && !context.getGeneratedCapacityUseFields().contains(capacityType.getIdentifier())) { // Generate the buffer field final String fieldName = Utils.createNameForHiddenCapacityImplementationAttribute(capacityType.getIdentifier()); final JvmField field = this.typesFactory.createJvmField(); field.setVisibility(JvmVisibility.PRIVATE); field.setSimpleName(fieldName); field.setTransient(true); final JvmType clearableReferenceType = this.typeReferences.findDeclaredType(ClearableReference.class, container); final JvmTypeReference skillClearableReference = this.typeReferences.createTypeRef( clearableReferenceType, this.typeReferences.createTypeRef(this.typeReferences.findDeclaredType(Skill.class, container))); field.setType(skillClearableReference); this.associator.associatePrimary(source, field); addAnnotationSafe(field, Extension.class); field.getAnnotations().add(annotationClassRef(ImportedCapacityFeature.class, Collections.singletonList(capacityType))); appendGeneratedAnnotation(field, getContext(container)); container.getMembers().add(field); // Generate the calling function final String methodName = Utils.createNameForHiddenCapacityImplementationCallingMethodFromFieldName( fieldName); final JvmOperation operation = this.typesFactory.createJvmOperation(); operation.setVisibility(JvmVisibility.PRIVATE); operation.setReturnType(cloneWithTypeParametersAndProxies(capacityType, operation)); operation.setSimpleName(methodName); this.associator.associatePrimary(source, operation); setBody(operation, (it) -> { it.append("if (this.").append(fieldName).append(" == null || this."); //$NON-NLS-1$ //$NON-NLS-2$ it.append(fieldName).append(".get() == null) {"); //$NON-NLS-1$ it.increaseIndentation(); it.newLine(); it.append("this.").append(fieldName).append(" = ") //$NON-NLS-1$ //$NON-NLS-2$ .append(Utils.HIDDEN_MEMBER_CHARACTER).append("getSkill("); //$NON-NLS-1$ it.append(capacityType.getType()).append(".class);"); //$NON-NLS-1$ it.decreaseIndentation(); it.newLine(); it.append("}"); //$NON-NLS-1$ it.newLine(); it.append("return ").append(Utils.HIDDEN_MEMBER_CHARACTER) //$NON-NLS-1$ .append("castSkill(").append(capacityType.getType()).append(".class, this.") //$NON-NLS-1$ //$NON-NLS-2$ .append(fieldName).append(");"); //$NON-NLS-1$ }); // Add the annotation dedicated to this particular method if (context.isAtLeastJava8()) { context.getPostFinalizationElements().add(() -> { final String inlineExpression = Utils.HIDDEN_MEMBER_CHARACTER + "castSkill(" + capacityType.getSimpleName() //$NON-NLS-1$ + ".class, ($0" + fieldName //$NON-NLS-1$ + " == null || $0" + fieldName //$NON-NLS-1$ + ".get() == null) ? ($0" + fieldName //$NON-NLS-1$ + " = $0" + Utils.HIDDEN_MEMBER_CHARACTER + "getSkill(" //$NON-NLS-1$ //$NON-NLS-2$ + capacityType.getSimpleName() + ".class)) : $0" + fieldName + ")"; //$NON-NLS-1$ //$NON-NLS-2$; this.inlineExpressionCompiler.appendInlineAnnotation( operation, source.eResource().getResourceSet(), inlineExpression, capacityType); }); } appendGeneratedAnnotation(operation, context); addAnnotationSafe(operation, Pure.class); container.getMembers().add(operation); context.addGeneratedCapacityUseField(capacityType.getIdentifier()); context.incrementSerial(capacityType.getIdentifier().hashCode()); } } } /** Transform the requirements of SARL capacities. * * @param source the feature to transform. * @param container the target container of the transformation result. */ protected void transform(SarlRequiredCapacity source, JvmGenericType container) { // } /** Generate the code for the given SARL members in a agent-oriented container. * * @param featureContainerType - the feature container. * @param container - the SARL container. * @param context - description of the generation context in which the members must be considered. */ protected void appendAOPMembers( JvmGenericType featureContainerType, XtendTypeDeclaration container, GenerationContext context) { Utils.populateInheritanceContext( featureContainerType, context.getInheritedFinalOperations(), context.getInheritedOverridableOperations(), null, context.getInheritedOperationsToImplement(), null, this.sarlSignatureProvider); for (final XtendMember feature : container.getMembers()) { if (context.isSupportedMember(feature) && (!(feature instanceof SarlCapacityUses)) && (!(feature instanceof SarlRequiredCapacity))) { transform(feature, featureContainerType, true); } } for (final XtendMember feature : container.getMembers()) { if (context.isSupportedMember(feature) && ((feature instanceof SarlCapacityUses) || (feature instanceof SarlRequiredCapacity))) { transform(feature, featureContainerType, false); } } // Add event handlers appendEventGuardEvaluators(featureContainerType); // Add dispatch methods appendSyntheticDispatchMethods(container, featureContainerType); // Add SARL synthetic functions appendSyntheticDefaultValuedParameterMethods( container, featureContainerType, context); } @Override protected JvmOperation deriveGenericDispatchOperationSignature( Iterable<JvmOperation> localOperations, JvmGenericType target) { final JvmOperation dispatcher = super.deriveGenericDispatchOperationSignature(localOperations, target); // // Fixing the behavior for determining the visibility of the dispatcher since // it does not fit the SARL requirements. // FIXME: Move to Xtend? // JvmVisibility higherVisibility = JvmVisibility.PRIVATE; for (final JvmOperation jvmOperation : localOperations) { final Iterable<XtendFunction> xtendFunctions = Iterables.filter( this.sarlAssociations.getSourceElements(jvmOperation), XtendFunction.class); for (final XtendFunction func : xtendFunctions) { final JvmVisibility visibility = func.getVisibility(); assert visibility != null; if (this.visibilityComparator.compare(visibility, higherVisibility) > 0) { higherVisibility = visibility; } } } dispatcher.setVisibility(higherVisibility); return dispatcher; } /** Generate the missed operations that are the results from the generation of actions with default value parameters. * * @param source - the SARL container. * @param target - the JVM feature container. * @param context - description of the generation context in which the members must be considered. */ protected void appendSyntheticDefaultValuedParameterMethods( XtendTypeDeclaration source, JvmDeclaredType target, GenerationContext context) { // Generate the different operations. final Iterator<Runnable> differedGeneration = context.getPreFinalizationElements().iterator(); while (differedGeneration.hasNext()) { final Runnable runnable = differedGeneration.next(); differedGeneration.remove(); runnable.run(); } // Generated the missed functions that are the result of the generation of operations // with default values. int actIndex = context.getActionIndex(); for (final Entry<ActionPrototype, JvmOperation> missedOperation : context.getInheritedOperationsToImplement() .entrySet()) { final String originalSignature = this.annotationUtils.findStringValue( missedOperation.getValue(), DefaultValueUse.class); if (!Strings.isNullOrEmpty(originalSignature)) { // Find the definition of the operation from the inheritance context. final JvmOperation redefinedOperation = context.getInheritedOverridableOperations().get( this.sarlSignatureProvider.createActionPrototype( missedOperation.getKey().getActionName(), this.sarlSignatureProvider.createParameterTypesFromString(originalSignature))); if (redefinedOperation != null) { final ActionParameterTypes parameterTypes = this.sarlSignatureProvider.createParameterTypesFromJvmModel( redefinedOperation.isVarArgs(), redefinedOperation.getParameters()); final QualifiedActionName qualifiedActionName = this.sarlSignatureProvider.createQualifiedActionName( missedOperation.getValue().getDeclaringType(), redefinedOperation.getSimpleName()); // Retreive the inferred prototype (including the prototypes with optional arguments) InferredPrototype redefinedPrototype = this.sarlSignatureProvider.getPrototypes( qualifiedActionName, parameterTypes); if (redefinedPrototype == null) { // The original operation was not parsed by the SARL compiler in the current run-time context. redefinedPrototype = this.sarlSignatureProvider.createPrototypeFromJvmModel( qualifiedActionName, redefinedOperation.isVarArgs(), redefinedOperation.getParameters()); } // Retreive the specification of the formal parameters that will be used for // determining the calling arguments. final List<InferredStandardParameter> argumentSpec = redefinedPrototype.getInferredParameterTypes().get( missedOperation.getKey().getParametersTypes()); // Create the missed java operation. final JvmOperation op = this.typeBuilder.toMethod( source, missedOperation.getValue().getSimpleName(), missedOperation.getValue().getReturnType(), null); op.setVarArgs(missedOperation.getValue().isVarArgs()); op.setFinal(true); final List<String> arguments = new ArrayList<>(); // Create the formal parameters. for (final InferredStandardParameter parameter : argumentSpec) { if (parameter instanceof InferredValuedParameter) { final InferredValuedParameter inferredParameter = (InferredValuedParameter) parameter; arguments.add(this.sarlSignatureProvider.toJavaArgument( target.getIdentifier(), inferredParameter.getCallingArgument())); } else { arguments.add(parameter.getName()); final JvmFormalParameter jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(parameter.getName()); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(parameter.getType())); this.associator.associate(parameter.getParameter(), jvmParam); op.getParameters().add(jvmParam); } } // Create the body setBody(op, (it) -> { it.append(redefinedOperation.getSimpleName()); it.append("("); //$NON-NLS-1$ it.append(IterableExtensions.join(arguments, ", ")); //$NON-NLS-1$ it.append(");"); //$NON-NLS-1$ }); // Add the annotations. addAnnotationSafe(op, DefaultValueUse.class, originalSignature); appendGeneratedAnnotation(op, context); // Add the operation in the container. target.getMembers().add(op); ++actIndex; } } } context.setActionIndex(actIndex); } /** Create a string concatenation client from a set of Java code lines. * * @param javaCodeLines - the Java code lines. * @return the client. */ private static StringConcatenationClient toStringConcatenation(final String... javaCodeLines) { return new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation builder) { for (final String line : javaCodeLines) { builder.append(line); builder.newLineIfNotEmpty(); } } }; } /** Generate the extended types for the given SARL statement. * * @param context - the context of the generation. * @param owner - the JVM element to change. * @param defaultJvmType - the default JVM type. * @param defaultSarlType - the default SARL type. * @param supertype - the supertype. */ protected void appendConstrainedExtends( GenerationContext context, JvmGenericType owner, Class<?> defaultJvmType, Class<? extends XtendTypeDeclaration> defaultSarlType, JvmParameterizedTypeReference supertype) { final List<? extends JvmParameterizedTypeReference> supertypes; if (supertype == null) { supertypes = Collections.emptyList(); } else { supertypes = Collections.singletonList(supertype); } appendConstrainedExtends(context, owner, defaultJvmType, defaultSarlType, supertypes); } /** Generate the extended types for the given SARL statement. * * @param context - the context of the generation. * @param owner - the JVM element to change. * @param defaultJvmType - the default JVM type. * @param defaultSarlType - the default Sarl type. * @param supertypes - the supertypes. */ protected void appendConstrainedExtends( GenerationContext context, JvmGenericType owner, Class<?> defaultJvmType, Class<? extends XtendTypeDeclaration> defaultSarlType, List<? extends JvmParameterizedTypeReference> supertypes) { boolean explicitType = false; final String ownerId = owner.getIdentifier(); for (final JvmParameterizedTypeReference superType : supertypes) { String superTypeId; try { superTypeId = superType.getIdentifier(); } catch (Exception ex) { logInternalError(ex); superTypeId = null; } if (!Objects.equal(ownerId, superTypeId) && superType.getType() instanceof JvmGenericType /*&& this.inheritanceHelper.isProxyOrSubTypeOf(superType, defaultJvmType, defaultSarlType, isInterface)*/) { owner.getSuperTypes().add(this.typeBuilder.cloneWithProxies(superType)); context.incrementSerial(superType.getIdentifier().hashCode()); explicitType = true; } } if (!explicitType) { final JvmTypeReference type = this._typeReferenceBuilder.typeRef(defaultJvmType); owner.getSuperTypes().add(type); context.incrementSerial(type.getIdentifier().hashCode()); } } /** Generate the implemented types for the given SARL statement. * * @param context - the context of the generation. * @param owner - the JVM element to change. * @param defaultJvmType - the default JVM type. * @param defaultSarlType - the default SARL type. * @param implementedtypes - the implemented types. */ protected void appendConstrainedImplements( GenerationContext context, JvmGenericType owner, Class<?> defaultJvmType, Class<? extends XtendTypeDeclaration> defaultSarlType, List<? extends JvmParameterizedTypeReference> implementedtypes) { boolean explicitType = false; for (final JvmParameterizedTypeReference superType : implementedtypes) { if (!Objects.equal(owner.getIdentifier(), superType.getIdentifier()) && superType.getType() instanceof JvmGenericType /*&& this.inheritanceHelper.isProxyOrSubTypeOf(superType, defaultJvmType, defaultSarlType, true)*/) { owner.getSuperTypes().add(this.typeBuilder.cloneWithProxies(superType)); context.incrementSerial(superType.getIdentifier().hashCode()); explicitType = true; } } if (!explicitType) { final JvmTypeReference type = this._typeReferenceBuilder.typeRef(defaultJvmType); owner.getSuperTypes().add(type); context.incrementSerial(type.getIdentifier().hashCode()); } } /** Add the @Generated annotation to the given target. * The annotation will not have any generated SARL code associated to it. * * @param target - the target of the annotation. * @param context the generation context. */ protected final void appendGeneratedAnnotation(JvmAnnotationTarget target, GenerationContext context) { appendGeneratedAnnotation(target, context, null); } /** Add the @Generated annotation to the given target. * * @param target - the target of the annotation. * @param context the generation context. * @param sarlCode - the code that is the cause of the generation. */ protected void appendGeneratedAnnotation(JvmAnnotationTarget target, GenerationContext context, String sarlCode) { final GeneratorConfig config = context.getGeneratorConfig(); if (config.isGenerateGeneratedAnnotation()) { addAnnotationSafe(target, Generated.class, getClass().getName()); } if (target instanceof JvmFeature) { addAnnotationSafe(target, SyntheticMember.class); } if (!Strings.isNullOrEmpty(sarlCode)) { addAnnotationSafe(target, SarlSourceCode.class, sarlCode); } } /** Append the guard evaluators. * * @param container the container type. */ protected void appendEventGuardEvaluators(JvmGenericType container) { final GenerationContext context = getContext(container); if (context != null) { final Collection<Pair<SarlBehaviorUnit, Collection<Procedure1<ITreeAppendable>>>> allEvaluators = context.getGuardEvaluationCodes(); if (allEvaluators == null || allEvaluators.isEmpty()) { return; } final JvmTypeReference voidType = this._typeReferenceBuilder.typeRef(Void.TYPE); final JvmTypeReference runnableType = this._typeReferenceBuilder.typeRef(Runnable.class); final JvmTypeReference collectionType = this._typeReferenceBuilder.typeRef(Collection.class, runnableType); for (final Pair<SarlBehaviorUnit, Collection<Procedure1<ITreeAppendable>>> evaluators : allEvaluators) { final SarlBehaviorUnit source = evaluators.getKey(); // Determine the name of the operation for the behavior output final String behName = Utils.createNameForHiddenGuardGeneralEvaluatorMethod(source.getName().getSimpleName()); // Create the main function final JvmOperation operation = this.typesFactory.createJvmOperation(); // Annotation for the event bus appendGeneratedAnnotation(operation, context); addAnnotationSafe(operation, PerceptGuardEvaluator.class); // Guard evaluator unit parameters // - Event occurrence JvmFormalParameter jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(this.grammarKeywordAccess.getOccurrenceKeyword()); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(source.getName())); this.associator.associate(source, jvmParam); operation.getParameters().add(jvmParam); // - List of runnables jvmParam = this.typesFactory.createJvmFormalParameter(); jvmParam.setName(RUNNABLE_COLLECTION); jvmParam.setParameterType(this.typeBuilder.cloneWithProxies(collectionType)); operation.getParameters().add(jvmParam); operation.setAbstract(false); operation.setNative(false); operation.setSynchronized(false); operation.setStrictFloatingPoint(false); operation.setFinal(false); operation.setVisibility(JvmVisibility.PRIVATE); operation.setStatic(false); operation.setSimpleName(behName); operation.setReturnType(this.typeBuilder.cloneWithProxies(voidType)); container.getMembers().add(operation); setBody(operation, (it) -> { it.append("assert "); //$NON-NLS-1$ it.append(this.grammarKeywordAccess.getOccurrenceKeyword()); it.append(" != null;"); //$NON-NLS-1$ it.newLine(); it.append("assert "); //$NON-NLS-1$ it.append(RUNNABLE_COLLECTION); it.append(" != null;"); //$NON-NLS-1$ for (final Procedure1<ITreeAppendable> code : evaluators.getValue()) { it.newLine(); code.apply(it); } }); this.associator.associatePrimary(source, operation); this.typeBuilder.copyDocumentationTo(source, operation); } } } /** Append the @FunctionalInterface to the given type if it is a functional interface according * to the Java 8 specification definition. * * @param type the type to update. */ protected void appendFunctionalInterfaceAnnotation(JvmGenericType type) { if (type != null && Utils.isFunctionalInterface(type, this.sarlSignatureProvider) && this.annotationFinder.findAnnotation(type, FunctionalInterface.class) == null) { addAnnotationSafe(type, FunctionalInterface.class); } } /** Create the functions that permits to compare the object. * The comparaison functions are {@link #equals(Object)} and {@link #hashCode()}. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. */ @SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity", "checkstyle:nestedifdepth"}) protected void appendComparisonFunctions(GenerationContext context, XtendTypeDeclaration source, JvmGenericType target) { boolean isEqualsUserDefined = false; boolean isHashCodeUserDefined = false; for (final JvmOperation operation : target.getDeclaredOperations()) { if (Objects.equal(EQUALS_FUNCTION_NAME, operation.getSimpleName()) && operation.getParameters().size() == 1 && Objects.equal(Object.class.getName(), operation.getParameters().get(0).getParameterType().getIdentifier())) { isEqualsUserDefined = true; } else if (Objects.equal(HASHCODE_FUNCTION_NAME, operation.getSimpleName()) && operation.getParameters().isEmpty()) { isHashCodeUserDefined = true; } } if (!isEqualsUserDefined || !isHashCodeUserDefined) { // Create a list of the declared non-static fields. final List<JvmField> declaredInstanceFields = new ArrayList<>(); for (final JvmField field : target.getDeclaredFields()) { if (isEqualityTestValidField(field)) { declaredInstanceFields.add(field); } } if (!declaredInstanceFields.isEmpty()) { final Map<ActionPrototype, JvmOperation> finalOperations = new TreeMap<>(); Utils.populateInheritanceContext( target, finalOperations, null, null, null, null, this.sarlSignatureProvider); boolean couldCreateEqualsFunction = true; if (!isEqualsUserDefined) { final ActionPrototype prototype = new ActionPrototype(EQUALS_FUNCTION_NAME, this.sarlSignatureProvider.createParameterTypesFromString(Object.class.getName())); couldCreateEqualsFunction = !finalOperations.containsKey(prototype); } boolean couldCreateHashCodeFunction = true; if (!isHashCodeUserDefined) { final ActionPrototype prototype = new ActionPrototype(HASHCODE_FUNCTION_NAME, this.sarlSignatureProvider.createParameterTypesForVoid()); couldCreateHashCodeFunction = !finalOperations.containsKey(prototype); } if (couldCreateEqualsFunction && couldCreateHashCodeFunction) { if (!isEqualsUserDefined) { final JvmOperation op = toEqualsMethod(source, target, declaredInstanceFields); if (op != null) { appendGeneratedAnnotation(op, context); target.getMembers().add(op); } } if (!isHashCodeUserDefined) { final JvmOperation op = toHashCodeMethod(source, declaredInstanceFields); if (op != null) { appendGeneratedAnnotation(op, context); target.getMembers().add(op); } } } } } } /** Create the functions that are related to the <code>toString</code> function. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. */ protected void appendToStringFunctions(GenerationContext context, XtendTypeDeclaration source, final JvmGenericType target) { // Create a list of the declared non-static fields. final List<JvmField> declaredInstanceFields = new ArrayList<>(); for (final JvmField field : target.getDeclaredFields()) { if (!field.isStatic()) { declaredInstanceFields.add(field); } } if (!declaredInstanceFields.isEmpty()) { final JvmOperation op = SARLJvmModelInferrer.this.typeBuilder.toMethod( source, "attributesToString", //$NON-NLS-1$ SARLJvmModelInferrer.this._typeReferenceBuilder.typeRef(String.class), (it2) -> { it2.setVisibility(JvmVisibility.PROTECTED); SARLJvmModelInferrer.this.typeBuilder.setDocumentation(it2, MessageFormat.format(Messages.SARLJvmModelInferrer_2, target.getSimpleName())); setBody(it2, (it3) -> { it3.append("StringBuilder result = new StringBuilder(" //$NON-NLS-1$ + "super.attributesToString());").newLine(); //$NON-NLS-1$ for (final JvmField attr : declaredInstanceFields) { it3.append("result.append(\"" + attr.getSimpleName() //$NON-NLS-1$ + " = \").append(this." //$NON-NLS-1$ + attr.getSimpleName() + ");").newLine(); //$NON-NLS-1$ } it3.append("return result.toString();"); //$NON-NLS-1$ }); }); if (op != null) { appendGeneratedAnnotation(op, context); addAnnotationSafe(op, Pure.class); target.getMembers().add(op); } } } /** Append the serial number field. * * <p>The serial number field is computed from the given context and from the generated fields. * The field is added if no field with name "serialVersionUID" was defined. * * <p>This function does not test if the field container is serializable. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. * @see #appendSerialNumberIfSerializable(GenerationContext, XtendTypeDeclaration, JvmGenericType) */ protected void appendSerialNumber(GenerationContext context, XtendTypeDeclaration source, JvmGenericType target) { for (final JvmField field : target.getDeclaredFields()) { if (SERIAL_FIELD_NAME.equals(field.getSimpleName())) { return; } } final JvmField field = this.typesFactory.createJvmField(); field.setSimpleName(SERIAL_FIELD_NAME); field.setVisibility(JvmVisibility.PRIVATE); field.setStatic(true); field.setTransient(false); field.setVolatile(false); field.setFinal(true); target.getMembers().add(field); field.setType(this.typeBuilder.cloneWithProxies(this._typeReferenceBuilder.typeRef(long.class))); final long serial = context.getSerial(); this.typeBuilder.setInitializer(field, toStringConcatenation(serial + "L")); //$NON-NLS-1$ appendGeneratedAnnotation(field, context); this.readAndWriteTracking.markInitialized(field, null); } /** Append the serial number field if and only if the container type is serializable. * * <p>The serial number field is computed from the given context and from the generated fields. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. * @see #appendSerialNumber(GenerationContext, XtendTypeDeclaration, JvmGenericType) */ protected void appendSerialNumberIfSerializable(GenerationContext context, XtendTypeDeclaration source, JvmGenericType target) { if (!target.isInterface() && this.inheritanceHelper.isSubTypeOf(target, Serializable.class, null)) { appendSerialNumber(context, source, target); } } /** Append the clone function. * * <p>The clone function replies a value of the current type, not {@code Object}. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. * @since 0.6 * @see #appendCloneFunctionIfCloneable(GenerationContext, XtendTypeDeclaration, JvmGenericType) */ protected void appendCloneFunction(GenerationContext context, XtendTypeDeclaration source, JvmGenericType target) { for (final JvmOperation operation : target.getDeclaredOperations()) { if (CLONE_FUNCTION_NAME.equals(operation.getSimpleName())) { return; } } final ActionPrototype standardPrototype = new ActionPrototype(CLONE_FUNCTION_NAME, this.sarlSignatureProvider.createParameterTypesForVoid()); final Map<ActionPrototype, JvmOperation> finalOperations = new TreeMap<>(); Utils.populateInheritanceContext( target, finalOperations, null, null, null, null, this.sarlSignatureProvider); if (!finalOperations.containsKey(standardPrototype)) { final JvmTypeReference[] genericParameters = new JvmTypeReference[target.getTypeParameters().size()]; for (int i = 0; i < target.getTypeParameters().size(); ++i) { final JvmTypeParameter typeParameter = target.getTypeParameters().get(i); genericParameters[i] = this._typeReferenceBuilder.typeRef(typeParameter); } final JvmTypeReference myselfReference = this._typeReferenceBuilder.typeRef(target, genericParameters); final JvmOperation operation = this.typeBuilder.toMethod( source, CLONE_FUNCTION_NAME, myselfReference, null); target.getMembers().add(operation); operation.setVisibility(JvmVisibility.PUBLIC); addAnnotationSafe(operation, Override.class); addAnnotationSafe(operation, Pure.class); final LightweightTypeReference myselfReference2 = Utils.toLightweightTypeReference( operation.getReturnType(), this.services); setBody(operation, (it) -> { it.append("try {"); //$NON-NLS-1$ it.increaseIndentation().newLine(); it.append("return (").append(myselfReference2).append(") super."); //$NON-NLS-1$//$NON-NLS-2$ it.append(CLONE_FUNCTION_NAME).append("();"); //$NON-NLS-1$ it.decreaseIndentation().newLine(); it.append("} catch (").append(Throwable.class).append(" exception) {"); //$NON-NLS-1$ //$NON-NLS-2$ it.increaseIndentation().newLine(); it.append("throw new ").append(Error.class).append("(exception);"); //$NON-NLS-1$ //$NON-NLS-2$ it.decreaseIndentation().newLine(); it.append("}"); //$NON-NLS-1$ }); appendGeneratedAnnotation(operation, context); } } /** Append the clone function only if the type is a subtype of {@link Cloneable}. * * <p>The clone function replies a value of the current type, not {@code Object}. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. * @since 0.6 * @see #appendCloneFunction(GenerationContext, XtendTypeDeclaration, JvmGenericType) */ protected void appendCloneFunctionIfCloneable(GenerationContext context, XtendTypeDeclaration source, JvmGenericType target) { if (!target.isInterface() && this.inheritanceHelper.isSubTypeOf(target, Cloneable.class, null)) { appendCloneFunction(context, source, target); } } /** Append the SARL specification version as an annotation to the given container. * * <p>The added annotation may be used by any underground platform for determining what is * the version of the SARL specification that was used for generating the container. * * @param context the current generation context. * @param source the source object. * @param target the inferred JVM object. */ protected void appendSARLSpecificationVersion(GenerationContext context, XtendTypeDeclaration source, JvmDeclaredType target) { addAnnotationSafe(target, SarlSpecification.class, SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING); } /** Append the SARL element type as an annotation to the given container. * * <p>The added annotation may be used by any underground platform for determining what is * the type of the SARL element without invoking the costly "instanceof" operations. * * @param source the source object. * @param target the inferred JVM object. */ protected void appendSARLElementType(XtendTypeDeclaration source, JvmDeclaredType target) { addAnnotationSafe(target, SarlElementType.class, source.eClass().getClassifierID()); } /** Remove the type parameters from the given type. * * @param type the type. * @return the same type without the type parameters. */ protected JvmTypeReference skipTypeParameters(JvmTypeReference type) { return this._typeReferenceBuilder.typeRef(type.getType()); } /** Generate a list of formal parameters with annotations for the default values. * * @param context - the generation context. * @param owner - the JVM element to change. * @param actionContainer - the container of the action. * @param varargs - indicates if the signature has variadic parameter. * @param params - the parameters. * @param isForInterface - indicates if the formal parameters are for an interface (<code>true</code>) * or a class (<code>false</code>). * @param paramSpec - the specification of the parameter as computed by a {@link IActionPrototypeProvider}. */ protected void translateSarlFormalParameters( GenerationContext context, JvmExecutable owner, JvmGenericType actionContainer, boolean varargs, List<? extends XtendParameter> params, final boolean isForInterface, List<InferredStandardParameter> paramSpec) { boolean hasDefaultValue = false; for (int i = 0; i < params.size(); ++i) { final XtendParameter param = params.get(i); assert param != null; final String paramName = param.getName(); final JvmTypeReference paramType = param.getParameterType(); if (!Strings.isNullOrEmpty(paramName) && paramType != null) { // "Standard" (Xtend) translation of the parameter translateParameter(owner, param); final JvmFormalParameter lastParam = owner.getParameters().get(owner.getParameters().size() - 1); // Treat the default value if (i < paramSpec.size() && param instanceof SarlFormalParameter && ((SarlFormalParameter) param).getDefaultValue() != null) { final XExpression defaultValue = ((SarlFormalParameter) param).getDefaultValue(); assert defaultValue != null; hasDefaultValue = true; final InferredStandardParameter inferredParam = paramSpec.get(i); assert inferredParam != null; final String namePostPart = inferredParam.getDefaultValueAnnotationValue(); final String name = this.sarlSignatureProvider.createFieldNameForDefaultValueID(namePostPart); // FIXME: Hide these attributes into an inner interface. final JvmField field = this.typeBuilder.toField(defaultValue, name, skipTypeParameters(paramType), (it) -> { SARLJvmModelInferrer.this.typeBuilder.setDocumentation(it, MessageFormat.format(Messages.SARLJvmModelInferrer_11, paramName)); it.setStatic(true); it.setFinal(true); if (isForInterface) { it.setVisibility(JvmVisibility.PUBLIC); } else { it.setVisibility(JvmVisibility.PRIVATE); } SARLJvmModelInferrer.this.typeBuilder.setInitializer(it, defaultValue); }); actionContainer.getMembers().add(field); if (owner instanceof JvmConstructor) { this.readAndWriteTracking.markInitialized(field, (JvmConstructor) owner); } else { this.readAndWriteTracking.markInitialized(field, null); } addAnnotationSafe(lastParam, DefaultValue.class, namePostPart); final String rawCode = reentrantSerialize(defaultValue); appendGeneratedAnnotation(field, context, rawCode); } } } if (hasDefaultValue) { addAnnotationSafe(owner, DefaultValueSource.class); } } /** Generate a list of formal parameters with annotations for the default values. * * @param owner - the JVM element to change. * @param actionContainer - the container of the action. * @param varargs - indicates if the signature has variadic parameter. * @param signature - the description of the parameters. * @return the arguments to pass to the original function. */ protected List<String> translateSarlFormalParametersForSyntheticOperation(JvmExecutable owner, JvmGenericType actionContainer, boolean varargs, List<InferredStandardParameter> signature) { final List<String> arguments = CollectionLiterals.newArrayList(); for (final InferredStandardParameter parameterSpec : signature) { if (parameterSpec instanceof InferredValuedParameter) { arguments.add( this.sarlSignatureProvider.toJavaArgument( actionContainer.getIdentifier(), ((InferredValuedParameter) parameterSpec).getCallingArgument())); } else { final EObject param = parameterSpec.getParameter(); final String paramName = parameterSpec.getName(); final JvmTypeReference paramType = parameterSpec.getType(); if (!Strings.isNullOrEmpty(paramName) && paramType != null) { final JvmFormalParameter lastParam = this.typesFactory.createJvmFormalParameter(); owner.getParameters().add(lastParam); lastParam.setName(paramName); lastParam.setParameterType(this.typeBuilder.cloneWithProxies(paramType)); this.associator.associate(param, lastParam); arguments.add(paramName); } } } return arguments; } /** Copy the annotations, except the ones given as parameters. * * @param annotations the annotations to copy. * @param target the target. * @param exceptions the annotations to skip. */ @SuppressWarnings("static-method") protected void translateAnnotationsTo(List<JvmAnnotationReference> annotations, JvmAnnotationTarget target, Class<?>... exceptions) { final Set<String> excepts = new HashSet<>(); for (final Class<?> type : exceptions) { excepts.add(type.getName()); } final List<JvmAnnotationReference> addition = new ArrayList<>(); for (final JvmAnnotationReference annotation : Iterables.filter(annotations, (an) -> { if (!ANNOTATION_TRANSLATION_FILTER.apply(an)) { return false; } return !excepts.contains(an.getAnnotation().getIdentifier()); })) { addition.add(annotation); } target.getAnnotations().addAll(addition); } /** Replies the type parameters for the given type. * * @param type the type. * @return the type parameters for the given type. */ @SuppressWarnings("static-method") protected List<JvmTypeParameter> getTypeParametersFor(XtendTypeDeclaration type) { if (type instanceof XtendClass) { return ((XtendClass) type).getTypeParameters(); } if (type instanceof XtendInterface) { return ((XtendInterface) type).getTypeParameters(); } return Collections.emptyList(); } @SuppressWarnings("static-method") private boolean isEqualityTestValidField(JvmField field) { return !field.isStatic() && !Utils.isHiddenMember(field.getSimpleName()); } @SuppressWarnings({"checkstyle:booleanexpressioncomplexity", "checkstyle:cyclomaticcomplexity"}) private boolean isEqualityTestValidField(JvmTypeReference reference) { for (final Class<?> type : EQUALITY_TEST_TYPES) { if (this.typeReferences.is(reference, type)) { return true; } } return false; } /** Generate the "equals()" operation. * This function was deprecated in Xbase, and should be provided by DSL * providers now. * * @param sarlElement - the SARL element for which the "equals function must be generated. * @param declaredType - the declating type. * @param jvmFields - the fields declared in the container. * @return the "equals" function. */ private JvmOperation toEqualsMethod( XtendTypeDeclaration sarlElement, final JvmDeclaredType declaredType, final Iterable<JvmField> jvmFields) { if (sarlElement == null || declaredType == null) { return null; } final JvmOperation result = this.typeBuilder.toMethod(sarlElement, EQUALS_FUNCTION_NAME, this._typeReferenceBuilder.typeRef(Boolean.TYPE), null); if (result == null) { return null; } addAnnotationSafe(result, Override.class); addAnnotationSafe(result, Pure.class); final JvmFormalParameter param = this.typesFactory.createJvmFormalParameter(); param.setName("obj"); //$NON-NLS-1$ param.setParameterType(this._typeReferenceBuilder.typeRef(Object.class)); this.associator.associate(sarlElement, param); result.getParameters().add(param); setBody(result, new Procedures.Procedure1<ITreeAppendable>() { @SuppressWarnings("synthetic-access") @Override public void apply(ITreeAppendable it) { boolean firstAttr = true; for (final JvmField field : jvmFields) { if (isEqualityTestValidField(field.getType())) { if (firstAttr) { firstAttr = false; it.append("if (this == obj)").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return true;").decreaseIndentation(); //$NON-NLS-1$ it.newLine().append("if (obj == null)").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ it.newLine().append("if (getClass() != obj.getClass())").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ final StringBuilder currentTypeName = new StringBuilder(); currentTypeName.append(declaredType.getSimpleName()); final List<JvmTypeParameter> typeParameters = getTypeParametersFor(sarlElement); if (!typeParameters.isEmpty()) { currentTypeName.append("<"); //$NON-NLS-1$ boolean first = true; for (final JvmTypeParameter typeParameter : typeParameters) { if (first) { first = false; } else { currentTypeName.append(", "); //$NON-NLS-1$ } currentTypeName.append(typeParameter.getName()); } currentTypeName.append(">"); //$NON-NLS-1$ } it.newLine().append(currentTypeName).append(" other = ("); //$NON-NLS-1$ it.append(currentTypeName).append(") obj;").newLine(); //$NON-NLS-1$ } generateToEqualForField(it, field); } } it.append("return super.").append(EQUALS_FUNCTION_NAME); //$NON-NLS-1$ it.append("(obj);"); //$NON-NLS-1$ } @SuppressWarnings({"checkstyle:booleanexpressioncomplexity", "checkstyle:cyclomaticcomplexity", "synthetic-access"}) private void generateToEqualForField(ITreeAppendable it, JvmField field) { final TypeReferences refs = SARLJvmModelInferrer.this.typeReferences; final JvmTypeReference type = field.getType(); if (refs.is(type, Boolean.TYPE) || refs.is(type, Boolean.class) || refs.is(type, Integer.TYPE) || refs.is(type, Integer.class) || refs.is(type, Long.TYPE) || refs.is(type, Long.class) || refs.is(type, Character.TYPE) || refs.is(type, Character.class) || refs.is(type, Byte.TYPE) || refs.is(type, Byte.class) || refs.is(type, Short.TYPE) || refs.is(type, Short.class)) { it.append("if (other.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(" != this.").append(field.getSimpleName()).append(")").increaseIndentation(); //$NON-NLS-1$ //$NON-NLS-2$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ } else if (refs.is(type, Double.TYPE) || refs.is(type, Double.class)) { it.append("if (Double.doubleToLongBits(other.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(") != Double.doubleToLongBits(this.").append(field.getSimpleName()); //$NON-NLS-1$ it.append("))").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ } else if (refs.is(type, Float.TYPE) || refs.is(type, Float.class)) { it.append("if (Float.floatToIntBits(other.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(") != Float.floatToIntBits(this.").append(field.getSimpleName()); //$NON-NLS-1$ it.append("))").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ } else { it.append("if (!").append(java.util.Objects.class); //$NON-NLS-1$ it.append(".equals(this.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(", other.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(")) {").increaseIndentation(); //$NON-NLS-1$ it.newLine().append("return false;").decreaseIndentation(); //$NON-NLS-1$ it.newLine().append("}"); //$NON-NLS-1$ } it.newLine(); } }); return result; } /** Generate the "hashCode()" operation. * This function was deprecated in Xbase, and should be provided by DSL * providers now. * * @param sarlElement - the SARL element for which the "hashCode" msut be generated. * @param jvmFields - the fields declared in the container. * @return the "hashCode" function. */ @SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity", "checkstyle:booleanexpressioncomplexity"}) private JvmOperation toHashCodeMethod( XtendTypeDeclaration sarlElement, final Iterable<JvmField> jvmFields) { if (sarlElement == null) { return null; } final JvmOperation result = this.typeBuilder.toMethod(sarlElement, HASHCODE_FUNCTION_NAME, this._typeReferenceBuilder.typeRef(Integer.TYPE), null); if (result == null) { return null; } addAnnotationSafe(result, Override.class); addAnnotationSafe(result, Pure.class); setBody(result, (it) -> { final TypeReferences refs = SARLJvmModelInferrer.this.typeReferences; it.append("int result = super.").append(HASHCODE_FUNCTION_NAME); //$NON-NLS-1$ it.append("();"); //$NON-NLS-1$ boolean firstAttr = true; for (final JvmField field : jvmFields) { if (isEqualityTestValidField(field.getType())) { if (firstAttr) { firstAttr = false; it.newLine().append("final int prime = 31;"); //$NON-NLS-1$ } final JvmTypeReference type = field.getType(); if (refs.is(type, Boolean.TYPE) || refs.is(type, Boolean.class)) { it.newLine().append("result = prime * result + (this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(" ? 1231 : 1237);"); //$NON-NLS-1$ } else if (refs.is(type, Integer.TYPE) || refs.is(type, Integer.class) || refs.is(type, Character.TYPE) || refs.is(type, Character.class) || refs.is(type, Byte.TYPE) || refs.is(type, Byte.class) || refs.is(type, Short.TYPE) || refs.is(type, Short.class)) { it.newLine().append("result = prime * result + this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(";"); //$NON-NLS-1$ } else if (refs.is(type, Long.TYPE) || refs.is(type, Long.class)) { it.newLine().append("result = prime * result + (int) (this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(" ^ (this.").append(field.getSimpleName()); //$NON-NLS-1$ it.append(" >>> 32));"); //$NON-NLS-1$ } else if (refs.is(type, Float.TYPE) || refs.is(type, Float.class)) { it.newLine().append("result = prime * result + Float.floatToIntBits(this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(");"); //$NON-NLS-1$ } else if (refs.is(type, Double.TYPE) || refs.is(type, Double.class)) { it.newLine().append("result = prime * result + (int) (Double.doubleToLongBits(this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(") ^ (Double.doubleToLongBits(this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(") >>> 32));"); //$NON-NLS-1$ } else { it.newLine().append("result = prime * result + "); //$NON-NLS-1$ it.append(java.util.Objects.class).append(".hashCode(this."); //$NON-NLS-1$ it.append(field.getSimpleName()).append(");"); //$NON-NLS-1$ } } } it.newLine().append("return result;"); //$NON-NLS-1$ }); return result; } /** Clone the given type reference that for being link to the given operation. * * <p>The proxies are not resolved, and the type parameters are clone when they are * related to the type parameter of the type container. * * @param type the source type. * @param forOperation the operation that will contain the result type. * @return the result type, i.e. a copy of the source type. */ protected JvmTypeReference cloneWithTypeParametersAndProxies(JvmTypeReference type, JvmOperation forOperation) { // TODO: Is similar function exist in Xtext? if (type == null) { return this._typeReferenceBuilder.typeRef(Object.class); } boolean cloneType = true; JvmTypeReference typeCandidate = type; // Use also cloneType as a flag that indicates if the type was already found in type parameters. if (cloneType && type instanceof JvmParameterizedTypeReference) { // Try to clone the type parameters. final List<JvmTypeParameter> typeParameters = forOperation.getTypeParameters(); if (!typeParameters.isEmpty()) { cloneType = false; typeCandidate = cloneAndAssociate(type, typeParameters); } } // Do the clone according to the type of the entity. assert typeCandidate != null; final JvmTypeReference returnType; if (InferredTypeIndicator.isInferred(typeCandidate) || !cloneType) { returnType = typeCandidate; } else { returnType = this.typeBuilder.cloneWithProxies(typeCandidate); } return returnType; } private JvmTypeReference cloneAndAssociate( final JvmTypeReference type, final List<JvmTypeParameter> typeParameters) { final Map<String, JvmTypeParameter> typeParameterIdentifiers = new TreeMap<>(); for (final JvmTypeParameter typeParameter : typeParameters) { typeParameterIdentifiers.put(typeParameter.getIdentifier(), typeParameter); } final boolean canAssociate = this.languageInfo.isLanguage(type.eResource()); EcoreUtil.Copier copier = new EcoreUtil.Copier(false) { private static final long serialVersionUID = 698510355384773254L; @SuppressWarnings("synthetic-access") @Override protected EObject createCopy(EObject eobject) { final EObject result = super.createCopy(eobject); if (result != null && eobject != null && !eobject.eIsProxy()) { if (canAssociate) { SARLJvmModelInferrer.this.associator.associate(eobject, result); } } return result; } @SuppressWarnings("synthetic-access") @Override public EObject copy(EObject eobject) { final String id; if (eobject instanceof JvmTypeReference) { id = ((JvmTypeReference) eobject).getIdentifier(); } else if (eobject instanceof JvmIdentifiableElement) { id = ((JvmIdentifiableElement) eobject).getIdentifier(); } else { id = null; } if (id != null) { final JvmTypeParameter param = typeParameterIdentifiers.get(id); if (param != null) { return SARLJvmModelInferrer.this.typeReferences.createTypeRef(param); } } final EObject result = super.copy(eobject); if (result instanceof JvmWildcardTypeReference) { final JvmWildcardTypeReference wildcardType = (JvmWildcardTypeReference) result; boolean upperBoundSeen = false; for (final JvmTypeConstraint constraint : wildcardType.getConstraints()) { if (constraint instanceof JvmUpperBound) { upperBoundSeen = true; break; } } if (!upperBoundSeen) { // no upper bound found - seems to be an invalid - assume object as upper bound final JvmTypeReference object = SARLJvmModelInferrer.this._typeReferenceBuilder.typeRef(Object.class); final JvmUpperBound upperBound = SARLJvmModelInferrer.this.typesFactory.createJvmUpperBound(); upperBound.setTypeReference(object); wildcardType.getConstraints().add(0, upperBound); } } return result; } @Override protected void copyReference(EReference ereference, EObject eobject, EObject copyEObject) { super.copyReference(ereference, eobject, copyEObject); } }; final JvmTypeReference copy = (JvmTypeReference) copier.copy(type); copier.copyReferences(); return copy; } /** Copy and clean the given documentation by removing any unecessary <code>@param</code>. * * @param sourceOperation the source for the documentation. * @param targetOperation the target for the documentation. * @return <code>true</code> if a documentation was added. */ protected boolean copyAndCleanDocumentationTo(JvmExecutable sourceOperation, JvmExecutable targetOperation) { assert sourceOperation != null; assert targetOperation != null; String comment = SARLJvmModelInferrer.this.typeBuilder.getDocumentation(sourceOperation); if (Strings.isNullOrEmpty(comment)) { return false; } comment = cleanDocumentation(comment, Iterables.transform(sourceOperation.getParameters(), (it) -> it.getSimpleName()), Iterables.transform(targetOperation.getParameters(), (it) -> it.getSimpleName())); SARLJvmModelInferrer.this.typeBuilder.setDocumentation(targetOperation, comment); return true; } /** Copy and clean the given documentation by removing any unecessary <code>@param</code>. * * @param sourceOperation the source for the documentation. * @param targetOperation the target for the documentation. * @return <code>true</code> if a documentation was added. */ protected boolean copyAndCleanDocumentationTo(XtendExecutable sourceOperation, JvmExecutable targetOperation) { assert sourceOperation != null; assert targetOperation != null; String comment = SARLJvmModelInferrer.this.typeBuilder.getDocumentation(sourceOperation); if (Strings.isNullOrEmpty(comment)) { return false; } comment = cleanDocumentation(comment, Iterables.transform(sourceOperation.getParameters(), (it) -> it.getName()), Iterables.transform(targetOperation.getParameters(), (it) -> it.getSimpleName())); SARLJvmModelInferrer.this.typeBuilder.setDocumentation(targetOperation, comment); return true; } /** Copy and clean the given documentation by removing any unecessary <code>@param</code>. * * @param sourceParameters the parameters of the source. * @param targetParameters the parameters of the target. * @return <code>true</code> if a documentation was added. */ private static String cleanDocumentation(String comment, Iterable<String> sourceParameters, Iterable<String> targetParameters) { String clean = comment; if (!Strings.isNullOrEmpty(clean)) { final Set<String> parameterNames = new TreeSet<>(); if (sourceParameters != null) { for (final String param : sourceParameters) { parameterNames.add(param); } } if (targetParameters != null) { for (final String param : targetParameters) { parameterNames.remove(param); } } for (final String parameterName : parameterNames) { clean = clean.replaceFirst( "\\Q@param\\E\\s+\\Q" + parameterName + "\\E\\s*", //$NON-NLS-1$//$NON-NLS-2$ "@optionalparam " + parameterName + " "); //$NON-NLS-1$//$NON-NLS-2$ } } return clean; } private String reentrantSerialize(EObject object) { Set<?> contexts = this.contextFinder.findByContentsAndContainer(object, null); // This is a bug fix for a bug that I cannot explain. // I some cases, the context of the given expression cannot be retreive directly. // A second call to the finding function solves the problem. while (contexts == null || contexts.isEmpty()) { Thread.yield(); contexts = this.contextFinder.findByContentsAndContainer(object, null); } final String code = this.sarlSerializer.serialize(object); if (code != null) { return code.trim(); } return code; } /** Copy the JVM operations from the source to the destination. * * @param source the source. * @param target the destination. * @param createdActions the set of actions that are created before (input) or during (output) the invocation. * @param bodyBuilder the builder of the target's operations. * @since 0.5 */ protected void copyNonStaticPublicJvmOperations(JvmGenericType source, JvmGenericType target, Set<ActionPrototype> createdActions, Procedure2<JvmOperation, ITreeAppendable> bodyBuilder) { final Iterable<JvmOperation> operations = Iterables.transform(Iterables.filter(source.getMembers(), (it) -> { if (it instanceof JvmOperation) { final JvmOperation op = (JvmOperation) it; return !op.isStatic() && op.getVisibility() == JvmVisibility.PUBLIC; } return false; }), (it) -> (JvmOperation) it); for (final JvmOperation operation : operations) { final ActionParameterTypes types = this.sarlSignatureProvider.createParameterTypesFromJvmModel( operation.isVarArgs(), operation.getParameters()); final ActionPrototype actSigKey = this.sarlSignatureProvider.createActionPrototype( operation.getSimpleName(), types); if (createdActions.add(actSigKey)) { final JvmOperation newOp = this.typesFactory.createJvmOperation(); target.getMembers().add(newOp); newOp.setAbstract(false); newOp.setDefault(operation.isDefault()); newOp.setDeprecated(operation.isDeprecated()); newOp.setFinal(false); newOp.setNative(false); newOp.setReturnType(this.typeBuilder.cloneWithProxies(operation.getReturnType())); newOp.setSimpleName(operation.getSimpleName()); newOp.setStatic(false); newOp.setStrictFloatingPoint(operation.isStrictFloatingPoint()); newOp.setSynchronized(operation.isSynchronized()); newOp.setVisibility(JvmVisibility.PUBLIC); for (final JvmFormalParameter parameter : operation.getParameters()) { final JvmFormalParameter newParam = this.typesFactory.createJvmFormalParameter(); newOp.getParameters().add(newParam); newParam.setName(parameter.getSimpleName()); newParam.setParameterType(this.typeBuilder.cloneWithProxies(parameter.getParameterType())); } newOp.setVarArgs(operation.isVarArgs()); setBody(newOp, (it) -> bodyBuilder.apply(operation, it)); } } } /** Copy the JVM constructors from the source to the destination. * * @param source the source. * @param target the destination. * @param sarlSource the SARL source element. If {@code null}, the generated constructors will not be associated to the SARL element. * @param createdConstructors the set of constructors that are created before (input) or during (output) the invocation. * @since 0.5 */ @SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"}) protected void copyVisibleJvmConstructors(JvmGenericType source, JvmGenericType target, XtendTypeDeclaration sarlSource, Set<ActionParameterTypes> createdConstructors) { final boolean samePackage = Objects.equal(source.getPackageName(), target.getPackageName()); final Iterable<JvmConstructor> constructors = Iterables.transform(Iterables.filter(source.getMembers(), (it) -> { if (it instanceof JvmConstructor) { final JvmConstructor op = (JvmConstructor) it; return op.getVisibility() != JvmVisibility.PRIVATE && (op.getVisibility() != JvmVisibility.DEFAULT || samePackage); } return false; }), (it) -> (JvmConstructor) it); // Sort the constructor in order to always add them in the same order. final SortedSet<Pair<JvmConstructor, ActionParameterTypes>> sortedConstructors = new TreeSet<>( (elt1, elt2) -> elt1.getValue().compareTo(elt2.getValue())); for (final JvmConstructor constructor : constructors) { final ActionParameterTypes types = this.sarlSignatureProvider.createParameterTypesFromJvmModel( constructor.isVarArgs(), constructor.getParameters()); sortedConstructors.add(new Pair<>(constructor, types)); } for (final Pair<JvmConstructor, ActionParameterTypes> pair : sortedConstructors) { if (createdConstructors.add(pair.getValue())) { final JvmConstructor constructor = pair.getKey(); final JvmConstructor newCons = this.typesFactory.createJvmConstructor(); for (final JvmFormalParameter parameter : constructor.getParameters()) { final JvmFormalParameter newParam = this.typesFactory.createJvmFormalParameter(); newParam.setName(parameter.getSimpleName()); newParam.setParameterType(this.typeBuilder.cloneWithProxies(parameter.getParameterType())); for (final JvmAnnotationReference annotationReference : parameter.getAnnotations()) { if (this.annotationUtils.findAnnotation(newParam, annotationReference.getAnnotation().getQualifiedName()) == null) { final JvmAnnotationReference annotation = EcoreUtil.copy(annotationReference); if (annotation != null) { newParam.getAnnotations().add(annotation); } } } newCons.getParameters().add(newParam); } newCons.setDeprecated(constructor.isDeprecated()); newCons.setSimpleName(target.getSimpleName()); newCons.setVisibility(constructor.getVisibility()); newCons.setVarArgs(constructor.isVarArgs()); setBody(newCons, (it) -> { it.append(this.grammarKeywordAccess.getSuperKeyword()); it.append("("); //$NON-NLS-1$ boolean first = true; for (final JvmFormalParameter parameter : newCons.getParameters()) { if (first) { first = false; } else { it.append(", "); //$NON-NLS-1$ } it.append(parameter.getSimpleName()); } it.append(");"); //$NON-NLS-1$ }); copyAndCleanDocumentationTo(constructor, newCons); appendGeneratedAnnotation(newCons, getContext(target)); for (final JvmAnnotationReference annotationReference : constructor.getAnnotations()) { if (this.annotationUtils.findAnnotation(newCons, annotationReference.getAnnotation().getQualifiedName()) == null) { final JvmAnnotationReference annotation = EcoreUtil.copy(annotationReference); if (annotation != null) { newCons.getAnnotations().add(annotation); } } } target.getMembers().add(newCons); } } } /** Internal error. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private abstract static class InternalError extends RuntimeException { private static final long serialVersionUID = 4637115741105214351L; InternalError(String message) { super(message); } } /** Internal error. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class GenerationContextNotFoundInternalError extends InternalError { private static final long serialVersionUID = 2275793506661573859L; GenerationContextNotFoundInternalError(JvmIdentifiableElement type) { super("generation context cannot be found for: " + type.getIdentifier()); //$NON-NLS-1$ } } }