Skip to content

Commit

Permalink
[lang] Add @DefaultSkill annotation.
Browse files Browse the repository at this point in the history
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 <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Sep 15, 2017
1 parent e9781fd commit ca92975
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 1 deletion.
@@ -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.
*
* <p>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.
*
* <p>The SRE could use this annotation to create dynamically the skill when
* the {@code Agent.getSkill()} is called.
*
* <p>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<? extends Skill> value();

}
Expand Up @@ -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() {
//
}
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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<Class<?>, ElementType> result = ImmutableMultimap.builder();
result.putAll(this.targetInfos);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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$
Expand Down
Expand Up @@ -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}''.
Expand All @@ -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.
Expand Down
@@ -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();
}

}
1 change: 1 addition & 0 deletions tests/io.sarl.tests.testdata/src/foo/MockFinalAgent.java
Expand Up @@ -32,6 +32,7 @@
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("deprecation")
public final class MockFinalAgent extends Agent {

/**
Expand Down

0 comments on commit ca92975

Please sign in to comment.