bdi4jade

Goal owner logic

8/14/2014 11:05:59 PM

Details

diff --git a/bdi-jade/src/bdi4jade/core/BDIAgent.java b/bdi-jade/src/bdi4jade/core/BDIAgent.java
index f5db993..a5a59a0 100644
--- a/bdi-jade/src/bdi4jade/core/BDIAgent.java
+++ b/bdi-jade/src/bdi4jade/core/BDIAgent.java
@@ -38,6 +38,7 @@ import java.util.Set;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import bdi4jade.annotation.GoalOwner;
 import bdi4jade.belief.Belief;
 import bdi4jade.core.GoalUpdateSet.GoalDescription;
 import bdi4jade.event.GoalEvent;
@@ -47,6 +48,7 @@ import bdi4jade.goal.GoalStatus;
 import bdi4jade.goal.Softgoal;
 import bdi4jade.message.BDIAgentMsgReceiver;
 import bdi4jade.plan.Plan;
+import bdi4jade.util.ReflectionUtils;
 
 /**
  * This class is an extension of {@link Agent} that has a current set of goals,
@@ -124,7 +126,8 @@ public class BDIAgent extends Agent {
 						.getGeneratedGoals()) {
 					Intention intention = addIntention(goal.getDispatcher(),
 							goal.getGoal(), null);
-					agentGoalUpdateSet.addIntention(intention);
+					if (intention != null)
+						agentGoalUpdateSet.addIntention(intention);
 				}
 				for (GoalUpdateSet goalUpdateSet : capabilityGoalUpdateSets
 						.values()) {
@@ -132,7 +135,8 @@ public class BDIAgent extends Agent {
 							.getGeneratedGoals()) {
 						Intention intention = addIntention(
 								goal.getDispatcher(), goal.getGoal(), null);
-						goalUpdateSet.addIntention(intention);
+						if (intention != null)
+							goalUpdateSet.addIntention(intention);
 					}
 				}
 
@@ -228,6 +232,7 @@ public class BDIAgent extends Agent {
 	private Set<Capability> capabilities;
 	protected final List<GoalListener> goalListeners;
 	protected final Log log;
+	private Map<Class<? extends Capability>, Set<Capability>> restrictedAccessOwnersMap;
 	private final Set<Softgoal> softgoals;
 
 	/**
@@ -237,6 +242,7 @@ public class BDIAgent extends Agent {
 		this.log = LogFactory.getLog(this.getClass());
 		this.bdiInterpreter = new BDIInterpreter(this);
 		this.capabilities = new HashSet<>();
+		this.restrictedAccessOwnersMap = new HashMap<>();
 		this.allIntentions = new HashMap<>();
 		this.aggregatedCapabilities = new HashSet<>();
 		this.agentIntentions = new LinkedList<>();
@@ -291,6 +297,7 @@ public class BDIAgent extends Agent {
 		synchronized (aggregatedCapabilities) {
 			this.aggregatedCapabilities.add(capability);
 			resetAllCapabilities();
+			computeGoalOwnersMap();
 		}
 	}
 
@@ -370,8 +377,15 @@ public class BDIAgent extends Agent {
 	 */
 	private final Intention addIntention(Capability dispatcher, Goal goal,
 			GoalListener goalListener) {
+		Intention intention = null;
+		try {
+			intention = new Intention(goal, this, dispatcher);
+		} catch (IllegalAccessException exc) {
+			log.error(exc);
+			return null;
+		}
+
 		synchronized (allIntentions) {
-			Intention intention = new Intention(goal, this, dispatcher);
 			this.allIntentions.put(goal, intention);
 			if (dispatcher == null) {
 				agentIntentions.add(intention);
@@ -399,6 +413,13 @@ public class BDIAgent extends Agent {
 		}
 	}
 
+	private void computeGoalOwnersMap() {
+		this.restrictedAccessOwnersMap = new HashMap<>();
+		for (Capability capability : aggregatedCapabilities) {
+			ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+		}
+	}
+
 	/**
 	 * Drops a given goal of this agent, which means setting it as no longer
 	 * desired. If the goal is not part of the agent's current goals, no action
@@ -584,6 +605,25 @@ public class BDIAgent extends Agent {
 	}
 
 	/**
+	 * Returns the capability instances that owns a dispatched goal, considering
+	 * the aggregated capabilities of this agent.
+	 * 
+	 * If this method returns an empty set, it means that this agent cannot add
+	 * a goal without the scope of a dispatcher that has access to it.
+	 * 
+	 * @param owner
+	 *            the annotation with the goal owner.
+	 * @return the capability instances related to this capability that owns the
+	 *         goal, or an empty set if the agent cannot add this goal.
+	 */
+	public Set<Capability> getGoalOwner(GoalOwner owner) {
+		Set<Capability> restrictedAccessOwners = restrictedAccessOwnersMap
+				.get(owner.capability());
+		return restrictedAccessOwners == null ? new HashSet<Capability>()
+				: restrictedAccessOwners;
+	}
+
+	/**
 	 * Returns all agent intentions, which are goals that this agent is
 	 * committed to achieve.
 	 * 
@@ -622,6 +662,7 @@ public class BDIAgent extends Agent {
 			boolean removed = this.aggregatedCapabilities.remove(capability);
 			if (removed) {
 				resetAllCapabilities();
+				computeGoalOwnersMap();
 			}
 			return removed;
 		}
diff --git a/bdi-jade/src/bdi4jade/core/Capability.java b/bdi-jade/src/bdi4jade/core/Capability.java
index 4fe0020..395a0e2 100644
--- a/bdi-jade/src/bdi4jade/core/Capability.java
+++ b/bdi-jade/src/bdi4jade/core/Capability.java
@@ -25,15 +25,19 @@ package bdi4jade.core;
 import jade.lang.acl.ACLMessage;
 
 import java.io.Serializable;
+import java.security.acl.Owner;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import bdi4jade.annotation.GoalOwner;
 import bdi4jade.belief.Belief;
 import bdi4jade.belief.BeliefBase;
 import bdi4jade.core.GoalUpdateSet.GoalDescription;
@@ -48,6 +52,7 @@ import bdi4jade.reasoning.DefaultPlanSelectionStrategy;
 import bdi4jade.reasoning.DeliberationFunction;
 import bdi4jade.reasoning.OptionGenerationFunction;
 import bdi4jade.reasoning.PlanSelectionStrategy;
+import bdi4jade.util.ReflectionUtils;
 
 /**
  * This capability represents a component that aggregates the mental attitudes
@@ -65,16 +70,18 @@ public class Capability implements Serializable {
 	protected final BeliefBase beliefBase;
 	private BeliefRevisionStrategy beliefRevisionStrategy;
 	private DeliberationFunction deliberationFunction;
+	private Map<Class<? extends Capability>, Set<Capability>> fullAccessOwnersMap;
 	protected final String id;
 	private final Collection<Intention> intentions;
 	protected final Log log;
 	private BDIAgent myAgent;
 	private OptionGenerationFunction optionGenerationFunction;
+	private final List<Class<? extends Capability>> parentCapabilities;
 	private final Set<Capability> partCapabilities;
 	protected final PlanLibrary planLibrary;
 	private PlanSelectionStrategy planSelectionStrategy;
+	private Map<Class<? extends Capability>, Set<Capability>> restrictedAccessOwnersMap;
 	private boolean started;
-
 	private Capability wholeCapability;
 
 	/**
@@ -137,6 +144,7 @@ public class Capability implements Serializable {
 			Set<Plan> initialPlans) {
 		this.log = LogFactory.getLog(getClass());
 		this.intentions = new LinkedList<>();
+		this.parentCapabilities = generateParentCapabilities();
 
 		// Id initialization
 		if (id == null) {
@@ -169,6 +177,8 @@ public class Capability implements Serializable {
 		this.deliberationFunction = new DefaultDeliberationFunction();
 		this.planSelectionStrategy = new DefaultPlanSelectionStrategy();
 
+		computeGoalOwnersMap();
+
 		synchronized (this) {
 			this.started = false;
 		}
@@ -203,6 +213,7 @@ public class Capability implements Serializable {
 	public final void addAssociatedCapability(Capability capability) {
 		this.associationTargets.add(capability);
 		capability.associationSources.add(this);
+		computeGoalOwnersMap();
 		resetAgentCapabilities();
 	}
 
@@ -244,6 +255,7 @@ public class Capability implements Serializable {
 		partCapability.wholeCapability = this;
 		this.partCapabilities.add(partCapability);
 
+		computeGoalOwnersMap();
 		resetAgentCapabilities();
 	}
 
@@ -270,6 +282,21 @@ public class Capability implements Serializable {
 		return false;
 	}
 
+	private void computeGoalOwnersMap() {
+		this.fullAccessOwnersMap = new HashMap<>();
+		ReflectionUtils.addGoalOwner(fullAccessOwnersMap, this);
+		if (wholeCapability != null) {
+			ReflectionUtils.addGoalOwner(fullAccessOwnersMap, this);
+		}
+		this.restrictedAccessOwnersMap = new HashMap<>();
+		for (Capability capability : associationTargets) {
+			ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+		}
+		for (Capability capability : partCapabilities) {
+			ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+		}
+	}
+
 	/**
 	 * Returns true if the object given as parameter is a capability and has the
 	 * same full id of this capability.
@@ -320,6 +347,19 @@ public class Capability implements Serializable {
 		this.optionGenerationFunction.generateGoals(goalUpdateSet);
 	}
 
+	@SuppressWarnings("unchecked")
+	private List<Class<? extends Capability>> generateParentCapabilities() {
+		List<Class<? extends Capability>> parentCapabilities = new LinkedList<>();
+		Class<?> currentClass = this.getClass();
+		while (Capability.class.isAssignableFrom(currentClass)
+				&& !Capability.class.equals(currentClass)) {
+			parentCapabilities.add((Class<Capability>) currentClass
+					.getSuperclass());
+			currentClass = currentClass.getSuperclass();
+		}
+		return parentCapabilities;
+	}
+
 	/**
 	 * Returns all capabilities with which this capability is associated.
 	 * 
@@ -377,6 +417,47 @@ public class Capability implements Serializable {
 	}
 
 	/**
+	 * Returns the capability instances that owns a dispatched goal, considering
+	 * the superclasses of this capability, its associations and compositions.
+	 * 
+	 * A capability may dispatch its own goals and goals of its parents. It may
+	 * also dispatch external goals of associated or part capabilities (and
+	 * their parents), and all goals of whole capabilities.
+	 * 
+	 * This method thus searches all capabilities that have a relationship with
+	 * this capability (either inheritance, composition or association) and
+	 * finds the concrete capability instances whose definition owns a goal
+	 * (specified with the {@link Owner} annotation in goals).
+	 * 
+	 * If this method returns an empty set, it means that this capability has no
+	 * access to the goal owned by capabilities of the given class.
+	 * 
+	 * @param owner
+	 *            the annotation with the goal owner.
+	 * @return the capability instances related to this capability (or the
+	 *         capability itself) that owns the goal, or an empty set if the
+	 *         capability has no access to goals owned by capability of the
+	 *         given class.
+	 */
+	public Set<Capability> getGoalOwner(GoalOwner owner) {
+		Set<Capability> owners = new HashSet<>();
+
+		Set<Capability> fullAccessOwners = fullAccessOwnersMap.get(owner
+				.capability());
+		if (fullAccessOwners != null)
+			owners.addAll(fullAccessOwners);
+
+		if (!owner.internal()) {
+			Set<Capability> restrictedAccessOwners = restrictedAccessOwnersMap
+					.get(owner.capability());
+			if (restrictedAccessOwners != null)
+				owners.addAll(restrictedAccessOwners);
+		}
+
+		return owners;
+	}
+
+	/**
 	 * Returns this capability id.
 	 * 
 	 * @return the id.
@@ -408,6 +489,15 @@ public class Capability implements Serializable {
 	}
 
 	/**
+	 * Returns the classes of all parent capabilities of this capability.
+	 * 
+	 * @return the parentCapabilities.
+	 */
+	public List<Class<? extends Capability>> getParentCapabilities() {
+		return parentCapabilities;
+	}
+
+	/**
 	 * Returns the parts of this capability.
 	 * 
 	 * @return the partCapabilities.
@@ -460,6 +550,7 @@ public class Capability implements Serializable {
 	public final void removeAssociatedCapability(Capability capability) {
 		this.associationTargets.remove(capability);
 		capability.associationSources.remove(this);
+		computeGoalOwnersMap();
 		resetAgentCapabilities();
 	}
 
@@ -480,6 +571,7 @@ public class Capability implements Serializable {
 		boolean removed = this.partCapabilities.remove(partCapability);
 		if (removed) {
 			partCapability.wholeCapability = null;
+			computeGoalOwnersMap();
 			resetAgentCapabilities();
 		}
 		return removed;
diff --git a/bdi-jade/src/bdi4jade/core/Intention.java b/bdi-jade/src/bdi4jade/core/Intention.java
index 0722bc1..c4da57d 100644
--- a/bdi-jade/src/bdi4jade/core/Intention.java
+++ b/bdi-jade/src/bdi4jade/core/Intention.java
@@ -32,6 +32,7 @@ import java.util.Set;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import bdi4jade.annotation.GoalOwner;
 import bdi4jade.event.GoalListener;
 import bdi4jade.exception.PlanInstantiationException;
 import bdi4jade.goal.Goal;
@@ -76,7 +77,8 @@ public class Intention {
 	 * @param bdiAgent
 	 *            the bdiAgent associated with this intention.
 	 */
-	public Intention(Goal goal, BDIAgent bdiAgent) {
+	public Intention(Goal goal, BDIAgent bdiAgent)
+			throws IllegalAccessException {
 		this(goal, bdiAgent, null);
 	}
 
@@ -92,7 +94,8 @@ public class Intention {
 	 * @param dispatcher
 	 *            the Capability that dispatched the goal.
 	 */
-	public Intention(Goal goal, BDIAgent bdiAgent, Capability dispatcher) {
+	public Intention(Goal goal, BDIAgent bdiAgent, Capability dispatcher)
+			throws IllegalAccessException {
 		this.log = LogFactory.getLog(this.getClass());
 		this.goal = goal;
 		this.myAgent = bdiAgent;
@@ -101,9 +104,29 @@ public class Intention {
 		this.waiting = true;
 		this.executedPlans = new HashSet<>();
 		this.currentPlan = null;
-		this.dispatcher = dispatcher;
-		this.owners = new HashSet<>(); // TODO
 		this.goalListeners = new LinkedList<>();
+		this.dispatcher = dispatcher;
+
+		GoalOwner owner = null;
+		if (goal.getClass().isAnnotationPresent(GoalOwner.class)) {
+			owner = goal.getClass().getAnnotation(GoalOwner.class);
+		}
+		if (owner == null) {
+			this.owners = new HashSet<>();
+		} else {
+			if (dispatcher == null) {
+				this.owners = myAgent.getGoalOwner(owner);
+			} else {
+				this.owners = dispatcher.getGoalOwner(owner);
+				if (owners.isEmpty()) {
+					throw new IllegalAccessException("Capability " + dispatcher
+							+ " has no access to goal "
+							+ goal.getClass().getSimpleName()
+							+ " of capability "
+							+ owner.getClass().getSimpleName());
+				}
+			}
+		}
 	}
 
 	/**
diff --git a/bdi-jade/src/bdi4jade/util/ReflectionUtils.java b/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
index 13bf193..5e9920f 100644
--- a/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
+++ b/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
@@ -23,12 +23,16 @@
 package bdi4jade.util;
 
 import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import bdi4jade.annotation.Parameter;
 import bdi4jade.annotation.Parameter.Direction;
+import bdi4jade.core.Capability;
 import bdi4jade.exception.ParameterException;
 import bdi4jade.goal.Goal;
 import bdi4jade.plan.planbody.PlanBody;
@@ -46,6 +50,29 @@ public abstract class ReflectionUtils {
 	private static final Log log = LogFactory.getLog(ReflectionUtils.class);
 	private static final String SETTER_PREFIX = "set";
 
+	// TODO
+	public static void addGoalOwner(
+			Map<Class<? extends Capability>, Set<Capability>> goalOwnersMap,
+			Capability capability) {
+		addGoalOwner(goalOwnersMap, capability.getClass(), capability);
+		for (Class<? extends Capability> parentCapability : capability
+				.getParentCapabilities()) {
+			addGoalOwner(goalOwnersMap, parentCapability, capability);
+		}
+	}
+
+	// TODO
+	public static void addGoalOwner(
+			Map<Class<? extends Capability>, Set<Capability>> goalOwnersMap,
+			Class<? extends Capability> cababilityClass, Capability capability) {
+		Set<Capability> owners = goalOwnersMap.get(cababilityClass);
+		if (owners == null) {
+			owners = new HashSet<>();
+			goalOwnersMap.put(cababilityClass, owners);
+		}
+		owners.add(capability);
+	}
+
 	private static void checkSkipIsOK(Parameter parameter, String msg,
 			Exception cause) throws ParameterException {
 		if (parameter.mandatory()) {