Skip to content

Commit

Permalink
[lang] Cleared references to skills are now properly supported.
Browse files Browse the repository at this point in the history
When the skill references were cleared, the getSkill function of the
agents was not throwing the UnimplementedCapacityException.

see #975
see #976

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Mar 5, 2020
1 parent 856d6cd commit dbed927
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 26 deletions.
88 changes: 63 additions & 25 deletions main/coreplugins/io.sarl.lang.core/src/io/sarl/lang/core/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
@SarlSpecification(SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING)
public class Agent extends AgentProtectedAPIObject implements Identifiable {

private static final DynamicSkillProvider SINGLETON = (it0, it1) -> null;

private final UUID id;

private final UUID parentID;
Expand Down Expand Up @@ -125,7 +127,11 @@ public Agent(
DynamicSkillProvider skillProvider) {
this.parentID = parentID;
this.id = (agentID == null) ? UUID.randomUUID() : agentID;
this.skillProvider = skillProvider;
if (skillProvider == null) {
this.skillProvider = SINGLETON;
} else {
this.skillProvider = skillProvider;
}
}

@Override
Expand Down Expand Up @@ -160,7 +166,11 @@ public UUID getID() {
* @since 0.6
*/
void $setDynamicSkillProvider(DynamicSkillProvider provider) {
this.skillProvider = provider;
if (provider == null) {
this.skillProvider = SINGLETON;
} else {
this.skillProvider = provider;
}
}

/** Replies the skill repository.
Expand Down Expand Up @@ -359,39 +369,67 @@ protected final <S extends Capacity> S getSkill(Class<S> capacity) {
@Pure
protected AtomicClearableReference<Skill> $getSkill(Class<? extends Capacity> capacity) {
AtomicClearableReference<Skill> skill = $getSkillRepository().get(capacity);
// Check if the stored skill is still not empty
if (skill != null) {
final Skill s = skill.get();
if (s == null) {
skill = null;
}
}
if (skill == null) {
// Try to load dynamically the skill
final DynamicSkillProvider dsp = this.skillProvider;
if (dsp != null) {
final ClearableReference<Skill> reference = dsp.installSkill(this, capacity);
if (reference != null) {
return reference;
}
skill = createSkillFromProvider(capacity);
if (skill == null) {
// Use the default skill declaration if present.
skill = createDefaultSkill(capacity);
}
// Use the default skill declaration if present.
final DefaultSkill annotation = capacity.getAnnotation(DefaultSkill.class);
if (annotation != null) {
if (skill == null) {
throw new UnimplementedCapacityException(capacity, getID());
}
}
return skill;
}

private AtomicClearableReference<Skill> createSkillFromProvider(Class<? extends Capacity> capacity) {
assert this.skillProvider != null;
final AtomicClearableReference<Skill> reference = this.skillProvider.installSkill(this, capacity);
if (reference != null) {
final Skill s = reference.get();
if (s != null) {
return reference;
}
}
return null;
}

private AtomicClearableReference<Skill> createDefaultSkill(Class<? extends Capacity> capacity) {
final DefaultSkill annotation = capacity.getAnnotation(DefaultSkill.class);
if (annotation != null) {
try {
final Class<? extends Skill> type = annotation.value();
Constructor<? extends Skill> cons;
try {
final Class<? extends Skill> type = annotation.value();
Constructor<? extends Skill> cons;
try {
cons = type.getConstructor(Agent.class);
cons.setAccessible(true);
final Skill skillInstance = cons.newInstance(this);
return $setSkill(skillInstance);
} catch (Throwable exception) {
cons = type.getConstructor();
}
cons = type.getConstructor(Agent.class);
cons.setAccessible(true);
final Skill skillInstance = cons.newInstance();
final Skill skillInstance = cons.newInstance(this);
return $setSkill(skillInstance);
} catch (Throwable exception) {
throw new UnimplementedCapacityException(capacity, getID(), exception);
cons = type.getConstructor();
}
cons.setAccessible(true);
final Skill skillInstance = cons.newInstance();
final AtomicClearableReference<Skill> ref = $setSkill(skillInstance);
if (ref != null) {
final Skill s = ref.get();
if (s != null) {
return ref;
}
}
} catch (Throwable exception) {
throw new UnimplementedCapacityException(capacity, getID(), exception);
}
throw new UnimplementedCapacityException(capacity, getID());
}
return skill;
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import io.sarl.lang.core.Capacity;
import io.sarl.lang.core.DefaultSkill;
import io.sarl.lang.core.Event;
import io.sarl.lang.core.SREutils;
import io.sarl.lang.core.Skill;
import io.sarl.lang.core.UnimplementedCapacityException;
import io.sarl.lang.util.AtomicClearableReference;
Expand Down Expand Up @@ -451,6 +452,94 @@ public void getSkill_defaultskill() throws Exception {
assertSame(ref0, ref1);
}

@Test
public void getSkill_noRegistration() throws Exception {
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity4.class);
});
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
}

@Test
public void getSkill_registrationSkill6() throws Exception {
Skill expected = new Skill6();
SREutils.setInternalSkill(this.agent, expected, new Class[0]);
//
AtomicClearableReference<Skill> actual = this.agent.$getSkill(Capacity4.class);
assertNotNull(actual);
assertSame(expected, actual.get());
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
}

@Test
public void getSkill_registrationSkill7() throws Exception {
Skill expected = new Skill7();
SREutils.setInternalSkill(this.agent, expected, new Class[0]);
//
AtomicClearableReference<Skill> actual0 = this.agent.$getSkill(Capacity4.class);
assertNotNull(actual0);
assertSame(expected, actual0.get());
//
AtomicClearableReference<Skill> actual1 = this.agent.$getSkill(Capacity5.class);
assertNotNull(actual1);
assertSame(expected, actual1.get());
}

@Test
public void getSkill_registrationSkill6_clearReference() throws Exception {
Skill expected = new Skill6();
SREutils.setInternalSkill(this.agent, expected, new Class[0]).clear();
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity4.class);
});
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
}

@Test
public void getSkill_registrationSkill7_clearReference0() throws Exception {
Skill expected = new Skill7();
// The following line clear the Capacity5 reference only.
SREutils.setInternalSkill(this.agent, expected, new Class[0]).clear();
//
AtomicClearableReference<Skill> actual = this.agent.$getSkill(Capacity4.class);
assertNotNull(actual);
assertSame(expected, actual.get());
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
}

@Test
public void getSkill_registrationSkill7_clearReference1() throws Exception {
Skill expected = new Skill7();
// The following line clear the Capacity5 reference only.
SREutils.setInternalSkill(this.agent, expected, new Class[0]).clear();
// The following line clear the Capacity4 reference only.
SREutils.getInternalSkillReference(this.agent, Capacity4.class).clear();
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity4.class);
});
//
assertException(UnimplementedCapacityException.class, () -> {
this.agent.$getSkill(Capacity5.class);
});
}

/** Only for making public several protected methods.
*
* @author $Author: sgalland$
Expand All @@ -470,7 +559,6 @@ public AgentMock(UUID parentID) {
public <S extends Skill> S setSkill_Fake(S skill, Class<? extends Capacity>... capacity) {
return setSkill(skill, capacity);
}


@Override
public AtomicClearableReference<Skill> $getSkill(Class<? extends Capacity> capacity) {
Expand Down Expand Up @@ -577,6 +665,50 @@ public Skill3() {
}
}

/**
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static interface Capacity4 extends Capacity {
//
}

/**
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static interface Capacity5 extends Capacity4 {
//
}

/**
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class Skill6 extends Skill implements Capacity4 {
public Skill6() {
//
}
}

/**
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class Skill7 extends Skill implements Capacity5 {
public Skill7() {
//
}
}

/**
* @author $Author: sgalland$
* @version $FullVersion$
Expand Down

0 comments on commit dbed927

Please sign in to comment.