From ca92975a5f18491a8ca0ba31dc42b0c210089a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Galland?= Date: Fri, 15 Sep 2017 10:42:24 +0200 Subject: [PATCH] [lang] Add @DefaultSkill annotation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This annotation enables a SARL developer to specify a default skill implementation to a defined capacity. The dynamic loading of the specified skill is suppported by the SRE (and several SRE may not support this feature). This feature is still an hidden feature. Signed-off-by: Stéphane Galland --- .../src/io/sarl/lang/core/DefaultSkill.java | 59 +++++++++ .../io/sarl/lang/validation/IssueCodes.java | 7 + .../src/io/sarl/lang/validation/Messages.java | 2 + .../sarl/lang/validation/SARLValidator.java | 62 ++++++++- .../sarl/lang/validation/messages.properties | 2 + .../parsing/general/DefaultSkillTest.java | 124 ++++++++++++++++++ .../src/foo/MockFinalAgent.java | 1 + 7 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/DefaultSkill.java create mode 100644 tests/io.sarl.lang.tests/src/test/java/io/sarl/lang/tests/general/parsing/general/DefaultSkillTest.java diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/DefaultSkill.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/DefaultSkill.java new file mode 100644 index 0000000000..8b9b6f661f --- /dev/null +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/DefaultSkill.java @@ -0,0 +1,59 @@ +/* + * $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.core; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Annotation for associating a capacity and a default skill. + * + *

This annotation should be associated to a {@code Capacity}. + * It takes a value that is the type of the skill that should be given to an + * agent by default when it tries to use the capacity. + * This annotation is similar to the {@code @ImplementedBy} annotation into + * the injection library. + * + *

The SRE could use this annotation to create dynamically the skill when + * the {@code Agent.getSkill()} is called. + * + *

This annotation is supported by an implementation of {@link DynamicSkillProvider}. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7 + * @see DynamicSkillProvider + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface DefaultSkill { + + /** The type of the implementation skill. + * + * @return the type of the implementation skill. + */ + Class value(); + +} diff --git a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/IssueCodes.java b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/IssueCodes.java index 5ec8e6009a..a3af25b344 100644 --- a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/IssueCodes.java +++ b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/IssueCodes.java @@ -197,6 +197,13 @@ public final class IssueCodes { public static final String MISSING_BODY = ISSUE_CODE_PREFIX + "missing_body"; //$NON-NLS-1$ + /** + * The {@code @DefaultSkill} annotation has an improper value. + * @since 0.7 + */ + public static final String INVALID_DEFAULT_SKILL_ANNOTATION = + ISSUE_CODE_PREFIX + "invalid_default_skill_annotation"; //$NON-NLS-1$ + private IssueCodes() { // } diff --git a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/Messages.java b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/Messages.java index 19690cf35a..7bd159f4f2 100644 --- a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/Messages.java +++ b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/Messages.java @@ -105,6 +105,7 @@ private Messages() { public static String SARLValidator_69; public static String SARLValidator_7; public static String SARLValidator_70; + public static String SARLValidator_71; public static String SARLValidator_72; public static String SARLValidator_73; public static String SARLValidator_74; @@ -121,6 +122,7 @@ private Messages() { public static String SARLValidator_85; public static String SARLValidator_86; public static String SARLValidator_87; + public static String SARLValidator_88; public static String SARLValidator_9; public static String SARLSyntaxErrorMessageProvider_0; public static String SARLSyntaxErrorMessageProvider_1; diff --git a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/SARLValidator.java b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/SARLValidator.java index bde3e1974e..e237dd4267 100644 --- a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/SARLValidator.java +++ b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/SARLValidator.java @@ -152,16 +152,20 @@ import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.XFeatureCall; import org.eclipse.xtext.xbase.XForLoopExpression; +import org.eclipse.xtext.xbase.XTypeLiteral; import org.eclipse.xtext.xbase.XVariableDeclaration; import org.eclipse.xtext.xbase.XbasePackage; import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation; import org.eclipse.xtext.xbase.compiler.GeneratorConfig; import org.eclipse.xtext.xbase.lib.CollectionLiterals; import org.eclipse.xtext.xbase.lib.Inline; +import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; import org.eclipse.xtext.xbase.typesystem.override.IOverrideCheckResult.OverrideCheckDetails; import org.eclipse.xtext.xbase.typesystem.override.IResolvedOperation; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; +import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceFactory; +import org.eclipse.xtext.xbase.typesystem.references.StandardTypeReferenceOwner; import org.eclipse.xtext.xbase.validation.FeatureNameValidator; import org.eclipse.xtext.xbase.validation.ReadAndWriteTracking; @@ -170,6 +174,7 @@ import io.sarl.lang.core.Agent; import io.sarl.lang.core.Behavior; import io.sarl.lang.core.Capacity; +import io.sarl.lang.core.DefaultSkill; import io.sarl.lang.core.Event; import io.sarl.lang.core.Skill; import io.sarl.lang.jvmmodel.SarlJvmModelAssociations; @@ -366,7 +371,7 @@ public class SARLValidator extends AbstractSARLValidator { @Inject private ReadAndWriteTracking readAndWriteTracking; - // Update thet annotation target information + // Update the annotation target information { final ImmutableMultimap.Builder, ElementType> result = ImmutableMultimap.builder(); result.putAll(this.targetInfos); @@ -394,6 +399,18 @@ public class SARLValidator extends AbstractSARLValidator { } } + /** Create a lightweight type reference from the given type. + * + * @param type the type to point to. + * @param context the context in which the reference is located. + * @return the reference. + */ + protected LightweightTypeReference toLightweightTypeReference(JvmType type, EObject context) { + final StandardTypeReferenceOwner owner = new StandardTypeReferenceOwner(getServices(), context); + final LightweightTypeReferenceFactory factory = new LightweightTypeReferenceFactory(owner, false); + return factory.toLightweightReference(type); + } + @Override protected boolean isValueExpectedRecursive(XExpression expr) { final EObject container = expr.eContainer(); @@ -2489,6 +2506,49 @@ protected void checkAssignment(XExpression expression, EStructuralFeature featur super.checkAssignment(expression, feature, simpleAssignment); } + /** Check the correct usage of the {@link DefaultSkill} annotation. + * + * @param capacity the associated capacity to check. + */ + @Check + @SuppressWarnings("checkstyle:nestedifdepth") + public void checkDefaultSkillAnnotation(SarlCapacity capacity) { + final String annotationId = DefaultSkill.class.getName(); + final XAnnotation annotation = IterableExtensions.findFirst(capacity.getAnnotations(), (it) -> { + return Strings.equal(annotationId, it.getAnnotationType().getIdentifier()); + }); + if (annotation != null) { + final XExpression expr = annotation.getValue(); + if (expr instanceof XTypeLiteral) { + final XTypeLiteral typeLiteral = (XTypeLiteral) expr; + final JvmType type = typeLiteral.getType(); + if (type != null && !type.eIsProxy()) { + final LightweightTypeReference reference = toLightweightTypeReference(type, capacity); + // Validating by the annotation value's type. + if (reference.isSubtypeOf(Skill.class)) { + final EObject element = this.associations.getPrimaryJvmElement(capacity); + assert element instanceof JvmType; + if (!reference.isSubtypeOf((JvmType) element)) { + error(MessageFormat.format( + Messages.SARLValidator_71, + capacity.getName(), type.getSimpleName()), + expr, + null, + ValidationMessageAcceptor.INSIGNIFICANT_INDEX, + IssueCodes.INVALID_DEFAULT_SKILL_ANNOTATION); + } + return; + } + } + } + error(Messages.SARLValidator_88, + expr, + null, + ValidationMessageAcceptor.INSIGNIFICANT_INDEX, + IssueCodes.INVALID_DEFAULT_SKILL_ANNOTATION); + } + } + /** The modifier validator for constructors. * * @author $Author: sgalland$ diff --git a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/messages.properties b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/messages.properties index f249685624..0209342f9b 100644 --- a/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/messages.properties +++ b/main/coreplugins/io.sarl.lang/src/io/sarl/lang/validation/messages.properties @@ -63,6 +63,7 @@ SARLValidator_68=Supertype must be a subtype of ''{0}''. SARLValidator_69=Supertype must be of type ''{0}''. SARLValidator_7=SARL library not found on the classpath, or older than 0.3.1. Error code: {0}; Resources on classpath are:\n{1}\nDeclared fields in SARLVersion class:\n{2} SARLValidator_70=The inheritance hierarchy of ''{0}'' is inconsistent. +SARLValidator_71=Invalid annotation value. {1} is not an implementation of {0}. SARLValidator_72=Invalid implemented type: ''{0}''. Only subtypes of ''{1}'' are allowed for ''{2}''. SARLValidator_73=Invalid implemented type: ''{0}''. Only the type ''{1}'' and one of its subtypes are allowed for ''{2}''. SARLValidator_74=Missing implemented type ''{0}'' for ''{1}''. @@ -79,6 +80,7 @@ SARLValidator_84=The method {0} in type {1} should be declared abstract. SARLValidator_85='Create'-method {0} is not permitted in an interface. SARLValidator_86=Abstract methods do not specify a body. SARLValidator_87=Discouraged use of reserved annotation. @{0} is an annotation that is reserved for the compiler usage. +SARLValidator_88=Invalid annotation value. It must be a type literal to a skill. SARLValidator_9=definition of {0} SARLSyntaxErrorMessageProvider_0=''{0}'' is a reserved keyword which is not allowed as identifier. Please choose another word or alternatively confuse your co-workers by escaping it like this: "{1}". SARLSyntaxErrorMessageProvider_1=''{0}'' is a reserved keyword which is not allowed as identifier. Please choose another word. diff --git a/tests/io.sarl.lang.tests/src/test/java/io/sarl/lang/tests/general/parsing/general/DefaultSkillTest.java b/tests/io.sarl.lang.tests/src/test/java/io/sarl/lang/tests/general/parsing/general/DefaultSkillTest.java new file mode 100644 index 0000000000..be53142b8c --- /dev/null +++ b/tests/io.sarl.lang.tests/src/test/java/io/sarl/lang/tests/general/parsing/general/DefaultSkillTest.java @@ -0,0 +1,124 @@ +/* + * 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.tests.general.parsing.general; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import io.sarl.lang.sarl.SarlAction; +import io.sarl.lang.sarl.SarlAgent; +import io.sarl.lang.sarl.SarlPackage; +import io.sarl.lang.sarl.SarlScript; +import io.sarl.lang.validation.IssueCodes; +import io.sarl.tests.api.AbstractSarlTest; + +import org.eclipse.xtend.lib.annotations.Accessors; +import org.eclipse.xtend.lib.annotations.EqualsHashCode; +import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor; +import org.eclipse.xtext.diagnostics.Diagnostic; +import org.eclipse.xtext.serializer.ISerializer; +import org.eclipse.xtext.xbase.XbasePackage; +import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotationsPackage; +import org.eclipse.xtext.xtype.XtypePackage; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import com.google.common.base.Strings; +import com.google.inject.Inject; + +/** + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("all") +public class DefaultSkillTest extends AbstractSarlTest { + + @Test + public void valid() throws Exception { + SarlScript mas = file(multilineString( + "import io.sarl.lang.core.DefaultSkill", + "@DefaultSkill(typeof(S1))", + "capacity C1 {", + " def myFct", + "}", + "skill S1 implements C1 {", + " def myFct {}", + "}" + )); + validate(mas).assertNoErrors(); + } + + @Test + public void notASkill() throws Exception { + SarlScript mas = file(multilineString( + "import io.sarl.lang.core.DefaultSkill", + "@DefaultSkill(typeof(S1))", + "capacity C1 {", + " def myFct", + "}", + "class S1 {", + "}" + )); + validate(mas).assertError( + XbasePackage.eINSTANCE.getXTypeLiteral(), + org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_TYPES, + "Type mismatch"); + } + + @Test + public void notACapacityImplementation() throws Exception { + SarlScript mas = file(multilineString( + "import io.sarl.lang.core.DefaultSkill", + "@DefaultSkill(typeof(S1))", + "capacity C1 {", + " def myFct", + "}", + "capacity C2 {", + " def myFct2", + "}", + "skill S1 implements C2 {", + " def myFct2 {}", + "}" + )); + validate(mas).assertError( + XbasePackage.eINSTANCE.getXTypeLiteral(), + IssueCodes.INVALID_DEFAULT_SKILL_ANNOTATION, + "Invalid annotation value. S1 is not an implementation of C1"); + } + + @Test + public void validSubType() throws Exception { + SarlScript mas = file(multilineString( + "import io.sarl.lang.core.DefaultSkill", + "@DefaultSkill(typeof(S1))", + "capacity C1 {", + " def myFct", + "}", + "capacity C2 extends C1 {", + " def myFct2", + "}", + "skill S1 implements C2 {", + " def myFct {}", + " def myFct2 {}", + "}" + )); + validate(mas).assertNoErrors(); + } + +} \ No newline at end of file diff --git a/tests/io.sarl.tests.testdata/src/foo/MockFinalAgent.java b/tests/io.sarl.tests.testdata/src/foo/MockFinalAgent.java index ac05befdd3..c70f2e34cf 100644 --- a/tests/io.sarl.tests.testdata/src/foo/MockFinalAgent.java +++ b/tests/io.sarl.tests.testdata/src/foo/MockFinalAgent.java @@ -32,6 +32,7 @@ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ +@SuppressWarnings("deprecation") public final class MockFinalAgent extends Agent { /**