View Javadoc

1   package cz.cuni.amis.pogamut.sposh.ut2004;
2   
3   import java.lang.reflect.Constructor;
4   import java.lang.reflect.InvocationTargetException;
5   import java.util.Collections;
6   import java.util.Set;
7   
8   import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
9   import cz.cuni.amis.pogamut.sposh.context.Context;
10  import cz.cuni.amis.pogamut.sposh.context.IUT2004Context;
11  import cz.cuni.amis.pogamut.sposh.engine.PoshEngine;
12  import cz.cuni.amis.pogamut.sposh.exceptions.StateInstantiationException;
13  import cz.cuni.amis.pogamut.sposh.executor.IAction;
14  import cz.cuni.amis.pogamut.sposh.executor.ISense;
15  import cz.cuni.amis.pogamut.sposh.executor.IWorkExecutor;
16  import cz.cuni.amis.pogamut.sposh.executor.StateAction;
17  import cz.cuni.amis.pogamut.sposh.executor.StateSense;
18  import cz.cuni.amis.pogamut.sposh.executor.StateWorkExecutor;
19  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
20  
21  /**
22   * This class should be used as base for bot that utilizes sposh and state primitives.
23   * It is failry simple, it creates {@link PoshEngine}, {@link IWorkExecutor} and {@link Context} during {@link StateSposhLogicController#initializeController(cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot) }.
24   * The {@link IWorkExecutor} takes all primitives from the plan and automatically
25   * tries to instantiate them.
26   *
27   * The automatic instantiation expects that names of the primitives are fully
28   * qualified names of classes (e.g. cz.cuni.pogamut.senses.Fail) that implement 
29   * {@link ISense} for senses and {@link IAction} for actions. The classes are 
30   * expected to have public constructor with one parameter that is of type CONTEXT
31   * (or its parent). It may be convinient to use classes {@link StateAction} and 
32   * {@link StateSense} as basic classes for your own custom primitives.
33   *
34   * Custom instantiation can be utilized in method {@link StateSposhLogicController#customPrimitiveInstantiation(cz.cuni.amis.pogamut.sposh.executor.StateWorkExecutor, java.util.Set, java.util.Set)  },
35   * simply insert your own primitives into the {@link StateWorkExecutor}. When automatic
36   * instantiation detects, that primitive with some name (probaly name that is not
37   * FQN of class) is already defined in the executor, the name is skipped (IOW: if
38   * you add sense <b>hurt</b> in the custom instantion, it won't cause error later one,
39   * although it is not defined by any class).
40   * 
41   * @author Honza
42   */
43  @AgentScoped
44  public abstract class StateSposhLogicController<BOT extends UT2004Bot, CONTEXT extends IUT2004Context> extends SposhLogicController<BOT, StateWorkExecutor> {
45  
46      /** Context for states. */
47      protected CONTEXT context;
48  
49      /**
50       * Initialize logic controller=call super initialization and create context
51       * and other stuff that is needed to have.
52       * @param bot
53       */
54      @Override
55      public void initializeController(BOT bot) {
56          super.initializeController(bot);
57          context = createContext();
58          // This will make sure work executor is instantiated
59          getWorkExecutor();
60      }
61  
62  
63      @Override
64      public void finishControllerInitialization() {
65      	super.finishControllerInitialization();
66          context.finishInitialization();
67      }
68  
69      /**
70       * Get context.
71       * @return get context, possibly create one
72       */
73      public final CONTEXT getContext() {
74          return context;
75      }
76  
77      /**
78       * To be overriden in children, this method enables user to instantiate
79       * primitives in any way it desires. If you keep it empty, logic will try
80       * to instantiate primitivies by their names and annotations.
81       * @param executor executor that will be used for primitives (empty, no primitive should be defined yet)
82       * @param actions set of actions used in the plan (unmodifiable)
83       * @param senses set of senses used in the plan (unmodifiable)
84       */
85      protected void customPrimitiveInstantiation(StateWorkExecutor executor, Set<String> actions, Set<String> senses) {
86          // Nothing to do here, override in children.
87      }
88  
89      /**
90       * Find class that is a state primitivie corresponding to passed action name.
91       * @param actionName name of action, should be FQN.
92       * @return class of the action
93       */
94      private Class getActionClass(String actionName) {
95          try {
96              return Class.forName(actionName);
97          } catch (ClassNotFoundException ex) {
98              throw new StateInstantiationException("Unable to find state class for action \"" + actionName + "\"", ex);
99          }
100     }
101 
102     /**
103      * Find class that is a state primitivie corresponding to passed sense name.
104      * @param senseName name of action, should be FQN.
105      * @return class of the sense
106      */
107     private Class getSenseClass(String senseName) {
108         try {
109             return Class.forName(senseName);
110         } catch (ClassNotFoundException ex) {
111             throw new StateInstantiationException("Unable to find state class for sense \"" + senseName + "\"", ex);
112         }
113     }
114 
115     /**
116      * Instantiate primitive of passed class. If there will be an error, write it into log and
117      * throw runtime exception.
118      * @param <T> Returning class of the primitive
119      * @param cls class type of primitive
120      * @return created instance.
121      */
122     private <T> T instantiatePrimitive(Class<T> cls) {
123         String name = cls.getName();
124         try {
125             Constructor primitiveConstructor = null;
126             Constructor<?>[] constructors = cls.getConstructors();
127             for (Constructor<?> constructor : constructors) {
128                 Class[] constructorParameters = constructor.getParameterTypes();
129                 if (constructorParameters.length != 1) {
130                     continue;
131                 }
132                 if (constructorParameters[0].isAssignableFrom(this.getContext().getClass())) {
133                     primitiveConstructor = constructor;
134                 }
135             }
136             if (primitiveConstructor == null) {
137                 throw new StateInstantiationException("Primitive \"" + name + "\" doesn't have a constructor that has exactly one parameter of class " + this.getContext().getClass().getName());
138             }
139             return (T) primitiveConstructor.newInstance(this.getContext());
140         } catch (InstantiationException ex) {
141             throw new StateInstantiationException("Unable to instantiate primitive \"" + name + "\" (" + ex.getMessage() + ")", ex);
142         } catch (IllegalAccessException ex) {
143             throw new StateInstantiationException("Illegal access protection for primitive \"" + name + "\"", ex);
144         } catch (IllegalArgumentException ex) {
145             throw new StateInstantiationException("Primitive \"" + name + "\" doesn't accept bot class (" + this.bot.getClass().getName() + ") in the constructor.", ex);
146         } catch (InvocationTargetException ex) {
147             throw new StateInstantiationException("Constructor of primitive \"" + name + "\" has thrown an exception (" + ex.getMessage() + ")", ex);
148         }
149     }
150 
151     @Override
152     protected StateWorkExecutor createWorkExecutor() {
153         StateWorkExecutor executor = new StateWorkExecutor(log);
154 
155         // Get names of all primitives;
156         Set<String> actions = this.getEngine().getPlan().getActionsNames();
157         Set<String> senses = this.getEngine().getPlan().getSensesNames();
158 
159         // Check that there isn't a sense with same name as action
160         for (String senseName : senses) {
161             if (actions.contains(senseName))
162                 throw new StateInstantiationException("List of senses and " + senseName);
163         }
164 
165 
166         // Use custom initialization
167         customPrimitiveInstantiation(executor, Collections.unmodifiableSet(actions), Collections.unmodifiableSet(senses));
168 
169         // Get classses associated with the names
170         for (String name : actions) {
171             if (executor.isNameUsed(name)) {
172                 throw new StateInstantiationException("Action instantiation: Primitive with name \"" + name + "\" is already in has already used in the executor.");
173             }
174             Class<IAction> cls = getActionClass(name);
175             IAction action = instantiatePrimitive(cls);
176             executor.addAction(name, action);
177         }
178         for (String name : senses) {
179             if (executor.isNameUsed(name)) {
180                 throw new StateInstantiationException("Sense instantiation: Primitive with name \"" + name + "\" is already in has already used in the executor.");
181             }
182             Class<ISense> cls = getSenseClass(name);
183             ISense sense = instantiatePrimitive(cls);
184             executor.addSense(name, sense);
185         }
186 
187         return executor;
188     }
189 
190     /**
191      * Create context for this logic controller.
192      * @return new logic controller.
193      */
194     protected abstract CONTEXT createContext();
195 }