diff --git a/docs/io.sarl.docs.markdown/src/main/documentation/reference/Agent.md b/docs/io.sarl.docs.markdown/src/main/documentation/reference/Agent.md index f76abe526b..c0c9637ece 100644 --- a/docs/io.sarl.docs.markdown/src/main/documentation/reference/Agent.md +++ b/docs/io.sarl.docs.markdown/src/main/documentation/reference/Agent.md @@ -613,7 +613,7 @@ implemented capacity. In the following example, the agent is creating the [:skiskill:] skill. This instance is associated with the corresponding capacity [:capcap:] with the function `[:setskillfctfull]{[:setskillfct](setSkill)(Skill, Class*)}`. -When the function `setSkill` is returning, the agent becomes able to use the skill. +When the function [:setskillfct:] is returning, the agent becomes able to use the skill. [:Success:] package io.sarl.docs.reference.ar @@ -638,6 +638,32 @@ When the function `setSkill` is returning, the agent becomes able to use the ski [:End:] + +If some cases, you may want to set the skill if one was not set up before. The specific behavior +is supported by `[:setskillifabsfctfull]{[:setskillifabsfct](setSkillIfabsent)(Skill, Class*)}`. + + [:Success:] + package io.sarl.docs.reference.ar + import io.sarl.core.Logging + import io.sarl.core.Initialize + capacity Cap { + def action + } + skill [:skiskill](Ski) implements [:capcap](Cap) { + uses Logging + def action { + info("Action") + } + } + [:On] + agent MyAgent { + on Initialize { + var theSkill = new Ski + [:setskillifabsfct!](theSkill) + } + } + [:End:] + ### Giving a Built-in Skill to an Agent Because the built-in capacities are supported by the runtime environment, the corresponding diff --git a/docs/io.sarl.docs.markdown/src/main/documentation/reference/Behavior.md b/docs/io.sarl.docs.markdown/src/main/documentation/reference/Behavior.md index 709c3893e6..0d6f5170dd 100644 --- a/docs/io.sarl.docs.markdown/src/main/documentation/reference/Behavior.md +++ b/docs/io.sarl.docs.markdown/src/main/documentation/reference/Behavior.md @@ -506,6 +506,7 @@ It is possible for a behavior to assign a skill to its agent. uses Logging def action { info("Action") } } + [:On] behavior MyBehavior { new (owner : Agent) { super(owner) @@ -516,6 +517,34 @@ It is possible for a behavior to assign a skill to its agent. [:End:] + + + +If some cases, you may want to set the skill if one was not set up before. The specific behavior +is supported by `[:setskillifabsfctfull]{[:setskillifabsfct](setSkillIfAbsent)(Skill, Class*)}`. + + [:Success:] + package io.sarl.docs.reference.br + import io.sarl.core.Logging + import io.sarl.lang.core.Agent + capacity Cap { + def action + } + skill Ski implements Cap { + uses Logging + def action { info("Action") } + } + [:On] + behavior MyBehavior { + new (owner : Agent) { + super(owner) + var theSkill = new Ski + setSkillIfAbsent( theSkill, Cap ) + } + } + [:End:] + + ### Using a Capacity with the Getters For invoking a function implemented by a skill, the two following steps must be done: diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AbstractSkillContainer.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AbstractSkillContainer.java index 29d57a1c59..9e9646f6aa 100644 --- a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AbstractSkillContainer.java +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AbstractSkillContainer.java @@ -88,19 +88,26 @@ public AbstractSkillContainer( @Override @SafeVarargs protected final S setSkill(S skill, Class... capacities) { - $setSkill(skill, capacities); + $setSkill(skill, false, capacities); return skill; } + @Override + @SafeVarargs + protected final void setSkillIfAbsent(Skill skill, Class... capacities) { + $setSkill(skill, true, capacities); + } + /** Add a skill to the agent. * * @param skill the new skill. + * @param ifabsent indicates if the skill mapping is set up if it is absent. * @param capacities the implemented capacities by the skill. * @return the reference to the skill. - * @since 16.0 + * @since 0.11 */ @SafeVarargs - protected final AtomicSkillReference $setSkill(Skill skill, Class... capacities) { + protected final AtomicSkillReference $setSkill(Skill skill, boolean ifabsent, Class... capacities) { assert skill != null : "the skill parameter must not be null"; //$NON-NLS-1$ $attachOwner(skill); AtomicSkillReference newRef = null; @@ -110,7 +117,7 @@ protected final S setSkill(S skill, Class. final Class type = element.getRawType(); if (Capacity.class.isAssignableFrom(type) && !Capacity.class.equals(type)) { final Class capacityType = type.asSubclass(Capacity.class); - newRef = registerSkill(skill, capacityType, newRef); + newRef = registerSkill(skill, ifabsent, capacityType, newRef); } } } else { @@ -122,7 +129,7 @@ protected final S setSkill(S skill, Class. "the skill must implement the given capacity " //$NON-NLS-1$ + capacity.getName()); } - newRef = registerSkill(skill, capacity, newRef); + newRef = registerSkill(skill, ifabsent, capacity, newRef); } } return newRef; @@ -135,11 +142,19 @@ protected final S setSkill(S skill, Class. */ protected abstract void $attachOwner(Skill skill); - private AtomicSkillReference registerSkill(Skill skill, Class capacity, AtomicSkillReference firstRef) { - final AtomicSkillReference newReference = new AtomicSkillReference(skill); - final AtomicSkillReference oldReference = $getSkillRepository().put(capacity, newReference); - if (oldReference != null) { - oldReference.clear(); + private AtomicSkillReference registerSkill(Skill skill, boolean ifabsent, + Class capacity, AtomicSkillReference firstRef) { + final AtomicSkillReference newReference; + if (ifabsent) { + newReference = $getSkillRepository().computeIfAbsent(capacity, it -> { + return new AtomicSkillReference(skill); + }); + } else { + newReference = new AtomicSkillReference(skill); + final AtomicSkillReference oldReference = $getSkillRepository().put(capacity, newReference); + if (oldReference != null) { + oldReference.clear(); + } } if (firstRef == null) { return newReference; diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/Agent.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/Agent.java index d9762f4208..529eb838aa 100644 --- a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/Agent.java +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/Agent.java @@ -74,7 +74,7 @@ public Agent( final Map, Skill> builtinCapacities = provider.getBuiltinCapacities(this); if (builtinCapacities != null && !builtinCapacities.isEmpty()) { for (final Entry, Skill> bic : builtinCapacities.entrySet()) { - $setSkill(bic.getValue(), bic.getKey()); + $setSkill(bic.getValue(), false, bic.getKey()); } } } diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentProtectedAPIObject.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentProtectedAPIObject.java index 3b423577b7..9e165978c7 100644 --- a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentProtectedAPIObject.java +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentProtectedAPIObject.java @@ -90,12 +90,24 @@ public final String toString() { * * @param - type of the skill. * @param capacities the capacity or the capacities to set. - * @param skill implementaion of capacity. + * @param skill implementation of capacity. * @return the skill that was set. + * @see #setSkillIfAbsent(Skill, Class...) */ @SuppressWarnings("unchecked") protected abstract S setSkill(S skill, Class... capacities); + /** + * Set the skill for the {@link Capacity} capacity if the mapping is not yet set. + * + * @param capacities the capacity or the capacities to set. + * @param skill implementation of capacity. + * @see #setSkill(Skill, Class...) + * @since 0.11 + */ + @SuppressWarnings("unchecked") + protected abstract void setSkillIfAbsent(Skill skill, Class... capacities); + /** * Clears the Skill associated with the capacity. * diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentTrait.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentTrait.java index 96e8675c2d..22daa0d7cd 100644 --- a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentTrait.java +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/AgentTrait.java @@ -141,6 +141,15 @@ protected final S setSkill(S skill, Class. return owner.setSkill(skill, capacities); } + @Override + @SafeVarargs + protected final void setSkillIfAbsent(Skill skill, Class... capacities) { + final Agent owner = getOwner(); + if (owner != null) { + owner.setSkillIfAbsent(skill, capacities); + } + } + @Override protected S clearSkill(Class capacity) { final Agent owner = getOwner(); diff --git a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/SREutils.java b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/SREutils.java index 37680fb5e1..ebb9fd0352 100644 --- a/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/SREutils.java +++ b/main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/SREutils.java @@ -133,7 +133,20 @@ public static S castInternalSkillReference(AbstractSkillCon */ public static AtomicSkillReference setInternalSkill(AbstractSkillContainer container, Skill skill, Class[] capacities) { assert capacities != null; - return container.$setSkill(skill, capacities); + return container.$setSkill(skill, false, capacities); + } + + /** Set the internal skill of a skill container if the skill is not yet mapped. + * + * @param container the container. + * @param skill the skill instance to attach to the container. + * @param capacities the list of implemented capacities. This array cannot be {@code null}. + * @return the reference to the skill. + * @since 0.11 + */ + public static AtomicSkillReference setInternalSkillIfAbsent(AbstractSkillContainer container, Skill skill, Class[] capacities) { + assert capacities != null; + return container.$setSkill(skill, true, capacities); } /** Replies the internal skill of a skill container. diff --git a/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractAgentTraitBehaviorTest.java b/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractAgentTraitBehaviorTest.java index cfbee4a792..ee7caf2e22 100644 --- a/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractAgentTraitBehaviorTest.java +++ b/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractAgentTraitBehaviorTest.java @@ -43,6 +43,7 @@ import io.sarl.lang.core.Skill; import io.sarl.lang.core.SpaceID; import io.sarl.lang.core.UnimplementedCapacityException; +import io.sarl.lang.core.tests.core.AbstractAgentTraitBehaviorTest.Capacity1.ContextAwareCapacityWrapper; import io.sarl.tests.api.AbstractSarlTest; import io.sarl.tests.api.Nullable; @@ -147,6 +148,33 @@ public void setSkill() throws Exception { assertInstanceOf(Capacity1.class, result); } + @Test + public void setSkillIfAbsent() throws Exception { + Skill1 skill = new Skill1(); + // + Object instance = getInstance(); + invokeProc(instance.getClass(), instance, + "setSkillIfAbsent", new Class[] {Skill.class, Class[].class}, skill, new Class[] {Capacity1.class}); + Object result = invokeFunc(instance.getClass(), instance, Object.class, + "getSkill", new Class[] {Class.class}, Capacity1.class); + while (result instanceof Capacity.ContextAwareCapacityWrapper) { + result = ((Capacity.ContextAwareCapacityWrapper) result).getDelegate(); + } + // + assertSame(skill, result); + // + Skill1 skill2 = new Skill1(); + invokeProc(instance.getClass(), instance, + "setSkillIfAbsent", new Class[] {Skill.class, Class[].class}, skill2, new Class[] {Capacity1.class}); + result = invokeFunc(instance.getClass(), instance, Object.class, + "getSkill", new Class[] {Class.class}, Capacity1.class); + while (result instanceof Capacity.ContextAwareCapacityWrapper) { + result = ((Capacity.ContextAwareCapacityWrapper) result).getDelegate(); + } + // + assertSame(skill, result); + } + @Test public void operator_mappedTo() throws Exception { Skill1 skill = new Skill1(); diff --git a/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractSkillContainerTest.java b/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractSkillContainerTest.java index 71da15600f..e9e8de6baa 100644 --- a/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractSkillContainerTest.java +++ b/tests/io.sarl.lang.core.tests/src/test/java/io/sarl/lang/core/tests/core/AbstractSkillContainerTest.java @@ -106,21 +106,13 @@ public void setSkill() throws Exception { assertNoSkill(Skill1.class); assertNoSkill(Skill2.class); - try { + assertException(InvalidParameterException.class, () -> { this.container.setSkill_Fake(new Skill2(), Capacity1.class); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); - try { + assertException(InvalidParameterException.class, () -> { this.container.setSkill_Fake(new Skill3(), Capacity1.class); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); s2 = new Skill2(); r = this.container.setSkill_Fake(s2, Capacity2.class); @@ -130,21 +122,13 @@ public void setSkill() throws Exception { assertNoSkill(Skill1.class); assertNoSkill(Skill2.class); - try { + assertException(InvalidParameterException.class, () -> { this.container.setSkill_Fake(new Skill1(), Capacity2.class); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); - try { + assertException(InvalidParameterException.class, () -> { this.container.setSkill_Fake(new Skill3(), Capacity2.class); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); } @Test @@ -167,6 +151,74 @@ public void setSkill_withoutCapacity() throws Exception { assertNoSkill(Skill2.class); } + @Test + public void setSkillIfAbsent() throws Exception { + Skill s1, s2; + Object r; + + assertNoSkill(Capacity1.class); + assertNoSkill(Capacity2.class); + assertNoSkill(Skill1.class); + assertNoSkill(Skill2.class); + + s1 = new Skill1(); + this.container.setSkillIfAbsent_Fake(s1, Capacity1.class); + r = this.container.getSkill_Fake(Capacity1.class); + assertSame(s1, r); + assertSkill(Capacity1.class, s1); + assertNoSkill(Capacity2.class); + assertNoSkill(Skill1.class); + assertNoSkill(Skill2.class); + + assertException(InvalidParameterException.class, () -> { + this.container.setSkillIfAbsent_Fake(new Skill2(), Capacity1.class); + }); + + assertException(InvalidParameterException.class, () -> { + this.container.setSkillIfAbsent_Fake(new Skill3(), Capacity1.class); + }); + + s2 = new Skill4(); + this.container.setSkillIfAbsent_Fake(s2, Capacity1.class); + r = this.container.getSkill_Fake(Capacity1.class); + assertSame(s1, r); + + assertException(InvalidParameterException.class, () -> { + this.container.setSkillIfAbsent_Fake(new Skill1(), Capacity2.class); + }); + + assertException(InvalidParameterException.class, () -> { + this.container.setSkillIfAbsent_Fake(new Skill3(), Capacity2.class); + }); + } + + @Test + public void setSkillIfAbsent_withoutCapacity() throws Exception { + Skill s4; + Object r; + + assertNoSkill(Capacity1.class); + assertNoSkill(Capacity2.class); + assertNoSkill(Skill1.class); + assertNoSkill(Skill2.class); + assertNoSkill(Skill4.class); + + s4 = new Skill4(); + this.container.setSkillIfAbsent_Fake(s4); + r = this.container.getSkill_Fake(Capacity2.class); + + assertSame(s4, r); + assertSkill(Capacity1.class, s4); + assertSkill(Capacity2.class, s4); + assertNoSkill(Skill1.class); + assertNoSkill(Skill2.class); + + Skill s2 = new Skill2(); + this.container.setSkillIfAbsent_Fake(s2); + r = this.container.getSkill_Fake(Capacity2.class); + assertSame(s4, r); +} + @Test public void clearSkill_multipleCapacityImplementation() throws Exception { assertNoSkill(Capacity1.class); @@ -282,21 +334,14 @@ public void operator_mappedTo() throws Exception { assertNoSkill(Skill1.class); assertNoSkill(Skill2.class); - try { + assertException(InvalidParameterException.class, () -> { this.container.operator_mappedTo(Capacity1.class, new Skill2()); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); - try { + assertException(InvalidParameterException.class, () -> { this.container.operator_mappedTo(Capacity1.class, new Skill3()); fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); s2 = new Skill2(); this.container.operator_mappedTo(Capacity2.class, s2); @@ -305,21 +350,14 @@ public void operator_mappedTo() throws Exception { assertNoSkill(Skill1.class); assertNoSkill(Skill2.class); - try { + assertException(InvalidParameterException.class, () -> { this.container.operator_mappedTo(Capacity2.class, new Skill1()); fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); - try { + assertException(InvalidParameterException.class, () -> { this.container.operator_mappedTo(Capacity2.class, new Skill3()); - fail("Expecting the exception InvalidParameterException, but got no exception."); //$NON-NLS-1$ - } - catch(InvalidParameterException exception) { - // - } + }); } @Test @@ -494,10 +532,18 @@ public AbstractSkillContainerMock(UUID id) { this.id = id; } + public S getSkill_Fake(Class capacity) { + return getSkill(capacity); + } + public S setSkill_Fake(S skill, Class... capacity) { return setSkill(skill, capacity); } + public void setSkillIfAbsent_Fake(Skill skill, Class... capacity) { + setSkillIfAbsent(skill, capacity); + } + @Override public AtomicSkillReference $getSkill(Class capacity) { return super.$getSkill(capacity);