Repository: gaia-ucm/jbt Branch: master Commit: 2d1bf37b843b Files: 170 Total size: 1.0 MB Directory structure: gitextract_1wlvd9oh/ ├── Documentation/ │ ├── Images/ │ │ └── Source/ │ │ └── Overview.odg │ ├── JBT.tex │ └── JBTBib.bib ├── JBTCore/ │ ├── .classpath │ ├── .gitignore │ ├── .project │ ├── libs/ │ │ ├── jargs.jar │ │ ├── jdom.jar │ │ ├── mmpm.jar │ │ └── sourceformatter.jar │ └── src/ │ └── jbt/ │ ├── exception/ │ │ ├── IllegalReturnStatusException.java │ │ ├── NotTickableException.java │ │ ├── SpawnException.java │ │ └── TickException.java │ ├── execution/ │ │ ├── context/ │ │ │ ├── BasicContext.java │ │ │ ├── GenericBTLibrary.java │ │ │ ├── HierarchicalContext.java │ │ │ ├── SafeContext.java │ │ │ └── SafeOutputContext.java │ │ ├── core/ │ │ │ ├── BTExecutor.java │ │ │ ├── BTExecutorFactory.java │ │ │ ├── BTLibraryFactory.java │ │ │ ├── ContextFactory.java │ │ │ ├── ExecutionTask.java │ │ │ ├── IBTExecutor.java │ │ │ ├── IBTLibrary.java │ │ │ ├── IContext.java │ │ │ ├── ITaskState.java │ │ │ ├── TaskState.java │ │ │ ├── TaskStateFactory.java │ │ │ └── event/ │ │ │ ├── ITaskListener.java │ │ │ └── TaskEvent.java │ │ └── task/ │ │ ├── composite/ │ │ │ ├── ExecutionComposite.java │ │ │ ├── ExecutionDynamicPriorityList.java │ │ │ ├── ExecutionParallel.java │ │ │ ├── ExecutionRandomSelector.java │ │ │ ├── ExecutionRandomSequence.java │ │ │ ├── ExecutionSelector.java │ │ │ ├── ExecutionSequence.java │ │ │ └── ExecutionStaticPriorityList.java │ │ ├── decorator/ │ │ │ ├── ExecutionDecorator.java │ │ │ ├── ExecutionHierarchicalContextManager.java │ │ │ ├── ExecutionInterrupter.java │ │ │ ├── ExecutionInverter.java │ │ │ ├── ExecutionLimit.java │ │ │ ├── ExecutionRepeat.java │ │ │ ├── ExecutionSafeContextManager.java │ │ │ ├── ExecutionSafeOutputContextManager.java │ │ │ ├── ExecutionSucceeder.java │ │ │ └── ExecutionUntilFail.java │ │ └── leaf/ │ │ ├── ExecutionFailure.java │ │ ├── ExecutionLeaf.java │ │ ├── ExecutionPerformInterruption.java │ │ ├── ExecutionSubtreeLookup.java │ │ ├── ExecutionSuccess.java │ │ ├── ExecutionVariableRenamer.java │ │ ├── ExecutionWait.java │ │ ├── action/ │ │ │ └── ExecutionAction.java │ │ └── condition/ │ │ └── ExecutionCondition.java │ ├── model/ │ │ ├── core/ │ │ │ └── ModelTask.java │ │ └── task/ │ │ ├── composite/ │ │ │ ├── ModelComposite.java │ │ │ ├── ModelDynamicPriorityList.java │ │ │ ├── ModelParallel.java │ │ │ ├── ModelRandomSelector.java │ │ │ ├── ModelRandomSequence.java │ │ │ ├── ModelSelector.java │ │ │ ├── ModelSequence.java │ │ │ └── ModelStaticPriorityList.java │ │ ├── decorator/ │ │ │ ├── ModelDecorator.java │ │ │ ├── ModelHierarchicalContextManager.java │ │ │ ├── ModelInterrupter.java │ │ │ ├── ModelInverter.java │ │ │ ├── ModelLimit.java │ │ │ ├── ModelRepeat.java │ │ │ ├── ModelSafeContextManager.java │ │ │ ├── ModelSafeOutputContextManager.java │ │ │ ├── ModelSucceeder.java │ │ │ └── ModelUntilFail.java │ │ └── leaf/ │ │ ├── ModelFailure.java │ │ ├── ModelLeaf.java │ │ ├── ModelPerformInterruption.java │ │ ├── ModelSubtreeLookup.java │ │ ├── ModelSuccess.java │ │ ├── ModelVariableRenamer.java │ │ ├── ModelWait.java │ │ ├── action/ │ │ │ └── ModelAction.java │ │ └── condition/ │ │ └── ModelCondition.java │ ├── tools/ │ │ └── btlibrarygenerator/ │ │ ├── ActionsAndConditionsGenerator.java │ │ ├── BTLibraryGenerator.java │ │ ├── librarygenerator/ │ │ │ ├── BTLibraryGenerationException.java │ │ │ └── BTLibraryGenerator.java │ │ ├── lowlevelgenerator/ │ │ │ ├── ActionsGenerator.java │ │ │ ├── CommonCodeGenerationUtilities.java │ │ │ └── ConditionsGenerator.java │ │ ├── modelbtgenerator/ │ │ │ ├── ModelBTGenerationException.java │ │ │ └── ModelBTGenerator.java │ │ └── util/ │ │ └── Util.java │ └── util/ │ └── Pair.java ├── JBTEditor/ │ ├── jbt.tools.bteditor/ │ │ ├── .classpath │ │ ├── .gitignore │ │ ├── .project │ │ ├── .settings/ │ │ │ └── org.eclipse.jdt.core.prefs │ │ ├── META-INF/ │ │ │ └── MANIFEST.MF │ │ ├── bteditor.product │ │ ├── build.properties │ │ ├── files/ │ │ │ └── standardNodes.xml │ │ ├── icons/ │ │ │ ├── jbt.icns │ │ │ └── jbt.xpm │ │ ├── libs/ │ │ │ ├── jdom.jar │ │ │ └── mmpm.jar │ │ ├── plugin.xml │ │ ├── plugin_customization.ini │ │ └── src/ │ │ └── jbt/ │ │ └── tools/ │ │ └── bteditor/ │ │ ├── Activator.java │ │ ├── Application.java │ │ ├── ApplicationActionBarAdvisor.java │ │ ├── ApplicationIcons.java │ │ ├── ApplicationWorkbenchAdvisor.java │ │ ├── ApplicationWorkbenchWindowAdvisor.java │ │ ├── BTCPPManager.java │ │ ├── BTXMLManager.java │ │ ├── DisableNewEditorHandler.java │ │ ├── NodesLoader.java │ │ ├── Perspective.java │ │ ├── actions/ │ │ │ ├── CheckErrorsAction.java │ │ │ ├── ClearErrorsAction.java │ │ │ ├── CollapseTreeAction.java │ │ │ ├── DialogExportAsCppAction.java │ │ │ ├── DialogLoadMMPMDomainAction.java │ │ │ ├── DialogOpenBTAction.java │ │ │ ├── EditorActionDelegate.java │ │ │ ├── ExpandTreeAction.java │ │ │ ├── ExportToCppAction.java │ │ │ ├── LoadMMPMDomainAction.java │ │ │ ├── NewBTAction.java │ │ │ ├── OpenBTAction.java │ │ │ ├── SaveBTAction.java │ │ │ └── SaveBTAsAction.java │ │ ├── editor/ │ │ │ ├── BTEditor.java │ │ │ ├── BTEditorCopyAndPasteManager.java │ │ │ ├── BTEditorIDGenerator.java │ │ │ └── BTEditorInput.java │ │ ├── event/ │ │ │ ├── ITreeModifierListener.java │ │ │ └── TreeModifiedEvent.java │ │ ├── model/ │ │ │ ├── BT.java │ │ │ ├── BTNode.java │ │ │ ├── ConceptualBTNode.java │ │ │ └── ConceptualNodesTree.java │ │ ├── util/ │ │ │ ├── DetailsDialog.java │ │ │ ├── Extensions.java │ │ │ ├── IconsPaths.java │ │ │ ├── OverlayImageIcon.java │ │ │ ├── Pair.java │ │ │ ├── StandardDialogs.java │ │ │ └── Utilities.java │ │ ├── viewers/ │ │ │ ├── BTNodeIndentifierTransfer.java │ │ │ ├── ConceptualBTNodeTransfer.java │ │ │ ├── ConceptualNodesTreeViewer.java │ │ │ └── NodeInfoViewer.java │ │ └── views/ │ │ ├── NodeInfo.java │ │ ├── NodesNavigator.java │ │ └── NodesSearcher.java │ └── jbt.tools.bteditor.feature/ │ ├── .project │ ├── build.properties │ └── feature.xml ├── LICENSE.txt ├── README.txt └── UserGuide/ ├── Images/ │ └── Source/ │ └── Overview.odg ├── UserGuide.tex ├── UserGuideBib.bib └── userguide.kilepr ================================================ FILE CONTENTS ================================================ ================================================ FILE: Documentation/JBT.tex ================================================ \documentclass[a4paper,10pt]{article} \usepackage[utf8x]{inputenc} \usepackage{graphicx} \usepackage{minted} %opening \title{\textit{Java Behaviour Trees}, a Java Framework for Creating and Running Behaviour Trees} \author{Ricardo Juan Palma Dur\'an} \begin{document} \maketitle \begin{abstract} Behaviour Trees (BTs) have gained popularity over the past few years as a tool for defining the behaviour of video games' characters. However, to the best of our knowledge, currently there is no open Java implementation of BTs. Here we present \textit{Java Behaviour Trees} (JBT), a GNU framework in Java for defining and running BTs. \end{abstract} \section{Introduction} We assume that the reader is familiar with the concept of BT. Here we are not going to explain all the details about what a BT is or how it conceptually works. Some authors have spent a lot of effort doing so, so we will just refer the reader to \cite{Millington09} for an introduction. The model we have implemented is a hybrid one, since it combines ideas from several sources. The basic underlying model is that of \cite{Millington09}, including all the task that the authors propose as well as the concept of context and libraries of behaviour trees. However, we have also added \textit{guards} to our implementation. Guards act like conditions that in some scenarios are checked in order to decide whether a task is run or not. We have also taken some ideas from \cite{AIGameDev}, specially that of having trees whose nodes are ticked only when necessary so that CPU time does not get wasted. \subsection{Model overview description} In this section we describe the JBT architecture as well as the main features that BTs have. \subsubsection{Model driven by ticks} JBT implements a BT model driven by ticks. A BT must be evaluated through ticks, so every game cycle an external caller \textit{ticks} the tree in order for the tree to update its status. A tick is just a way of giving the tree some CPU time to update their status; in particular, ticks are used to give the nodes of the tree some time to evaluate whether they have finished or not, and consequently make the tree evolve. The simplest approach to BTs driven by ticks is that of ticking the root node and then letting each node recursively tick its children according to its semantics. However, this is a very inefficient process, since in general the major part of the nodes of the tree are just waiting for their children to finish. Therefore, they should not receive ticks, since unless their children are done they will do nothing useful when receiving the tick. Therefore, in general only very few nodes should be ticked at a game cycle, and as a result JBT implements a model in which there is a list of \textit{tickable} nodes. Only the nodes in the list can be ticked. \subsubsection{Model independent from execution} When running a BT, there should be a clear distinction between the tree that is being run (the model) and how it is actually being run (the execution). For each particular behaviour, we distinguish between the \textit{Model BT} that defines it and how it is being run. The \textit{how} is what the \textit{BT Executor} does. Basically, for every entity in the game that wants to run a behaviour (Model BT), there is a BT Executor. The BT Executor takes the Model BT and processes it (without modifying it), simulating the behaviour that is represented by the Model BT. This choice implies that, apart from the Model BT, there is another type of tree, the \textit{Execution BT}. When an entity wants to execute a behaviour, the BT Executor takes the Model BT and creates an Execution BT to execute the behaviour. The BT Executor along with the Execution BT know how to run the behaviour that the Model BT represents. \subsubsection{Architecture} Figure \ref{fig:Overview} shows an overview of the proposed architecture for BTs. There is a Model BT that represents a particular behaviour. Also, there is a BT Executor for every entity that wants to run the Model BT. Each BT Executor makes use of the Model BT and builds an Execution BT that actually runs the behaviour conceptualized by the Model BT. An external Game AI ticks the BT Executors, in order for them to update the trees that they are running. \begin{figure} \centering \includegraphics[width=\textwidth]{./Images/Overview.pdf} \caption{Overview of the BT architecture} \label{fig:Overview} \end{figure} \section{Lists of nodes} At all times, the BT Executor receives ticks from an external Game AI. As a consequence, the BT Executor makes the tree it contains evolve. Not all the nodes of the tree should receive ticks. In general, the evolution of the tree depends on a very small set of nodes, which, in general, is mainly composed of leaves of the tree. In general, intermediate nodes, such as sequences or selectors, should do nothing unless they are notified by their children after finishing. As a consequence, it is obvious that only a few nodes should receive ticks. The rest should be somehow \textit{asleep}, waiting for some events to take place in order to be awakened. We have implemented an architecture where, for each BT being run, there is a list of \textit{tickable nodes} (\textit{Tickable Nodes List} from now on). Whenever the external Game AI ticks the BT Executor, the BT Executor will only tick the nodes in the Tickable Nodes List. This list is modified through out the execution of the tree, so nodes enter and leave the list depending on whether they are useful with respect to the evolution of the tree or not. Also, at all times the BT Executor knows which nodes are open (or active). A node is active if it belongs to a path from the root node to an active leaf node. Therefore, open nodes represent those that are actively involved in the current execution of the tree. They may not be tickable nodes, but the fact that they belong to a path from the root to an active leaf node means that they may receive ticks soon. This list is called the \textit{Open Nodes List}. \section{Model BT, guards and context} A Model BT is a BT that conceptually represents a behaviour. A Model BT does not have running capabilities, that is, it is just a way of defining a behaviour. An external interpreter, the BT Executor, uses the Model BT as a base to run the behaviour it embodies. A Model BT is shared by all the entities that want to run such a behaviour. By following this model, there is only one tree even though it is being actually run by several entities. The main functionality of a Model BT is very simple. The base class for a node (also called \textit{task}) is mainly as follows: \begin{minted}{java} public abstract class ModelTask{ /* The list of children of the task. */ private List children; /* The guard of the task, which may be null. */ private ModelTask guard; public ModelTask(ModelTask guard, ModelTask... children){ this.children = new Vector(); for(int i=0;i getChildren(){ return this.children; } public ModelTask getGuard(){ return this.guard; } ... } \end{minted} That is, a task is just a container of other tasks, with no running capabilities. A task's children can be accessed, as well as its guard. In this model, a behaviour tree is equivalent to a ModelTask object, that is, a ModelTask can be interpreted as the behaviour tree whose root is the ModelTask itself. There can be as many ModelTask subclasses as supported by the underlying BT model being implemented, but none of them should define running methods. A guard is represented by an ModelTask object. Initially, we defined guards as objects implementing the \textit{IGuard} interface: \begin{minted}{java} public interface IGuard{ /* Evaluates the guard within a context. */ public boolean evaluate(IContext); } \end{minted} That is, a guard was just an entity capable of being evaluated within a context, issuing a boolean value as a result. However, a more general approach is that of considering a ModelTask to be a guard. By doing so, \textit{conditions} (ModelTasks that evaluate certain attributes of the world) can be used as guards. What is more, any behaviour tree can be used as a guard. As long as the behaviour tree succeeds, the guard evaluation is interpreted as \textit{true}; if the behaviour tree fails, the guard evaluation is interpreted as \textit{false}. In general, guards will be implemented by single ModelTask nodes. For instance, if a guard has to check if the current player has enough resources to build a particular type of unit, then a subclass of ModelTask may represent such condition. This conditions may be used, not only as a guard, but also as a condition node along the tree. The main advantage of this method is that of reusability, since predefined tasks can also be used as guards. A context is defined by the \textit{IContext} interface: \begin{minted}{java} public interface IContext{ /* Sets the value of a variable. */ public void setVariable(String name, Object value); /* Gets the value of a variable (null if not found). */ public Object getVariable(String name); ... } \end{minted} We will extend this interface further in following sections. However, the previous two methods define the core of what a context should be in a behaviour tree: an large repository of variables that can be modified an accessed by tasks. \section{Execution BT and BT Executor}\label{Sub:ExecutionBTAndBTExecutor} An Execution BT is a BT capable of running a particular BT Model. The idea behind this whole architecture is that, for each ModelTask, there must be another task knowing how to run it. For instance, there could be an ExecutionSequenceTask knowing how a ModelSequenceTask behaves, or an ExecutionParallelTask knowing how to run a ModelParallelTask. \textit{ExecutionTask} is the base class for all the tasks with the ability to run. An ExecutionTask has a reference to the ModelTask it represents, so it knows what the structure of the conceptual tree is like. An ExecutionTask also has a reference to the BT Executor in charge of it. The BT Executor contains the Tickable Nodes List and the Open Nodes List. Both lists store ExecutionTasks. Initially, the BT Executor receives the Model BT it should execute. It then creates an ExecutionTask for the root node of the tree, and \textit{spawns} it. When an ExecutionTask is spawned, it recursively spawns its children according to the semantics of the particular task. The idea is that, when \textit{spawn()} is called, the path(s) containing all the tasks from the root to the active leaf nodes must be created and stored. When a task is spawned, it has to decide whether it enters the list of tickable nodes or not. Also, when a task is spawned, it is inserted into the \textit{Open Nodes List}. From then on, the BT Executor just ticks the list of tickables nodes it contains. At every tick, the tickable nodes analyse their current situation to check whether they have or have not finished. For an intermediate node, this decision in general depends on whether its children have finished or not. For leaf nodes, this decision usually depends on an external process that has to be checked for termination. When a task (intermediate or not) has finished, it should notify its parent. In general, parent (intermediate) nodes will not usually be tickables; therefore, they will not be in the Tickable Nodes List, and as a result, the only way for them to notice that they have to be updated is by being explicitly told so. Thus, when a tickable finishes, a TaskEvent is fired. Parents register as TaskEventListeners of their children (at spawning time), so whenever a child is done, the parent will immediately realize and will be able to act as a consequence. By following this pattern, only very few tasks must stay in the Tickable Nodes List: in general, only leaf nodes will receive ticks. When they are done, they will automatically fire a TaskEvent. Parents, on the other hand, will usually register as TaskEventListeners of their children, so they will receive such events and will be able to have a chance to react to the termination of their children. If the parent finishes as a consequence of the child's termination, then the parent will fire a TaskEvent to notify its parent too, and so on. The BT Executor has a very simple interface: \begin{minted}{java} public class BTExecutor{ /* The Model BT being run. */ private ModelTask modelBT; /* The list of tickable nodes. */ private List tickableList; /* The list of open nodes. */ private List openList; /* Updates the tree being run. */ public void tick(); /* Terminates the execution of the tree. */ public void terminate(); /* Returns the status of the tree being run. */ public Status getStatus(); ... } \end{minted} An ExecutionTask mainly contains a reference to the ModelTask it is running, the BTExecutor and the context (IContext), as follows: \begin{minted}{java} public abstract ExecutionTask implements ITaskListener{ /* Reference to the corresponding ModelTask. */ private ModelTask modelTask; /* The BTExecutor that is running this ExecutionTask. */ private BTExecutor executor; /* The context of the task. */ private IContext context; /* List of all the listeners of this task. */ private List listeners; /* Current status of the task. */ private Status currentStatus; /* Spawns the task and its children. */ public final void spawn(IContext context){...}; /* Ticks the task. */ public final void tick(){...} /* Terminates the task. */ public final void terminate(){...} /* Returns the current status of the task. */ public Status getCurrentStatus(){ return this.currentStatus; } /* * Adds a task listener to this task. The * task listener will be notified when an important * change in the status of this task occurs. */ public void addTaskListener(ITaskListener listener){ if(listener != null){ this.listener.add(listener); } } /* Called to fire the TaskEvent on all the listeners. */ private void fireTaskEvent(Status s){ for(ITaskListener t:this.listeners){ t.taskFinished(new TaskEvent(s)); } } ... } \end{minted} An ExecutionTask has three main methods, \textit{spawn()}, \textit{tick()} and \textit{terminate()}. These three methods define the main functionality of an ExecutionTask. They have a base definition that cannot be overriden by subclasses, and their internal implementation relies upon three abstract methods, \textit{internalSpawn()}, \textit{internalTick()} and \textit{internalTerminate()} respectively. Subclasses define their specific behaviour by implementing these abstract methods. As stated before, the standard behaviour of all ExecutionTask is implemented in the \textit{spawn()}, \textit{tick()} and \textit{terminate()} methods. Their definition is as follows: \begin{minted}{java} /* Spawn. */ public final void spawn(IContext context){ /* * Store the context. */ this.context = context; /* Set the current status of the task to Status.RUNNING. */ this.status = Status.RUNNING; /* * Request to be inserted into the list of open tasks. */ this.executor.requestInsertionIntoOpenList(this); /* * Carry out the actual spawn. */ internalSpawn(); } /* Tick. */ public final Status tick(){ Status newStatus = internalTick(); this.currentStatus = newStatus; if(this.currentStatus != RUNNING){ this.executor.requestRemovalFromTickableList(this); this.executor.requestRemovalFromOpenList(this); fireTaskEvent(newStatus); } return newStatus; } \end{minted} With respect to the \textit{terminate()} method, see section \ref{Sub:TerminatingTasks} for further explanation. As we can see, the main purpose of \textit{spawn()} is to store the context (so that in is accessible later on, when the task actually needs it) and request that the task be inserted into the list of open nodes. Finally, it calls \textit{internalSpawn()}, where the task will carry out the spawning process according to its semantics (for instance, a parallel task would spawn all of its children, while a selector would spawn just one child). This method is in charge of the following: \begin{itemize} \item It decides whether it should enter the Tickable Nodes List or not\footnote{See Section \ref{Sub:ManagingLists} for further explanation.}. For instance, an ExecutionSequenceTask should not enter the list, because the fact that it evolves or not depends on the termination of its children (therefore, only its children should enter the list). However, low level actions or tasks such as DynamicPriorotyList should. It is very important to note that if the spawning process of a task fails, it should enter the Tickable Nodes List in order for it to notify its parent at the next AI cycle. A DynamicPriorityList, for instance, may fail in case no valid guard is found, in which case it should be ticked at the next AI cycle so as to notify its parent. \item According to the semantics of the node, it recursively spawns none, one or several of its children. For instance, an ExecutionSequenceTask should spawn the very first node of its list of children. An ExecutionParallelTask should spawn all of its children. On the contrary, a low level task (leaf node) cannot spawn any child since it has none. \item In case of a low level task, \textit{internalSpawn()} should start the execution of the process associated to the node. Keep in mind that low level tasks may perform long processes that require several ticks in order to complete. It is in this method that those processes start (maybe in independent threads). It should be noted, however, that many processes may be \textit{instantaneous}, so they may complete even within the \textit{internalSpawn()} method. Nevertheless, in these cases the tree should not evolve, reason why the termination notification to its parent should be carried out in the \textit{tick()} method, in the next AI cycle. If \textit{internalSpawn()} were allowed to notify parents when the node terminated, then a single call to \textit{spawn()} may take too long to complete due to the uninterrupted evolution of the tree, which is something that has to be avoided. \end{itemize} On the other hand, \textit{tick()} calls the \textit{internalTick()} method, where the task will carry out the actual ticking process. Then, if the task has finished after calling it, a TaskEvent will be fired to notify all the listeners that are registered with the task, and it requests to be removed from the Tickable Nodes List and the Open Nodes List. It is by this mechanism that parents are notified about their children termination, and can act accordingly. \textit{internalTick()} is the method that actually performs the tick. In general, this method will analyse the current status of the children of the task (\textit{ExecutionTask.getStatus()}), and will evolve accordingly. For instance, when an ExecutionSequenceTask receives a TaskEvent from its currently active child, its \textit{tick()} method gets called, and the following is done (in \textit{internalTick()}): \begin{itemize} \item If the child has succeeded, the ExecutionSequenceTask will spawn the next child in case there is one, and will return RUNNING. In case it is the last child, it will return SUCCESS. \item If the child has failed, the ExecutionSequenceTask will also fail, returning FAILURE. \end{itemize} The ITaskListener interface represents an interface for listening to events fired when there are important changes in the status of a task. \begin{minted}{java} public interface ITaskListener{ /* * Called whenever there is a change in * the status of a task. */ public void statusChanged(TaskEvent e); } \end{minted} In general, the method \textit{statusChanged()} will just call \textit{tick()} on the node itself. By doing so, the node will be able to advance, either by spawning other children or by firing a TaskEvent to its parent to make it go on too. \section{Creating ExecutionTasks from ModelTasks} ExecutionTasks must be created for ModelTasks. In order to make this process easier, it can be delegated to ModelTasks. When an ExecutionTask needs to create the ExecutionTask of one of its children, it can ask the corresponding ModelTask to do so. Therefore, ModelTasks define a new method, \begin{minted}{java} /* * Creates an ExecutionTask that is able to run * this ModelTask. "executor" is the BTExecutor * managing the ExecutionTask that will be created. */ public abstract ExecutionTask createExecutor(BTExecutor executor); \end{minted} This method should return an ExecutionTask capable of running the corresponding ModelTask. Keep in mind that the returned ExecutionTask must have a reference to the ModelTask that created it. For instance, in case of a ModelSequenceTask, it should do something like: \begin{minted}{java} public ExecutionTask createExecutor(BTExecutor executor){ return new ExecutionSequenceTask(this, executor); } \end{minted} By following this pattern, it would be ModelTasks that would be in charge of creating ExecutionTasks through \textit{createExecutor()}. \section{Managing the Tickable Nodes List and the Open Nodes List}\label{Sub:ManagingLists} When tasks are being ticked, there may be a problem accessing the Tickable Nodes List. The algorithm followed by the BTExecutor is pretty simple: \begin{minted}{java} public class BTExecutor{ ... public void tick(){ if(this.firstTimeCalled){ ExecutionTask root = this.modelBT.createExecutor(); root.spawn(); this.firstTimeCalled = false; } else{ for(ExecutionTask t:this.tickableList){ t.tick(); } } ... } ... } \end{minted} That is, it just ticks all the nodes in the Tickable Nodes List. However, while the list is being ticked, some nodes may need to leave the list, so the iteration through its elements may be left in an inconsistent state. Therefore, when a node wants to enter or leave the list, it does not \textit{immediately} do it. Instead, it \textit{requests} to be inserted or removed. When the BTExecutor has ticked all the nodes, it processes these requests, so the code above should finally be something like: \begin{minted}{java} public class BTExecutor{ ... public void tick(){ if(this.firstTimeCalled){ ExecutionTask root = this.modelBT.createExecutor(); root.spawn(); this.firstTimeCalled = false; } else{ for(ExecutionTask t:this.tickableList){ t.tick(); } } processRemovalsAndInsertions(); } ... } \end{minted} In order to prevent the Open Nodes List from failing the same way, insertions and removals from it are also requested and delayed instead of being immediately carried out. \section{Terminating tasks}\label{Sub:TerminatingTasks} In certain cases, tasks must be abruptly terminated. This happens, for instance, when a parallel task realizes that one of its children has failed. In such a case, it has to immediately stop the other children. Another example is when a whole tree needs to be terminated, because it just does not make sense to run it any more. As we can see, there are several scenarios in which it is necessary to have the ability to stop individual tasks or branches of a tree. This is the purpose of the method \textit{terminate()}, first introduced in the section \ref{Sub:ExecutionBTAndBTExecutor}. As a consequence of these requirements, BTExecutor and ExecutionTask need to be extended, so both include a \textit{terminate()} method. In the case of the BTExecutor, its implementation is trivial: it will just terminate the tree that it is running. However, the \textit{terminate()} method in the ExecutionTask class is more complicated. Since the termination process of an ExecutionTask varies according to the specific type, it is an final method whose implementation relies on the abstract \textit{internalTerminate()} method, which carries out the actual termination process. What \textit{terminate()} does is to request that the task be removed from both the list of tickable and open nodes, sets the status of the task to \textit{terminated}, and finally calls \textit{internalTerminate()}. For non-leaf tasks, \textit{internalTerminate()} usually calls \textit{terminate()} on their active children, in a recursive manner. For leaf tasks, it will stop and free all the processes and resources belonging to the task. The problem about terminating tasks is that they must not leave the list of tickable nodes until the next game AI cycle (that is, until \textit{BTExecutor.tick()}) is called again. This is due to the fact that the list of tickable nodes may be being ticked just when a task is terminated. If the terminated task(s) were removed from the list, then it would be left in an inconsistent state and the BTExecutor would have problems ticking all of its tasks. It is for this reason that when a task is requested to terminate itself, the task does not immediately remove itself from the Tickable Nodes List, but requests it. Now, the problem is that the BTExecutor could tick tasks that have been terminated, since they are not removed from the Tickable Nodes List until the current AI cycle finishes.In order to prevent from any problem, the \textit{ExecutionTask.tick()} method is modified so that, if the task has been terminated, it does nothing. \begin{minted}{java} public abstract ExecutionTask implements ITaskListener{ ... public Status tick(){ if(!this.terminated){ Status newStatus = internalTick(); this.currentStatus = newStatus; if(this.currentStatus != RUNNING){ requestRemovalFromTickableList(this); requestRemovalFromOpenList(this); fireTaskEvent(newStatus); } return newStatus; } else{ return Status.TERMINATED; } } ... \end{minted} The \textit{ExecutionTask.terminate()} method is mainly implemented like this: \begin{minted}{java} public abstract ExecutionTask implements ITaskListener{ ... public void terminate(){ if(!this.terminated){ requestRemovalFromOpenList(); requestRemovalFromTickableList(); this.terminated = true; this.currentStatus = Status.TERMINATED; internalTerminate(); } } ... } \end{minted} That is, the \textit{terminate()} method requests to remove the task from both the Open Nodes List and the Tickable Nodes List. Also, it activates a flag to indicate that the task has been terminated. Finally, it calls the abstract method \textit{ExecutionTask.internalTerminate()} which will actually terminate the task. \textbf{It must be noted that the termination process must be carefully handled}. When a task is terminated, it immediately becomes totally unresponsive, so ticking it will have no effect; also, at the next game AI cycle, it will be removed from the list of tickable nodes. As a result, its parent will never be notified about its child termination -it will not receive a TaskEvent-, and it will never be able to evolve. Therefore, when a task is terminated, its parent should enter the list of tickable nodes so that at the next game AI cycle it can receive ticks and realizes that its child has finished (been terminated). This is for example what the ExecutionInterrupter does when it is interrupted: it terminates its child and then requests to be inserted into the list of tickable nodes. Even though it may not be necessary to always insert the parent task into the list of tickable nodes, it should be kept in mind that terminating tasks is a very delicate process that must be properly handled. \section{General process} So far we have described a general architecture to model and run behaviour trees. Despite the fact that there are still many details to be taken care of, we can give an overview of the whole process of managing a behaviour tree: \begin{itemize} \item A Model BT is created. The Model BT represents the behaviour that must be run, but it cannot be executed by itself. The tree would be implemented by a ModelTask. \item A BTExecutor is created. The BTExecutor will contain a reference to the ModelTask that must be run. \item From then on, \textit{BTExecutor.tick()} is called in order to run the tree. At every game cycle, \textit{tick()} is called. The external caller should not worry about anything else; it will be the BTExecutor that will handle the execution of the BT. \begin{itemize} \item The very first time \textit{tick()} is called, the BTExecutor takes the root node of the Model BT, and creates the corresponding ExecutionTask node, which is spawned. This is done by calling \textit{ModelTask.createExecutor()}, and then calling \textit{ExecutionTask.spawn()} on the returned ExecutionTask. \item From then on, every time \textit{BTExecutor.tick()} is called, the BTExecutor will just tick the nodes in the Tickable Nodes List. \end{itemize} \item Whenever a task finishes, it fires an event to inform its parent. The parent will then either report success or failure (depending on whether the child failed or succeeded) or spawn a new child. If the parent also finishes, it will fire a new event to its parent, in a recursive manner. \end{itemize} \section{Actions and conditions (leaf nodes)} Actions and conditions, that is, leaf nodes in the BT, must be somehow specified. At design level, the BT is designed including all the low level actions and conditions that the tree is supposed to run, but in an abstract manner. On the implementation side, however, there must be an ExecutionTask that knows how to run each leaf node. Since these tasks are outside the framework, a way of defining their behaviour must be provided. The framework provides two abstract classes, \textit{ModelAction} and \textit{ModelCondition}, so every low level action and condition must subclass them, \begin{minted}{java} public abstract class ModelAction extends ModelTask{ ... } \end{minted} \begin{minted}{java} public abstract class ModelCondition extends ModelTask{ ... } \end{minted} For instance, a low level action \textit{MoveTo} would be defined like this: \begin{minted}{java} public class MoveTo extends ModelAction{ ... } \end{minted} ModelTasks and ModelConditions also define a \textit{createExecutor()} method that must return the corresponding ExecutionTask to run the action or condition. The designer should define the \textit{internalSpawn()}, \textit{internalTick()} and \textit{internalTerminate()} methods for these ExecutionTasks. For instance, lets suppose that the designer has created a BT with a low level action called \textit{MoveTo}. Such action would be represented by two classes: \begin{minted}{java} public class ModelMoveTo extends ModelAction{ ... public ExecutionTask createExecutor(BTExecutor executor){ return new ExecutionMoveTo(this, executor); } } \end{minted} \begin{minted}{java} public class ExecutionMoveTo extends ExecutionAction{ ... public void internalSpawn(){ /* TODO: designer should implement this method! */ } public Status internalTick(){ /* TODO: designer should implement this method! */ return Status.SUCCESS; } public void internalTerminate(){ /* TODO: designer should implement this method! */ } } \end{minted} So the designer should only implement the \textit{internalSpawn()}, \textit{internalTick()} and \textit{internalTerminate()} methods. In the case of the MoveTo action, \textit{internalSpawn()} would send the unit the move order through the game engine. \textit{internalTick()} would check whether the unit has arrived at its destination. If so, it would return \textit{Status.SUCCESS}; otherwise, it would return \textit{Status.RUNNING}. \section{Evaluation order} The problem with having a Tickable Nodes List is that the order in which nodes are ticked does not necessarily correspond to that of an ideal BT. Suppose for example that we have a BT with a DynamicPriorotyList (DPL) task, and some of its children are currently running. Suppose an scenario in which, if the active child is ticked, it finishes successfully, but at the same time, if the DPL is ticked, it realizes there is a child with higher priority that must be run. Ideally, the DPL is ticked first, so it terminates the currently active child and spawns the child with higher priority. However, if in the Tickable Nodes List the currently active child is placed before the DPL node, it will receive a tick first. In the presented scenario, the child would finish with success, the DPL would be informed, and as a result, it would terminate without spawning the child with higher priority. Moreover, the child task would succeeds while in the ideal BT it would not. This problem can be fixed by forcing an order in the Tickable Nodes List. In particular, if parent nodes always precede their children at ticking time, then this problem is fixed, since by doing so the ideal evaluation order of a BT is emulated. \section{Subtree Lookup and IContext}\label{sec:SubtreeLookupAndIContext} One of the most important features of behaviour trees is that of reusability. In the context of behaviour trees it is very important to notice the way that parent tasks interact with their children: parents do not care about what their children are; in JBT, on the conceptual side, this can be seen in the fact that ModelTasks always have other ModelTasks as children, and it is through its interface that they interact, no matter the particular subtype. On the execution side, ExecutionTasks always have other ExecutionTasks as their children, and it is also through its interface that they interact, no matter the particular subtype. A behaviour tree is represented by its root ModelTask (with its corresponding ExecutionTask). As a consequence, a behaviour tree task interacts with a single child task just the same way it interacts with a complete behaviour tree (that is, with the root of the tree). This property is specially important with respect to reusability: the designer labels trees with a name, and then at some point in whatever tree he is building, decides to reuse a particular tree -identifier by its name-. By following this philosophy, the effort of creating trees decreases dramatically. The ability to reuse trees by name is implemented by the Subtree Lookup task. The ModelSubtreeLookup task receives, as an input argument in its constructor, the name of the subtree that it has to emulate. When it gets spawned, it will emulate the behaviour of the tree it is expected to emulate. The ExecutionTask that is able to run a ModelSubtreeLookup is the ExecutionSubtreeLookup class. This class, however, faces a problem when it tries to emulate the tree whose name was initially given: even though it knows the name that identifies the tree to emulate, it does not know where to find such tree (that is, it does not know where the root ModelTask of such tree is). Thus, there must be a mechanism through which the ExecutionSubtreeLookup can find the tree it is expected to emulate. This mechanism is implemented via the IContext interface. The IContext interface is extended with a new method: \begin{minted}{java} /* Returns a behavior tree by name. */ public ModelTask getBT(String name); \end{minted} Given the name of the tree to retrieve, \textit{getBT()} returns the root of such tree. Note that users of the IContext interface do not care about where the trees it provides come from; it only cares about retrieving trees by name, which is exactly that the IContext interface exposes. \section{Behaviour trees libraries} Behaviour trees are identified by names that somehow summarize what they do. Take for example a character that is expected to behave aggressively. Such character could use a behaviour tree with name ``AggressiveBehaviour''. In general, behaviour trees are identified by name, so there must be a way of retrieving behaviour trees given a name. This is what a behaviour tree library represents: a repository of trees that can be accessed. In JBT, a behaviour trees library is represented by the \textit{IBTLibrary} interface. The IBTLibrary interface defines a single method that returns a tree with a given name. It is defined as follows: \begin{minted}{java} public interface IBTLibrary extends Iterable> { /* Returns a behaviour tree given its name. */ public ModelTask getBT(String name); } \end{minted} One important detail about the IBTLibrary is that it implements the \textit{Iterable} interface. By doing so, it allows its users to retrieve all the trees it contains. In particular, for each tree, it retrieves both the tree itself (the root ModelTask of the tree) and its name (as a String). \section{Persistent tasks} Some tasks in BTs are persistent in the sense that, after finishing, if they are spawned again, they remember past information. Take for example the Limit task. A Limit task allows to run its child node only a certain number of times (for example, 5). After being spawned, it has to remember how many times it has been run so far, so that, once the threshold is exceeded, it fails. In the simple model of BT there is no problem about it, because nodes are objects that exist from the beginning to the end, so they can remember whatever information they want. However, in the model we propose, nodes (ExecutionTask objects) are destroyed once they finish (they leave the Tickable Nodes List and whatever other lists they may be in), so a new mechanism must be implemented for keeping past information. We propose to use the BTExecutor with that purpose. For instance, if a Limit task needs to know that it has been run 3 times so far, just before being destroyed it may write into the BTExecutor that number (3). The next time it is spawned, it may read, from the BTExecutor, that information. All the persistent information of a task is saved into a \textit{ITaskState} object. The ITaskState interface represents a collection of variables that can be accessed: \begin{minted}{java} public interface ITaskState { /* Returns the value of a state variable by its name. */ public Object getStateVariable(String name); } \end{minted} When the tasks needs to store its state for future use, it will have to create an ITaskState object containing all the information it wishes to keep. On the other hand, when the task needs to restore its previous state, it will receive an ITaskState object from which it will have to retrieve the needed information. We will see this in a moment, but first we have to address an important problem: who is in charge of storing the ITaskState objects of the tasks of a tree? Moreover, by what mechanism can we unambiguously assign an ITaskState to a node of the tree, so that the proper ITaskState object is delivered to the proper ExecutionTask? Both questions are in fact related. An effective mechanism to assign ITaskState objects to tasks is by using the position of the task in the tree. Each ExecutionTask in the tree has got a position that is constant all over the execution of the tree, no matter how the tree evolves. Thanks to this we can assign ITaskState objects to positions in the tree; since a position in the tree corresponds to one, and only one ExecutionTask, such ITaskState will be that of the ExecutionTask pointed by the position. The first question is a tricky one. Initially it could be thought that the context may be used; however, the fact that one context can also be used by guards (which are actually behaviour trees run each one by its own BTExecutor), makes it impossible to do so, because it could lead to clashes between positions of the main tree and those of the guards. Once the context has been discarded, the only alternative left is the BTExecutor. The BTExecutor suffices for this task, since, among other things, it is shared by all the nodes of the tree except for those of guards' trees, thus avoiding position clashes. As a result, the BTExecutor class is extended to support the storage of ITaskState objects: \begin{minted}{java} /* * Sets the state of the task placed at a particular * position in the tree. */ public boolean setTaskState(Position taskPosition, ITaskState state); /* * Retrieves the state of the task placed at a particular * position in the tree. */ public ITaskState getTaskState(Position taskPosition); /* * Clears the state of the task placed at a particular * position in the tree. */ public boolean clearTaskState(Position taskPosition); \end{minted} In order to facilitate the persistence of tasks to the designer, we can modify both the \textit{spawn()} and \textit{tick()} method so that they automatize this process. \begin{minted}{java} public final void spawn(IContext context){ /* * Store the context. */ this.context = context; /* Set the current status of the task to Status.RUNNING. */ this.status = Status.RUNNING; /* * Request to be inserted into the list of open tasks. */ this.executor.requestInsertionIntoOpenList(this); /* Restore the previous state. */ ITaskState previousState = this.executor.getTaskState(); restoreState(previousState); /* * Carry out the actual spawn. */ internalSpawn(); } \end{minted} Where it must be noted the new \textit{restoreState(ITaskState)} method. This is an abstract method that must be implemented by subclasses, and its purpose is to restore the state of an ExecutionTask given the set of persistent information (ITaskState object) that was previously issued by the task. For the major part of tasks, this method will do nothing, so it will have an empty implementation. However, for tasks such as Limit, it will retrieve past information from the ITaskState object to properly restore the task. If the ITaskState object is null, it means that there is no past information, so the method should do nothing. For instance, the very first time that a Limit task is spawned it cannot retrieve past information, so \textit{restoreState()} should do nothing. The \textit{tick()} method should also be modified to automatically store into the BTExecutor the persistent state information that \textit{restoreStatus()} needs to restore the task: \begin{minted}{java} /* Tick. */ public Status tick(){ if(!this.terminated){ Status newStatus = internalTick(); this.currentStatus = newStatus; if(this.currentStatus != RUNNING){ ITaskState taskState = storeState(); this.executor.setTaskState(getPosition(), taskState); requestRemovalFromTickableList(this); requestRemovalFromOpenList(this); fireTaskEvent(newStatus); } return newStatus; } else{ return Status.TERMINATED; } } \end{minted} Also here must be noted the new abstract method \textit{storeState()}. This method is implemented by each subclass, and it returns an ITaskState object containing all the information that must be made persistent for future use. This method must issue an ITaskState object compatible with the \textit{restoreState()} method, so that the ITaskState objects that the former issues can be understood by the latter. If no persistent information had to be stored for the task, null should be returned. The \textit{terminate()} method is also extended with a call to the \textit{storeTerminationState()}, which is a method equivalent to \textit{storeState()} but that is called only when the task is terminated. In short, \textit{storeState()} creates an ITaskState object that contains all the persistent information that the task may want to use in the future. That information is read by the \textit{restoreState()} method. \section{Native tasks}\label{sec:NativeTasks} JBT offers a wide range of tasks that can be used to build behaviour trees. JBT basically implements the BT model described in \cite{Millington09}, but extended with guards. JBT supports the following tasks: \begin{itemize} \item Composite tasks: tasks with one or more children, whose execution depends on the execution of their children. The task's children are ordered. \begin{itemize} \item Sequence: task that sequentially executes all its children in order. If one fails, the Sequence task fails. If all succeeds, the Sequence task succeeds. \item Selector: task that sequentially executes all its children in order. If one succeeds, the Selector task succeeds. If all fail, the Selector task fails. \item Parallel: task that concurrently executes all its children. A Parallel task does have a \textit{parallel policy}. If the parallel task's policy is \textit{sequence}, the parallel fails if one child fails; if all succeed, then the parallel succeed. If the parallel task's policy is \textit{selector}, the parallel fails if all its children fail. If one succeeds, then the parallel also succeeds. \item Random Selector: task that executes all its children in a random order. If one fails, the Sequence task fails. If all succeeds, the Sequence task succeeds. \item Random Sequence: task that sequentially executes all its children in random order. If one succeeds, the Selector task succeeds. If all fail, the Selector task fails. \item Dynamic Priority List: task that executes the child with the highest priority whose guard is evaluated to true. At every AI cycle, the children's guards are re-evaluated, so if the guard of the running child is evaluated to false, it is terminated, and the child with the highest priority starts running. The Dynamic Priority List task finishes when no guard is evaluated to true (thus failing) or when its active child finishes (returning the active child's termination status). \item Static Priority List: task that executes the child with the highest priority whose guard is evaluated to true. Unlike the Dynamic Priority List, the Static Priority List does not keep evaluating its children's guards once a child is spawned. The Static Priority List task finishes when no guard is evaluated to true (thus failing) or when its active child finishes (returning the active child's termination status). \end{itemize} \item Decorator tasks: tasks with one child whose purpose is to alter the way other tasks behave. \begin{itemize} \item Interrupter: task that controls the termination of its child task. An Interrupter simply lets its child task run normally. If the child returns a result, the Interrupter will return it. However, the Interrupter can be asked to terminate the child task and return an specified status when done so. \item Inverter: task used to invert the status code returned by its child. When the decorated task finishes, its status code gets inverted. \item Limit: task that limits the number of times a task can be executed. This decorator is used when a task (the child of the decorator) must be run a maximum number of times. When the maximum number of times is exceeded, the decorator will fail forever on. \item Repeat: task that runs its child task forever. When its child task finishes, it runs it once more. \item Until Fail: task that runs its child as long as it does not fail. When the child task fails, Until Fail succeeds. \item Hierarchical Context Manager: task that creates a new context for its child. The context that it creates is a Hierarchical Context. \item Safe Output Context Manager: task that creates a new context for its child. The context that it creates is a Safe Output Context. \item Safe Context Manager: task that creates a new context for its child. The context that it creates is a Safe Context. \end{itemize} \item Leaf tasks: tasks with no children. \begin{itemize} \item Wait: task that keeps running for a period of time, and then succeeds. The user can specify for how long the Wait task should be running. \item Subtree Lookup: the Subtree Lookup described in section \ref{sec:SubtreeLookupAndIContext}. \item Perform Interruption: task that interrupts an Interrupter task. \item Variable Renamer: task that renames a variable in the context. \item Success: task that immediately succeeds. \item Failure: task that immediately fails. \item Action: generic action that is executed in the game engine. \item Condition: generic condition that is executed in the game engine. \end{itemize} \end{itemize} \section{Automatic behaviour trees generation} So far, JBT offers the main classes for building and executing behaviour trees. The user needs to construct a ModelTask object representing the tree that he wants to run, and then create a BTExecutor to run it. This philosophy of creating behaviour trees, however, lacks of reusability, and can be very time consuming. First of all, the user must explicitly create the Java structure of all the behaviour trees, which can be very time consuming for very complex ones. It would be nice for the user to be able to define behaviour trees in a standard XML format that then may be directly translated into the corresponding JBT Java classes. By doing so, the user should not have to deal directly with the JBT Java classes, and as a result it would be much easier for him to define behaviour trees. On the other hand, the user must create and implement all the classes that represent low level actions and conditions. These are classes that extend the \textit{ModelAction}, \textit{ExecutionAction}, \textit{ModelCondition} and \textit{ExecutionCondition} classes. To ease this task, the user may define actions and conditions in a standard XML format so that they may be directly translated into the corresponding JBT Java classes. However, since the way a low level action and condition works is not known by JBT, the programmer would still be in charge of defining their abstract methods so that JBT could run them as expected. JBT offers two tools that do these two tasks. \subsection{Low level actions and conditions generation} JBT lets the user define, in a XML format, the set of actions and conditions that will be used to generate the skeleton of the corresponding JBT Java classes. The XML format that JBT understand is an extension of that of Make Me Play Me (MMPM) domain files\footnote{MMPM does support several action parameter types; our extension just adds the type "OBJECT", which represents a generic Java Object.}. A MMPM domain file contains a set of actions, sensors, goals and entities. As far as JBT is concerned, it only needs the set of actions and sensors. For each action in the domain file, JBT is able to generate the corresponding ModelAction and ExecutionAction classes. Also, for each boolean sensor in the domain file, JBT is able to generate the corresponding ModelCondition and ExecutionCondition classes. The generated ModelAction and ModelCondition classes are complete, so they do not need to be modified. However, the ExecutionAction and ExecutionCondition generated classes contain a set of abstract method that must be implemented according to the semantics of the respective actions and conditions, so that they do what they are expected to do. In particular, the abstract methods that must be implemented are: \begin{minted}{java} protected void internalSpawn(); protected Status internalTick(); protected void internaTerminate(); protected void restoreState(ITaskState state); protected ITaskState storeState(); protected ITaskState storeTerminationState(); \end{minted} MMPM domain files also let the designer specify input parameters for actions and sensors. These are parameters that actions and sensors are supposed to use when running. The generated JBT ExecutionAction and ExecutionCondition classes include \textit{getter} methods for such input parameters. As a result, the programmer will be able to access the value of the input parameters in the abstract methods he has to implement, by using the getter methods. The value for the input parameters are either retrieved from the execution context(IContext) or directly provided at construction time, but these details are hidden from the programmer that implements the action or condition (he should just use the getter methods to retrieve whatever input parameters may be needed). The class \textit{ActionsAndConditionsGenerator.java} implements the program that performs this generation process. \subsection{Behaviour trees libraries generation} JBT lets the user define behaviour trees in a standard XML format. Once he has defined several trees in different files, JBT can parse these files and produce a behaviour tree library class (class implementing the IBTLibrary interface) that the programmer can use to retrieve the corresponding behaviour trees and run them. The class \textit{BTLibraryGenerator.java} implements the program the performs this generation process. \subsubsection{Behaviour tree XML format} In this section we explain the format the user must follow to define behaviour trees in XML files. A behaviour tree has a root tag, $$, within which all the tree is defined. The $$ element has only one child, which is the root node of the tree. \begin{minted}{xml} ... \end{minted} A $$ has three attributes, an "id", a "name" and a "type". "id" and "type" are mandatory for all nodes. "name", however, is not. There cannot be two nodes with the same "id" in the same tree\footnote{This is not totally true. Guards are an exception. Keep reading for a further explanation.}. The root node of the tree has type "Root". For instance: \begin{minted}{xml} ... \end{minted} A $$ has three types of child element. One of them is the $$ element, which stores the list of children of the node. $$ just contains a sequence of $$ elements. For instance: \begin{minted}{xml} ... ... ... \end{minted} Leaf tasks of the tree do not have any child task, so $$ is not use there. The second type of child of $$ is the $$ element. $$ just stores the list of parameters of the task, as well as their value. Each parameter "name" and its value is stored in a $$ element. For instance, this is the set of parameters of a particular task: \begin{minted}{xml} 34 434 HydraliskID \end{minted} Where we can see three parameters, "preFailureTime", "failureTime" and "target" and their corresponding values, "34", "434" and "HydraliskID". A $$ element also has an attribute, "fromcontext", that can be either "true" or "false". If "false", the value of the parameter is read directly from the $$ element. If "true", the value in the $$ element represents the location, in the context, where the value of the parameter can be found. For instance, a parameter like this: \begin{minted}{xml} 34 \end{minted} means that the value of the parameter "preFailureTime" is 34, while this: \begin{minted}{xml} preFailureTimeLocation \end{minted} means that the value of the parameter "preFailureTime" can be found in the context, in the place referenced by the string "preFailureTimeLocation". Among other things, the "fromcontext" attribute determines the way that the getter methods of the automatically generated low level actions and conditions behave. If "true", the getter method will retrieve the parameter from the context (\textit{IContext}). Otherwise, its value will be known in advance, so it will be easily retrieved. The $$ child of $$ is optional, depending on whether the respective task does or does not have parameters. For instance, a Sequence node does not have parameters, but a Wait node does. The third type of child of $$ is the $$ element. A $$ only contains one $$ element, which represents the guard. A $$ may have no guard, in which case the $$ element is not used. With respect to guards, there is an important detail about their identifiers that must be noted. In general, there cannot be two different nodes with the same ID in the same tree. However, guards are an exception. Guards are actually considered independent trees, so the ID of a guard's node can match the ID of a node of the tree that contains the guard. For instance, if a tree $A$ has a node with ID $ID\_1$, a node of a guard of an $A$'s node may have $ID\_1$ as ID, and there would be no conflict at all, since $A$ and any of $A$'s guards are considered to be independent trees. This somehow restricts the way that nodes can interact with each other. For instance, if a node in $A$ refers to a node with ID $ID\_23$, the node with $ID\_23$ must be placed in $A$ and not in any of the guards that the nodes of $A$ may have. The Perform Interruption is a good example of this, since it has a parameter that stores the ID of the node that it has to interrupt. This restriction may be fixed in the future. There are several standard types of node that are used when building a behaviour tree (one for each native task supported by JBT -see section \ref{sec:NativeTasks}-). These nodes have a fixed syntax and well established semantics. They are: \begin{itemize} \item Sequence: \begin{itemize} \item "type" attribute has value "Sequence". \item It does not have parameters. \item It does have children. \end{itemize} \item Selector: \begin{itemize} \item "type" attribute has value "Selector". \item It does not have parameters. \item It does have children. \end{itemize} \item Random Sequence: \begin{itemize} \item "type" attribute has value "RandomSequence". \item It does not have parameters. \item It does have children. \end{itemize} \item Random Selector: \begin{itemize} \item "type" attribute has value "RandomSelector". \item It does not have parameters. \item It does have children. \end{itemize} \item Parallel: \begin{itemize} \item "type" attribute has value "Parallel". \item It does have one parameter, named "policy", whose value may be either "selector" or "sequence". Its value cannot be retrieved from the context. For instance: \begin{minted}{xml} selector ... \end{minted} \item It does have children. \end{itemize} \item Dynamic Priority List: \begin{itemize} \item "type" attribute has value "DynamicPriorityList". \item It does not have parameters. \item It does have children. \end{itemize} \item Static Priority List: \begin{itemize} \item "type" attribute has value "StaticPriorityList". \item It does not have parameters. \item It does have children. \end{itemize} \item Hierarchical Context Manager: \begin{itemize} \item "type" attribute has value "HierarchicalContextManager". \item It does not have parameters. \item It does have one child. \end{itemize} \item Safe Output Context Manager: \begin{itemize} \item "type" attribute has value "SafeOutputContextManager". \item It does not have parameters. \item It does have one child. \end{itemize} \item Safe Context Manager: \begin{itemize} \item "type" attribute has value "SafeContextManager". \item It does not have parameters. \item It does have one child. \end{itemize} \item Interrupter: \begin{itemize} \item "type" attribute has value "Interrupter". \item It does not have parameters. \item It does have one child. \end{itemize} \item Inverter: \begin{itemize} \item "type" attribute has value "Inverter". \item It does not have parameters. \item It does have one child. \end{itemize} \item Limit: \begin{itemize} \item "type" attribute has value "Limit". \item It does have one parameter, named "runs", whose value can be any integer greater than 0. Its value cannot be retrieved from the context: \begin{minted}{xml} 12 ... \end{minted} \item It does have one child. \end{itemize} \item Repeat: \begin{itemize} \item "type" attribute has value "Repeat". \item It does not have parameters. \item It does have one child. \end{itemize} \item Until Fail: \begin{itemize} \item "type" attribute has value "UntilFail". \item It does not have parameters. \item It does have one child. \end{itemize} \item Perform Interruption: \begin{itemize} \item "type" attribute has value "PerformInterruption". \item It does have two parameters: \begin{itemize} \item One is named "expectedresult", and its value can be either "success" or "failure". Its value cannot be retrieved from the context. \item The other one's name is "nodeid", and its value must be the identifier of a node in the tree. Its value cannot be retrieved from the context. \begin{minted}{xml} failure NODE_YYY \end{minted} \end{itemize} \item It does not have any children. \end{itemize} \item Subtree Lookup: \begin{itemize} \item "type" attribute has value "SubtreeLookup". \item It does have one parameter, named "subtreename", whose value is any string. Its value cannot be retrieved from the context: \begin{minted}{xml} MoveTo ... \end{minted} \item It does not have any children. \end{itemize} \item Wait: \begin{itemize} \item "type" attribute has value "Wait". \item It does have one parameter, named "duration", whose value must be an integer greater or equal than 0. Its value cannot be retrieved from the context: \begin{minted}{xml} 1500 \end{minted} \item It does not have any children. \end{itemize} \item Variable Renamer: \begin{itemize} \item "type" attribute has value "VariableRenamer". \item It does have two parameters: \begin{itemize} \item One is named "variableName", whose value must be a string. Its value cannot be retrieved from the context. \item The other one's name is "newVariableName", and its value is a string. Its value cannot be retrieved from the context. \begin{minted}{xml} VariableToRename NewName \end{minted} \end{itemize} \item It does not have any children. \end{itemize} \item Success: \begin{itemize} \item "type" attribute has value "Success". \item It does not have parameters. \item It does not have any children. \end{itemize} \item Failure: \begin{itemize} \item "type" attribute has value "Failure". \item It does not have parameters. \item It does not have any children. \end{itemize} \end{itemize} With respect to low level actions and conditions, they are represented by $$ elements whose "type" attribute is "Action" and "Condition" respectively, and whose parameter "name" specifies the concrete type of action and condition. \subsection{JBT Editor} Even though JBT is able to parse behaviour trees in the XML format defined above and generate the corresponding Java classes that can be run, for the user it is still a complicated task to define complex behaviour trees in such a format. JBT provides the \textit{JBT Editor} (see a screenshot in figure \ref{fig:JBTEditor}), a program that can be used to define behaviour trees through a graphical user interface in a very user-friendly way. These behaviour trees can be then exported into the XML format defined above, so that they can be parsed by JBT to create the corresponding behaviour trees libraries. By using the JBT Editor, the user does not have to deal with XML files directly; also, it checks the correctness of the trees, so whatever trees are stored into XML files, they are correct and can be parsed by JBT with no problems. JBT also lets the user load MMPM domain files and use its actions and conditions in the trees the user creates, as well as specify values for their input parameters and check their correctness. \begin{figure} \centering \includegraphics[width=\textwidth]{./Images/JBTEditor.png} % JBTEditor.png: 1365x730 pixel, 72dpi, 48.15x25.75 cm, bb= \caption{JBT Editor} \label{fig:JBTEditor} \end{figure} \bibliographystyle{plain} \bibliography{JBTBib} \end{document} ================================================ FILE: Documentation/JBTBib.bib ================================================ @Book{Millington09, author = {Ian Millington and John Funge}, title = {Artificial Intelligence for Games}, publisher = {Morgan Kaufmann}, year = {2009}, edition = {Second} } @misc{AIGameDev, author = {Alex J. Champandard}, title = {http://aigamedev.com} } ================================================ FILE: JBTCore/.classpath ================================================ ================================================ FILE: JBTCore/.gitignore ================================================ /bin ================================================ FILE: JBTCore/.project ================================================ JBT org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature ================================================ FILE: JBTCore/src/jbt/exception/IllegalReturnStatusException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.exception; import jbt.execution.core.ExecutionTask; /** * Exception thrown when {@link ExecutionTask#internalTick()} returns a Status * that is not allowed. * * @author Ricardo Juan Palma Durán * */ public class IllegalReturnStatusException extends TickException { private static final long serialVersionUID = 1L; public IllegalReturnStatusException(){ super(); } public IllegalReturnStatusException(String msg){ super(msg); } } ================================================ FILE: JBTCore/src/jbt/exception/NotTickableException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.exception; /** * Exception thrown when a task that cannot be ticked is ticked. * * @author Ricardo Juan Palma Durán * */ public class NotTickableException extends TickException { private static final long serialVersionUID = 1L; public NotTickableException(){ super(); } public NotTickableException(String msg){ super(msg); } } ================================================ FILE: JBTCore/src/jbt/exception/SpawnException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.exception; /** * Exception thrown when a task that cannot be spawned is spawned. * * @author Ricardo Juan Palma Durán * */ public class SpawnException extends RuntimeException { private static final long serialVersionUID = 1L; public SpawnException() { super(); } public SpawnException(String msg) { super(msg); } } ================================================ FILE: JBTCore/src/jbt/exception/TickException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.exception; /** * Exception thrown when there is an error in the ticking process of a task. * * @author Ricardo Juan Palma Durán * */ public abstract class TickException extends RuntimeException { private static final long serialVersionUID = 1L; public TickException() { super(); } public TickException(String msg) { super(msg); } } ================================================ FILE: JBTCore/src/jbt/execution/context/BasicContext.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.context; import java.util.Hashtable; import java.util.Map; import jbt.execution.core.IBTLibrary; import jbt.execution.core.IContext; import jbt.model.core.ModelTask; /** * Basic implementation of the IContext interface. This class uses a Hashtable * to store the set of variables. *

* Also, since a context must contain a set of behaviour trees, this class * defines some methods to add behaviour trees to the context. * * @author Ricardo Juan Palma Durán * */ public class BasicContext implements IContext { /** * The set of variables that the context consists of. */ private Map variables; /** * The BT library that is internally used to manage all the trees of the * context. */ private GenericBTLibrary library; /** * Default constructor. Constructs an empty BasicContext. */ public BasicContext() { this.variables = new Hashtable(); this.library = new GenericBTLibrary(); } /** * * @see es.ucm.bt.core.IContext#getVariable(java.lang.String) */ public Object getVariable(String name) { return this.variables.get(name); } /** * * @see es.ucm.bt.core.IContext#setVariable(java.lang.String, * java.lang.Object) */ public boolean setVariable(String name, Object value) { if (value == null) { return this.variables.remove(name) == null ? false : true; } return this.variables.put(name, value) == null ? false : true; } /** * * @see es.ucm.bt.core.IContext#clear() */ public void clear() { this.variables.clear(); } /** * * @see jbt.execution.core.IContext#clearVariable(java.lang.String) */ public boolean clearVariable(String name) { return this.variables.remove(name) == null ? false : true; } /** * Adds all the behaviour trees in library to the set of * behaviour trees stored in the context. If there is already a tree with * the same name as that of one of the trees in library, it is * overwritten. * * @param library * the library containing all the behaviour trees to add to this * context. * @return true if a previously stored behaviour tree has been overwritten, * and false otherwise. */ public boolean addBTLibrary(IBTLibrary library) { return this.library.addBTLibrary(library); } /** * Adds the behaviour tree tree to the set of behaviour trees * stored in the context. If there is already a tree with the name * name, then it is overwritten by tree. * * @param name * the name that will identify the tree tree in the * context. * @param tree * the tree to insert. * @return true if there was already a tree with name name, and * false otherwise. */ public boolean addBT(String name, ModelTask tree) { return this.library.addBT(name, tree); } /** * * @see jbt.execution.core.IContext#getBT(java.lang.String) */ public ModelTask getBT(String name) { return this.library.getBT(name); } } ================================================ FILE: JBTCore/src/jbt/execution/context/GenericBTLibrary.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.context; import java.util.Hashtable; import java.util.Iterator; import java.util.Map.Entry; import jbt.execution.core.IBTLibrary; import jbt.model.core.ModelTask; import jbt.util.Pair; /** * Simple implementation of the {@link IBTLibrary} interface, which internally * uses a Hashtable to map tree names to actual trees. This class also defines * methods for adding behaviour trees to the library itself. * * @author Ricardo Juan Palma Durán * */ public class GenericBTLibrary implements IBTLibrary { /** * The hashtable that stores all the trees of the library. */ private Hashtable trees; /** * Constructs a GenericBTLibrary containing no trees. */ public GenericBTLibrary() { this.trees = new Hashtable(); } /** * * @see jbt.execution.core.IBTLibrary#getBT(java.lang.String) */ public ModelTask getBT(String name) { return this.trees.get(name); } /** * Returns a read-only iterator through the behaviour trees of the library. * While this iterator is being used, the library cannot be modified. * Otherwise, the results are undefined. Note that both trees and their * names can be accessed through this iterator. * * @see java.lang.Iterable#iterator() */ public Iterator> iterator() { return new GenericBTLibraryIterator(); } /** * Adds all the behaviour trees in library to the set of * behaviour trees stored in this library. If there is already a tree with * the same name as that of one of the trees in library, it is * overwritten. * * @param library * the library containing all the behaviour trees to add to this * library. * @return true if a previously stored behaviour tree has been overwritten, * and false otherwise. */ public boolean addBTLibrary(IBTLibrary library) { boolean overwritten = false; for (Pair tree : library) { if (this.trees.put(tree.getFirst(), tree.getSecond()) != null) { overwritten = true; } } return overwritten; } /** * Adds the behaviour tree tree to the set of behaviour trees * stored in this library. If there is already a tree with the name * name, then it is overwritten by tree. * * @param name * the name that will identify the tree tree in the * library. * @param tree * the tree to insert. * @return true if there was already a tree with name name, and * false otherwise. */ public boolean addBT(String name, ModelTask tree) { if (this.trees.put(name, tree) != null) { return true; } else { return false; } } /** * Implementation of the iterator that GenericBTLibrary uses. This is a * read-only iterator (removal is not supported), and it internally uses an * iterator through the entry set of the hashtable. * * @author Ricardo Juan Palma Durán * */ private class GenericBTLibraryIterator implements Iterator> { private Iterator> internalIterator; public GenericBTLibraryIterator() { this.internalIterator = trees.entrySet().iterator(); } public boolean hasNext() { return this.internalIterator.hasNext(); } public Pair next() { Entry next = this.internalIterator.next(); return new Pair(next.getKey(), next.getValue()); } public void remove() { throw new UnsupportedOperationException( "This iterator cannot be used to remove elements"); } } } ================================================ FILE: JBTCore/src/jbt/execution/context/HierarchicalContext.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.context; import jbt.execution.core.IContext; /** * A HierarchicalContext is a context that stores a parent IContext to fall back * to when it cannot find a particular variable in its own set of variables. * This class just redefines the method {@link #getVariable(String)} so that if * the variable name cannot be found, its value is retrieved from the parent * context. * * @author Ricardo Juan Palma Durán * */ public class HierarchicalContext extends BasicContext { /** * The parent context. When a variable cannot be retrieved from the current * context, it will be looked up in the parent context. */ private IContext parent; /** * Default constructor. Builds an empty HierarchicalContext, with no parent * context. */ public HierarchicalContext() { super(); } /** * Sets the parent context of this HierarchicalContext. May be null, in * which case no parent context will be used. * * @param parent * the parent context, which may be null. */ public void setParent(IContext parent) { this.parent = parent; } /** * Returns the value of a variable whose name is name. If a * variable with such a name cannot be found in the current context, and * there is a parent context set, the variable will be looked up in the * parent context, and its value returned. If it cannot be found in the * parent context (or in any other parent contexts through recursion), null * is returned. * * @see es.ucm.bt.context.BasicContext#getVariable(java.lang.String) */ public Object getVariable(String name) { Object result; result = super.getVariable(name); if (result == null) { if (this.parent != null) { result = this.parent.getVariable(name); } } return result; } } ================================================ FILE: JBTCore/src/jbt/execution/context/SafeContext.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.context; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import java.util.Set; import jbt.execution.core.IContext; import jbt.model.core.ModelTask; /** * The SafeContext represents a context that can be used to safely controls * modifications in another context (the input context). *

* A SafeContext contains an IContext (the input context). Initially, all * variables are read form the input context. However, when a variable is set or * cleared, its value is not modified in the input context, but it is locally * modified instead. From then on, the variable will be locally read instead of * reading if from the input context. Thus, the input context is never modified. *

* SafeContext can be used to situations in which an entity should use another * context (the input context) in read-only mode. If such entity uses a * SafeContext, it will not modify the input context, but on the other hand will * interact with the SafeContext in just the same way it would with the input * context. * * @author Ricardo Juan Palma Durán * */ public class SafeContext implements IContext { /** * The original input context which the SafeOutputContext is based on. */ private IContext inputContext; /** * Flag that tells whether the SafeOutputContext has been cleared. */ private boolean cleared; /** * The set of local variables managed by the SafeOutputContext. */ private Map localVariables; /** * Set containing the names of those variables whose value has been set or * cleared by the SafeOutputContext. */ private Set localModifiedVariables; /** * Constructs a SafeContext whose input context is inputContext * . * * @param inputContext * the input context. */ public SafeContext(IContext inputContext) { this.inputContext = inputContext; this.localVariables = new Hashtable(); this.cleared = false; this.localModifiedVariables = new HashSet(); } /** * Retrieves the value of a variable.If the variable has not been modified * by the SafeContext, its value is retrieved from the input context. * However, if the variable has been modified (either cleared or set), the * value will be retrieved from the SafeContext. * * @param name * the name of the variable to retrieve. * * @return the value of a variable whose name is name, or null * if it does not exist. * * @see jbt.execution.core.IContext#getVariable(java.lang.String) */ public Object getVariable(String name) { if (this.localModifiedVariables.contains(name) || this.cleared) { return this.localVariables.get(name); } else { Object variable = this.localVariables.get(name); if (variable != null) { return variable; } else { return this.inputContext.getVariable(name); } } } /** * Sets the value of a variable. Its value is not written into the input * context. Instead, its value is stored into a local variable managed by * the SafeContext. * * @param name * the name of the variable. * @param value * the value for the variable. * @return true if a variable with the same name already existed, and false * otherwise. * * @see jbt.execution.core.IContext#setVariable(java.lang.String, * java.lang.Object) */ public boolean setVariable(String name, Object value) { if (!this.localModifiedVariables.contains(name)) { this.localModifiedVariables.add(name); } if (value == null) { return this.localVariables.remove(name) == null ? false : true; } return this.localVariables.put(name, value) == null ? false : true; } /** * Clears the context. Variables are not removed from the input context, but * from the set of local variables managed by the SafeContext. * * @see jbt.execution.core.IContext#clear() */ public void clear() { this.localVariables.clear(); this.cleared = true; } /** * Clears a variable of the context. If it not removed from the input * context, but from the set of local variables managed by the SafeContext. * * @param name * the name of the variable to clear. * @return true if a variable was actually cleared, and false in case it did * not exist. * * @see jbt.execution.core.IContext#clearVariable(java.lang.String) */ public boolean clearVariable(String name) { if (!this.localModifiedVariables.contains(name)) { this.localModifiedVariables.add(name); } return this.localVariables.remove(name) == null ? false : true; } /** * Returns the behaviour tree of a particular name, or null in case it * cannot be found. The behaviour tree is extracted from the input context * passed at construction time. * * @param the * name of the behaviour tree to retrieve. * @return the behaviour tree, or null if it cannot be found. * * @see jbt.execution.core.IContext#getBT(java.lang.String) */ public ModelTask getBT(String name) { return this.inputContext.getBT(name); } } ================================================ FILE: JBTCore/src/jbt/execution/context/SafeOutputContext.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.context; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import jbt.execution.core.IContext; import jbt.model.core.ModelTask; /** * The SafeOutputContext represents a context that can be used to safely * controls modifications in another context (the input context). *

* A SafeOutputContext contains an IContext (the input context), and a * list of output variables. These are the variables that can be written * into the input context. The rest of variables are stored locally in the * SafeOutputContext. *

* Thus, when the SafeOutputContext sets the value of a variable, it will * normally set its value in a local variable. However, if the variable is one * of the list of output variables, the value will be set in the input context. *

* When retrieving variables, a variable in the list of output variables will * always be retrieved from the input context. A variable that is not in the * list of output variables will also be retrieved from the input context; * however, when such variable is modified (either its value is changed or * cleared), the value will be retrieved from the SafeOutputContext (that is, * from the moment a variable that is not in the list of output variables is * modified, it is managed locally). *

* With respect to clearing variables, output variables are always cleared in * the input context. However, since only output variables can be modified in * the input context, any other variable will be cleared in the * SafeOutputContext. *

* The SafeOutputContext can be used in situations where an entity must use a * context (the input context) in a soft-read-only mode. By using the * SafeOutputContext, such entity will only be able to modify the output * variables in the input context. On the other hand, it will interact with the * SafeOutputContext in just the same way it would with the input context. * * @author Ricardo Juan Palma Durán * */ public class SafeOutputContext implements IContext { /** * The original input context which the SafeOutputContext is based on. */ private IContext inputContext; /** * The list of output variables. These variables can be written into the * {@link #inputContext}, unlike the rest, that are stored in * {@link #localVariables}. */ private List outputVariables; /** * Set containing the names of those non-output variables whose value has * been set or cleared by the SafeOutputContext. */ private Set localModifiedVariables; /** * Flag that tells whether the SafeOutputContext has been cleared. */ private boolean cleared; /** * The set of local variables managed by the SafeOutputContext. */ private Map localVariables; /** * Constructs a SafeOutputContext whose input context is * inputContext and whose list of output variables is * outputVariables. * * @param inputContext * the input context. * @param outputVariables * the list of output variables. */ public SafeOutputContext(IContext inputContext, List outputVariables) { this.inputContext = inputContext; this.outputVariables = outputVariables; this.localVariables = new Hashtable(); this.localModifiedVariables = new HashSet(); this.cleared = false; } /** * Retrieves the value of a variable. If it is an output variable, its value * is retrieved from the input context. Otherwise, if the variable has not * been modified by the SafeOutputContext, its value is also retrieved from * the input context. However, if the variable has been modified (either * cleared or set), the value will be retrieved from the SafeOutputContext. * * @param name * the name of the variable to retrieve. * * @return the value of a variable whose name is name, or null * if it does not exist. * * @see jbt.execution.core.IContext#getVariable(java.lang.String) */ public Object getVariable(String name) { if (this.outputVariables.contains(name)) { return this.inputContext.getVariable(name); } else { if (this.localModifiedVariables.contains(name) || this.cleared) { return this.localVariables.get(name); } else { Object variable = this.localVariables.get(name); if (variable != null) { return variable; } else { return this.inputContext.getVariable(name); } } } } /** * Sets the value of a variable. If it is an output variable, its value is * written into the input context. Otherwise, its value will be stored into * a local variable managed by the SafeOutputContext. * * @param name * the name of the variable. * @param value * the value for the variable. * @return true if a variable with the same name already existed, and false * otherwise. * * @see jbt.execution.core.IContext#setVariable(java.lang.String, * java.lang.Object) */ public boolean setVariable(String name, Object value) { if (this.outputVariables.contains(name)) { return this.inputContext.setVariable(name, value); } else { if (!this.localModifiedVariables.contains(name)) { this.localModifiedVariables.add(name); } if (value == null) { return this.localVariables.remove(name) == null ? false : true; } return this.localVariables.put(name, value) == null ? false : true; } } /** * Clears the context. Output variables are cleared in the input context. * The rest are removed from the set of local variables managed by the * SafeOutputContext. * * @see jbt.execution.core.IContext#clear() */ public void clear() { this.localVariables.clear(); for (String outputVariable : this.outputVariables) { this.inputContext.clearVariable(outputVariable); } this.cleared = true; } /** * Clears a variable of the context. If it is an output variable, the value * is cleared in the input context. Otherwise, the variable is removed from * the set of local variables managed by the SafeOutputContext. * * @param name * the name of the variable to clear. * @return true if a variable was actually cleared, and false in case it did * not exist. * * @see jbt.execution.core.IContext#clearVariable(java.lang.String) */ public boolean clearVariable(String name) { if (this.outputVariables.contains(name)) { return this.inputContext.clearVariable(name); } else { if (!this.localModifiedVariables.contains(name)) { this.localModifiedVariables.add(name); } return this.localVariables.remove(name) == null ? false : true; } } /** * Returns the behaviour tree of a particular name, or null in case it * cannot be found. The behaviour tree is extracted from the input context * passed at construction time. * * @param the * name of the behaviour tree to retrieve. * @return the behaviour tree, or null if it cannot be found. * * @see jbt.execution.core.IContext#getBT(java.lang.String) */ public ModelTask getBT(String name) { return this.inputContext.getBT(name); } } ================================================ FILE: JBTCore/src/jbt/execution/core/BTExecutor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import jbt.execution.context.BasicContext; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.task.decorator.ExecutionInterrupter; import jbt.model.core.ModelTask; import jbt.model.core.ModelTask.Position; import jbt.model.task.decorator.ModelInterrupter; /** * BTExecutor is the implementation of the IBTExecutor interface. *

* BTs are conceptually modeled by a hierarchy of interconnected ModelTask objects. A BT is * represented by the root of the tree, which is a single ModelTask object. *

* Therefore, in order to run a BT, a BTExecutor only needs the root task of the tree (a ModelTask * object), and the context (IContext) that will be used by the tree. Keep in mind that a BT is * executed within a context that contains information about the game that leaf tasks (actions and * conditions) and guards (ModelTask) may need in order to run. *

* The internal implementation of the BTExecutor is based on using, for each ModelTask of the BT, an * ExecutionTask obtained by calling the {@link ModelTask#createExecutor(BTExecutor, ExecutionTask)} * method. The most important feature of the BTExecutor, however, is that it uses a list of * tickable nodes. When {@link #tick()} is called, not all the nodes of the tree are ticked, * but only those that are currently relevant to the execution of the tree. By doing so, running the * tree is a much more efficient process, since only the nodes that can make the tree evolve receive * CPU time. *

* The BTExecutor is also in charge of storing the permanent state of tasks (see {@link ITaskState} * and {@link ExecutionTask#storeState()}). For each BT there is only one BTExecutor that actually * runs it. Therefore, the BTExecutor can be used as the repository for storing the state of the * tasks of the tree. * * @see ModelTask * @see ExecutionTask * * @author Ricardo Juan Palma Durán * */ public class BTExecutor implements IBTExecutor { /** The root task of the BT this executor is running. */ private ModelTask modelBT; /** The ExecutionTask associated to the root ModelTask. */ private ExecutionTask executionBT; /** List of tickable tasks. */ private List tickableTasks; /** List of open tasks. */ private List openTasks; /** The context that will be passed to the root task. */ private IContext context; /** * Boolean telling whether this BTExecutor has been ticked ( {@link #tick()} ) before. */ private boolean firstTimeTicked = true; /** * List of the tasks that must be inserted into the list of tickable nodes. */ private List currentTickableInsertions; /** * List of the tasks that must be removed from the list of tickable nodes. */ /* * TODO: improve the way requests for insertions and removals are handled. Currently simple * Lists are used to manage them, but in case there were many requests, this process would not * be very efficient. */ private List currentTickableRemovals; /** * List of the tasks that must be inserted into the list of open nodes. */ private List currentOpenInsertions; /** * List of the tasks that must be removed from the list of open nodes. */ private List currentOpenRemovals; /** * List of all the ExecutionInterrupter currently active in the behaviour tree. They are indexed * by their ModelInterrupter in the conceptual tree. *

* This list is used by ExecutionPerformInterruption, which must have a way of knowing what * ExecutionInterrupter it is interrupting. */ private Map interrupters; /** * States of the tasks of the tree that is being run by this BTExecutor. States are indexed by * Position. These are the positions of the ExecutionTask in the execution tree. These positions * are unique (they do not necessarily match the position of the corresponding ModelTask), so * each node in the execution tree can be unambiguously referenced by such position. Note that * this Map does not store the states of the nodes of the guards of the tree that is being run. */ private Map tasksStates; /** * Creates a BTExecutor that handles the execution of a behaviour tree. The behaviour tree is * represented by a ModelTask (the root of the tree). *

* A context for the tree must be provided. This context is passed to the root of the tree, and, * in general, it will be shared by all the nodes in the tree (it will be passed down the * hierarchy of the tree). Note however that, depending on the semantics of the tree itself, * some nodes may not use this context. * * @param modelBT * the root of the behaviour tree to run. * @param context * the initial context for the tree. */ public BTExecutor(ModelTask modelBT, IContext context) { if (modelBT == null) { throw new IllegalArgumentException("The input ModelTask cannot be null"); } if (context == null) { throw new IllegalArgumentException("The input IContext cannot be null"); } this.modelBT = modelBT; this.modelBT.computePositions(); this.context = context; this.tickableTasks = new LinkedList(); this.openTasks = new LinkedList(); this.currentOpenInsertions = new LinkedList(); this.currentOpenRemovals = new LinkedList(); this.currentTickableInsertions = new LinkedList(); this.currentTickableRemovals = new LinkedList(); this.interrupters = new Hashtable(); this.tasksStates = new Hashtable(); } /** * Creates a BTExecutor that handles the execution of a behaviour tree. The behaviour tree is * represented by a ModelTask (the root of the tree). *

* A new empty context for the tree is created. This context is passed to the root of the tree, * and, in general, it will be shared by all the nodes in the tree (it will be passed down the * hierarchy of the tree). Note however that, depending on the semantics of the tree itself, * some nodes may not use the context context. * * @param modelBT * the root of the behaviour tree to run. */ public BTExecutor(ModelTask modelBT) { if (modelBT == null) { throw new IllegalArgumentException("The input ModelTask cannot be null"); } this.modelBT = modelBT; this.modelBT.computePositions(); this.context = new BasicContext(); this.tickableTasks = new LinkedList(); this.openTasks = new LinkedList(); this.currentOpenInsertions = new LinkedList(); this.currentOpenRemovals = new LinkedList(); this.currentTickableInsertions = new LinkedList(); this.currentTickableRemovals = new LinkedList(); this.interrupters = new Hashtable(); this.tasksStates = new Hashtable(); } /** * * @see jbt.execution.core.IBTExecutor#tick() */ public void tick() { /* * The ticking algorithm works as follows: * * If it is the very first time that this method is called, an ExecutionTask is created from * the root ModelTask (that is, the root of the behaviour tree that this BTExecutor is going * to run). Then, that task is spawned. * * From then on, tick() will just call tick() on all the ExecutionTasks in the list of * tickable tasks. * * It is important to note that insertions and removals from the list of tickable and open * tasks are processed at the very beginning and at the very end of this method, but not * while it is ticking the current list of tickable tasks. */ Status currentStatus = this.getStatus(); /* We only tick if the tree has not finished yet or if it has not started running. */ if (currentStatus == Status.RUNNING || currentStatus == Status.UNINITIALIZED) { processInsertionsAndRemovals(); if (this.firstTimeTicked) { this.executionBT = this.modelBT.createExecutor(this, null); this.executionBT.spawn(this.context); this.firstTimeTicked = false; } else { for (ExecutionTask t : tickableTasks) { t.tick(); } } processInsertionsAndRemovals(); } } /** * * @see jbt.execution.core.IBTExecutor#terminate() */ public void terminate() { if (this.executionBT != null) { this.executionBT.terminate(); } } /** * Returns the ExecutionInterrupter that is currently active and registered in the BTExecutor ( * {@link #registerInterrupter(ExecutionInterrupter)}) whose associated ModelInterrupter is * modelInterrupter. Returns null if there is no such an ExecutionInterrupter. * * @param modelInterrupter * the ModelInterrupter associated to the ExecutionInterrupter to retrieve. * @return the ExecutionInterrupter whose associated ModelInterrupter is * modelInterrupter. */ public ExecutionInterrupter getExecutionInterrupter(ModelInterrupter modelInterrupter) { return this.interrupters.get(modelInterrupter); } /** * Registers an ExecutionInterrupter with this BTExecutor. * * @param interrupter * the ExecutionInterrupter to register. */ public void registerInterrupter(ExecutionInterrupter interrupter) { this.interrupters.put((ModelInterrupter) interrupter.getModelTask(), interrupter); } /** * Unregisters an ExecutionInterrupter from this BTExecutor. * * @param interrupter * the ExecutionInterrupter to unregister. */ public void unregisterInterrupter(ExecutionInterrupter interrupter) { this.interrupters.remove(interrupter.getModelTask()); } /** * Enum defining the relevant lists that the BTExecutor handles. * *

    *
  • {@link #OPEN}: the list of open (active) nodes. *
  • {@link #TICKABLE}: the list of tickable nodes, that is, those that receive ticks every * time {@link BTExecutor#tick()} is called. *
* * @author Ricardo Juan Palma Durán * */ public static enum BTExecutorList { /** Enum for the list of open nodes. */ OPEN, /** Enum for the list of tickable nodes. */ TICKABLE }; /** * Method used to request the BTExecutor to insert an ExecutionTask into one of the list that it * handles. The insertion is not performed right away, but delayed until: * *
    *
  • Either the current game AI cycle (call to {@link #tick()}) finishes. This happens if the * insertion is requested in the middle of an AI cycle, that is, if tick() is still * running. *
  • Or the next AI cycle starts. This happens if the insertion is requested when the * BTExecutor is not ticking the underlying BT. In this case, the next time tick() * is called, the insertion will be processed just before the BT is actually ticked. *
* * @param listType * the type of the list that the task will be inserted into. * @param t * the task that wants to be inserted into the list of type listType. */ public void requestInsertionIntoList(BTExecutorList listType, ExecutionTask t) { if (listType == BTExecutorList.OPEN) { if (!this.currentOpenInsertions.contains(t)) { this.currentOpenInsertions.add(t); } } else { if (!this.currentTickableInsertions.contains(t)) { this.currentTickableInsertions.add(t); } } } /** * Method used to request the BTExecutor to remove an ExecutionTask from one of the list that * the BTExecutor handles. The removal is not performed right away, but delayed until: * *
    *
  • Either the current game AI cycle (call to {@link #tick()}) finishes. This happens if the * removal is requested in the middle of an AI cycle, that is, if tick() is still * running. *
  • Or the next AI cycle starts. This happens if the removal is requested when the BTExecutor * is not ticking the underlying BT. In this case, the next time tick() is called, * the removal will be processed just before the BT is actually ticked. *
* * @param listType * the type of the list from which the task will be removed. * @param t * the task that wants to be removed from the list of type listType. */ public void requestRemovalFromList(BTExecutorList listType, ExecutionTask t) { if (listType == BTExecutorList.OPEN) { if (!this.currentOpenRemovals.contains(t)) { this.currentOpenRemovals.add(t); } } else { if (!this.currentTickableRemovals.contains(t)) { this.currentTickableRemovals.add(t); } } } /** * Cancels a previous request of insertion into one of the lists that the BTExecutor handles. If * no such insertion request was made, this method does nothing. * * @param listType * the list from which the insertion request will be canceled. * @param t * the task whose insertion will be canceled. */ public void cancelInsertionRequest(BTExecutorList listType, ExecutionTask t) { if (listType == BTExecutorList.OPEN) { this.currentOpenInsertions.remove(t); } else { this.currentTickableInsertions.remove(t); } } /** * Cancels a previous request of removal from one of the lists that the BTExecutor handles. If * no such removal request was made, this method does nothing. * * @param listType * the list from which the removal request will be canceled. * @param t * the task whose removal will be canceled. */ public void cancelRemovalRequest(BTExecutorList listType, ExecutionTask t) { if (listType == BTExecutorList.OPEN) { this.currentOpenRemovals.remove(t); } else { this.currentTickableRemovals.remove(t); } } /** * Method that processes the insertions and removals into and from the lists of tickable and * open nodes that have been previously requested via the requestXXX methods. *

* After calling this method, all pending removals and insertions are processed, so no new * insertion and removal will be carried out unless new ones are requested. */ private void processInsertionsAndRemovals() { /* * Process insertions and removals. */ for (ExecutionTask t : this.currentTickableInsertions) { this.tickableTasks.add(t); } for (ExecutionTask t : this.currentTickableRemovals) { this.tickableTasks.remove(t); } for (ExecutionTask t : this.currentOpenInsertions) { this.openTasks.add(t); } for (ExecutionTask t : this.currentOpenRemovals) { this.openTasks.remove(t); } /* * Clear the lists of tasks to insert and remove. */ this.currentOpenInsertions.clear(); this.currentOpenRemovals.clear(); this.currentTickableInsertions.clear(); this.currentTickableRemovals.clear(); } /** * * @see jbt.execution.core.IBTExecutor#getBehaviourTree() */ public ModelTask getBehaviourTree() { return this.modelBT; } /** * * @see jbt.execution.core.IBTExecutor#getStatus() */ public Status getStatus() { if (this.executionBT == null) { return Status.UNINITIALIZED; } else { return this.executionBT.getStatus(); } } /** * * @see jbt.execution.core.IBTExecutor#getRootContext() */ public IContext getRootContext() { return this.context; } /** * Sets the permanent state of a given task. The task is identified by the position it occupies * in the execution behaviour tree, which unambiguously identifies it. * * @param taskPosition * the position of the task whose state must be stored. * @param state * the state of the task, or null if it should be cleared. * @return true if there was a previous state for this task in the BTExecutor, or false * otherwise. */ public boolean setTaskState(Position taskPosition, ITaskState state) { if (state == null) { return this.tasksStates.remove(taskPosition) == null ? false : true; } return this.tasksStates.put(taskPosition, state) == null ? false : true; } /** * Returns the permanent state of a task. The task is identified by the position it occupies in * the execution behaviour tree, which unambiguously identifies it. * * @param taskPosition * the position of the task whose state must be retrieved. * @return the state of the task, or null if there is no state stored in the BTExecutor for the * task. */ public ITaskState getTaskState(Position taskPosition) { return this.tasksStates.get(taskPosition); } /** * Copies the set of all tasks' states stored in executor into this BTExecutor. *

* After calling this method, the set of all tasks' states is shared by both BTExecutor * objects (executor and this), so if one modifies it, the other will * notice the change. */ public void copyTasksStates(BTExecutor executor) { this.tasksStates = executor.tasksStates; } /** * Clears the permanent state of a task. The task is identified by the position it occupies in * the execution behaviour tree, which unambiguously identifies it. * * @param taskPosition * the position of the task whose state must be cleared. * @return true if the BTExecutor contained the state of the task before calling this method, or * false otherwise. */ public boolean clearTaskState(Position taskPosition) { return this.tasksStates.remove(taskPosition) == null ? false : true; } /** * * @see java.lang.Object#toString() */ public String toString() { return "[Root: " + this.modelBT.getClass().getSimpleName() + ", Status: " + this.getStatus() + "]"; } } ================================================ FILE: JBTCore/src/jbt/execution/core/BTExecutorFactory.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import jbt.model.core.ModelTask; /** * The BTExecutorFactory implements the simple factory pattern, and allows * clients of the framework to create instances of {@link IBTExecutor} objects * that will run specific behaviour trees. * * @author Ricardo Juan Palma Durán * */ public class BTExecutorFactory { /** * Creates an IBTExecutor that is able to run a specific behaviour tree. The * input context is also specified. * * @param treeToRun * the behaviour tree that the returned IBTExecutor will run, * @param context * the input context to be used by the behaviour tree. * @return an IBTExecutor to run the tree treeToRun. */ public static IBTExecutor createBTExecutor(ModelTask treeToRun, IContext context) { return new BTExecutor(treeToRun, context); } /** * Creates an IBTExecutor that is able to run a specific behaviour tree. A * new empty context is created for the tree. * * @param treeToRun * the behaviour tree that the returned IBTExecutor will run, * @return an IBTExecutor to run the tree treeToRun. */ public static IBTExecutor createBTExecutor(ModelTask treeToRun) { return new BTExecutor(treeToRun); } } ================================================ FILE: JBTCore/src/jbt/execution/core/BTLibraryFactory.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.Iterator; import java.util.List; import jbt.execution.context.GenericBTLibrary; import jbt.model.core.ModelTask; /** * The BTLibraryFactory implements the simple factory pattern, and allows * clients of the framework to create instances of {@link IBTLibrary} composed * of behaviour trees. * * @author Ricardo Juan Palma Durán * */ public class BTLibraryFactory { /** * Creates a BT library that contains all the BTs contained in the libraries * of libraries. If several trees are referenced by the same * name, only the last one (according to its order in the input libraries) * will remain. * * @param libraries * the list with all the libraries whose BTs will contain the * returned BT library. * @return a BT library that contains all the BTs contained in the libraries * of libraries. */ public static IBTLibrary createBTLibrary(List libraries) { GenericBTLibrary result = new GenericBTLibrary(); for (IBTLibrary library : libraries) { result.addBTLibrary(library); } return result; } /** * Creates a BT library that contains all the behaviour trees in * behaviourTrees. The name of the trees are specified in * names, so, for instance, the i-th element in * names represents the name of the i-th tree in * behaviourTrees. If several trees are referenced by the same * name, only the last one (according to its order in the input lists) will * remain. * * @param behaviourTrees * the list with the trees that the BT library will contain. * @param names * the list with the names of the trees. * @return a BT library that contains all the behaviour trees in the list * behaviourTrees. */ public static IBTLibrary createBTLibrary(List behaviourTrees, List names) { GenericBTLibrary result = new GenericBTLibrary(); Iterator treesIterator = behaviourTrees.iterator(); Iterator namesIterator = names.iterator(); while (treesIterator.hasNext()) { result.addBT(namesIterator.next(), treesIterator.next()); } return result; } } ================================================ FILE: JBTCore/src/jbt/execution/core/ContextFactory.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.Iterator; import java.util.List; import jbt.execution.context.BasicContext; import jbt.model.core.ModelTask; /** * The ContextFactory implements the simple factory pattern, and allows clients * of the framework to create instances of {@link IContext} objects that can be * used when running behaviour trees. * * @author Ricardo Juan Palma Durán * */ public class ContextFactory { /** * Creates a new empty context (with no variables) that contains all the * behaviour trees specified in library. * * @param library * the set of behaviour trees that the returned IContext will * contain. * @return a new empty context that contains all the behaviour trees * specified in library. */ public static IContext createContext(IBTLibrary library) { BasicContext result = new BasicContext(); result.addBTLibrary(library); return result; } /** * Creates a new empty context (with no variables) that contains all the * behaviour trees in the libraries libraries. * * @param libraries * the list of libraries whose behaviour trees this context will * contain. * @return a new empty context that contains all the behaviour trees in the * libraries libraries. */ public static IContext createContext(List libraries) { BasicContext result = new BasicContext(); for (IBTLibrary library : libraries) { result.addBTLibrary(library); } return result; } /** * Creates a new empty context (with no variables in it) that contains all * the behaviour trees in behaviourTrees. The name of the trees * are specified in names, so, for instance, the i-th element * in names represents the name of the i-th tree in * behaviourTrees. * * @param behaviourTrees * the list with the trees that the context will contain. * @param names * the list with the names of the trees. * @return a new empty context that contains all the behaviour trees in the * list behaviourTrees. */ public static IContext createContext(List behaviourTrees, List names) { BasicContext result = new BasicContext(); Iterator treesIterator = behaviourTrees.iterator(); Iterator namesIterator = names.iterator(); while (treesIterator.hasNext()) { result.addBT(namesIterator.next(), treesIterator.next()); } return result; } /** * Creates a new empty context (with no variables in it). * * @return a new empty context. */ public static IContext createContext() { return new BasicContext(); } } ================================================ FILE: JBTCore/src/jbt/execution/core/ExecutionTask.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import jbt.exception.IllegalReturnStatusException; import jbt.exception.NotTickableException; import jbt.exception.SpawnException; import jbt.exception.TickException; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.event.ITaskListener; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.core.ModelTask.Position; /** * A behaviour tree is conceptually modeled by the ModelTask class. A ModelTask, * however, does not know how to run, since it is just a conceptual abstraction. *

* The ExecutionTask represents a tasks that knows how to run a particular * ModelTask. For each type of ModelTask, such as ModelSequence or * ModelParallel, there is an ExecutionTask that knows how to run it (e.g., * ExecutionSequence and ExecutionParallel). *

* ExecutionTask works together with the BTExecutor class to run its * corresponding ModelTask (an ExecutionTask does have a managing BTExecutor). * An ExecutionTask defines several methods for the different stages it goes * through. *

    *
  • {@link #spawn(IContext)} is initially called when the task needs to * create the hierarchical structure of ExecutionTask objects that, as a whole, * are able to run the parent ExecutionTask. *
  • {@link #tick()} is used from then on, in order to give the ExecutionTask * some time to think and evolve according to its semantics. *
  • {@link #terminate()} is used when the task needs to be abruptly * terminated. *
* * The three methods above depend on the implementation that each subclass makes * of the {@link #internalSpawn()}, {@link #internalTick()} and * {@link #internalTerminate()} methods respectively. These three protected * abstract methods are in charge of carrying out the actual spawning, ticking * and termination processes. It has to be noted that it is very important the * way that the ExecutionTask class interacts with other ExecutionTask classes * as well as with the BTExecutor. With respect to the BTExecutor, the * ExecutionTask asks to be inserted and removed from both the lists of open and * tickable nodes of the BTExecutor. Subclasses only have to worry about * requesting to be inserted into the list of tickable nodes. Other types of * insertions and removals are automatically handled by the ExecutionTask class. * * @see ModelTask * @see BTExecutor * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionTask implements ITaskListener { /** The ModelTask this ExecutionTask is running. */ private ModelTask modelTask; /** The BTExecutor that is managing this ExecutionTask. */ private BTExecutor executor; /** The context of the task. */ private IContext context; /** * List of all the listeners that are listening to TaskEvent from this task. */ private List listeners; /** Current status of the task. */ private Status status; /** Flag telling whether the task can be spawned or not. */ private boolean spawnable; /** Flag indicating whether the task can ticked or not. */ private boolean tickable; /** Flag indicating whether the task has been terminated or not. */ private boolean terminated; /** The parent ExecutionTask. null if this is the root of the tree. */ private ExecutionTask parent; /** * The position that the task occupies in the execution tree. Note that this * position does not necessarily match that of the underlying ModelTask. * This position is computed from the parent ExecutionTask when the * ExecutionTask is created. */ private Position position; /** * Enum defining the possible states of an ExecutionTask. Throughout its * execution, an ExecutionTask may be in several states: * *
    *
  • {@link #FAILURE}: means the task has failed, that is, it could not * complete successfully. *
  • {@link #SUCCESS}: means the task has completed successfully. *
  • {@link #RUNNING}: means the task is still running. *
  • {@link #TERMINATED}: means the task has been abruptly terminated. It * is conceptually similar to {@link #FAILURE}, so whenever a task has been * terminated, it is also considered to have failed. *
  • {@link #UNINITIALIZED}: means the task has not been spawned yet, that * is, it has not started executing. *
* * @author Ricardo Juan Palma Durán * */ public static enum Status { /** Status code meaning the task has failed. */ FAILURE, /** Status code meaning the task has succeeded. */ SUCCESS, /** Status code meaning the task is still running. */ RUNNING, /** * Status code meaning the task has been abruptly terminated. * It is conceptually similar to {@link #FAILURE}, so whenever a task * has been terminated, it is also considered to have failed. */ TERMINATED, /** * Status code meaning the task has not been spawned yet, that is, it * has not started executing. */ UNINITIALIZED, } /** * Constructs an ExecutionTask with an associated ModelTask and a * BTExecutor. The ModelTask represents the conceptual task that the * ExecutionTask is running, and the BTExecutor is that in charge of the * ExecutionTask. Also, the parent of the ExecutionTask must be provided. * * @param modelTask * the ModelTask this ExecutionTask will run. * @param executor * the BTExecutor managing this task. * @param parent * the parent ExecutionTask, or null in case this is the root of * the tree. */ public ExecutionTask(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { this.modelTask = modelTask; this.executor = executor; this.listeners = new LinkedList(); this.spawnable = true; this.tickable = false; this.terminated = false; this.status = Status.UNINITIALIZED; this.parent = parent; /* Compute the position of this node. */ if (parent == null) { this.position = new Position(); } else { this.position = new Position(parent.position); int nextMove = getMove(); this.position.addMove(nextMove); } } /** * This method is called the very first time the task has to be executed. *

* This method is in charge of creating all the structure down the hierarchy * of the BT that is needed to actually run the task. This process will * probably include creating and spawning, in a recursive manner, some of * the children of this task. *

* spawn() is not an abstract method. In reality, the actual * spawning process is not carried out by spawn() itself, but * by the abstract {@link #internalSpawn()} method. What * spawn() does is to just restore the previous state of the * task (if any), and then call internalSpawn(). * internalSpawn() has a different implementation for every * subclass of ExecutionTask, since it is that method the one that in fact * carries out the spawning process. Thus, subclasses must define the * abstract internalSpawn() method. *

* This method also stores the execution context (context) to * be used by the task, which will be accessible through * {@link #getContext()} after calling this method. * * @param context * the context that the task will use. */ public final void spawn(IContext context) throws SpawnException { /* If the task cannot be spawned, throw an exception. */ if (!this.spawnable) { throw new SpawnException("The task cannot be spawned. It has already been spawned."); } /* * Store the context. */ this.context = context; this.spawnable = false; this.tickable = true; /* Set the current status of the task to Status.RUNNING. */ this.status = Status.RUNNING; /* * Request to be inserted into the list of open tasks. */ this.executor.requestInsertionIntoList(BTExecutorList.OPEN, this); /* * Restore the past state of the task in case it has any. */ ITaskState previousState = this.executor.getTaskState(getPosition()); restoreState(previousState); /* * Carry out the actual spawn. */ internalSpawn(); } /** * This is the method that carries out the actual spawning process of the * ExecutionTask. Sublclasses must define it, since the spawning process * varies depending on the type of the task. *

* internalSpawn() is called from the {@link #spawn(IContext)} * method. When internalSpawn() is called, the context of the * task is already accessible through {@link #getContext()}, so it can be * used by the task. *

* internalSpawn() is the method that creates all the structure * of interconnected tasks (ExecutionTask) that are necessary to run this * task. Each subclass is spawned in a different way. For instance, when a * sequence task is spawned, it has to spawn its first child, but when a * parallel task is spawned, it has to spawn all of its children. *

* An ExecutionTask contains a reference to a ModelTask which is trying to * run. When internalSpawn() is called, it has to create, * according to the semantics of the task, new ExecutionTask objects for the * children of the ModelTask. *

* For instance, let us suppose that there is a ModelSequence class * subclassing ModelTask. The ExecutionTask associated to ModelSequence is * ExecutionSequence. An ExecutionSequence has a reference to the * ModelSequence it is running. When ExecutionSequence is spawned, it has to * create, according to the semantics of the task, new ExecutionTask * objects for the children of the ModelTask. In this case it means that * the first child of the sequence should also be spawned (in a recursive * manner). Therefore, what the ExecutioSequence has to is to take the first * child of the ModelSequence (let us call it child), which will be a * ModelTask. Then, it will have to create the appropriate ExecutionTask for * child, by calling child.createExecutor(). Finally, it will * have to call the spawn() method recursively on the * ExecutionTask returned by child.createExecutor(). Other tasks * would behave differently. For instance, when an ExecutionParallel * (associated to a ModelParallel) is spawned, it has to create an * ExecutionTask for all of the children of its ModelParallel, and * recursively spawn every single one of them. *

* Leaf tasks (also known as low level task, since they usually -but * not always- perform a game-dependent process), such as actions and * conditions, are spawned in a different way. They do not recursively spawn * any child, since they have none. When a leaf task is spawned, it should * start the execution of the process associated to the node. Keep in mind * that low level tasks may perform long processes that require several * ticks in order to complete. It is in this method that those processes * start (maybe in independent threads). *

* It should be noted, however, that many processes may be instantaneous, so * they may complete even within the internalSpawn() method. * Nevertheless, in these cases the BT should not evolve, reason why the * termination notification to its parent is carried out in the * tick() method, probably in the next AI cycle. If * spawn() were allowed to notify parents when the task * terminates, then a single call to spawn() may take too long * to complete due to the uninterrupted evolution of the tree, which is * something that has to be avoided. *

* An important part of the spawning process is to decide if the * ExecutionTask will enter the list of tickable nodes of the BTExecutor. * Only tasks that request to be inserted into that list are ticked at every * game AI cycle (when {@link BTExecutor#tick()} is called). In order to * request it, the task has to call * {@link #requestInsertionIntoTickableList()}. In general, all leaf tasks * should be ticked every cycle, since the progress of parent tasks depends * on the termination of their children. However, non-leaf tasks may also * need ticking. For instance, the dynamic priority list task needs to * constantly receive ticks, since it has to check its children's guards all * the time -the dynamic priority list can evolve not only because of the * termination of the currently active child, but also because of the * reevaluation of guards-. In general, if the only way of making a task * evolve is through the notification of termination from one or several of * its children, then the task should not be in the list of tickable nodes. * On the other hand, if a task can evolve because of factors other than the * termination of one or several of its children, then it should request to * be inserted into the list of tickable nodes. */ protected abstract void internalSpawn(); /** * After spawning an ExecutionTask, tick() has to be called in * order to update it and keep track of its status. *

* This method is in charge of updating this ExecutionTask according to its * semantics. This process may include spawning none, one, or several of its * children depending on their termination status. It may also have to * terminate some of its children. *

* tick() is not an abstract method. In reality, the actual * ticking process is not carried out by tick() itself, but by * the abstract {@link #internalTick()} method. Thus, subclasses must * define the abstract internalSpawn() method. *

* What tick() does is to call internalTick() to * carry out the actual ticking process. Then, tick() checks * if, after the tick, the task has finished (that is, it checks if the * status returned by internalTick() is {@link Status#FAILURE} * or {@link Status#SUCCESS}). If so, tick() fires a TaskEvent * to the parent of this ExecutionTask, so the parent does whatever it has * to do after the termination of its child, and also requests to be removed * from both the lists of tickable and open nodes of the BTExecutor (because * this task will not be ticked again unless it is spawned again). Also, if * the ExecutionTask has finished, tick() stores the current * state of the task just in case it is spawned again in the future. *

* It should be noted that when a task has been terminated ( * {@link #terminate()}), tick() does nothing, and it just * returns {@link Status#TERMINATED} (it does not even fire a TaskEvent). * * @return the status of the task after being ticked. */ public final Status tick() throws TickException { /* If the task cannot be ticked, throw an exception. */ if (!this.tickable) { throw new NotTickableException("The task cannot be ticked. It must be spawned first."); } /* If the task has been terminated, do nothing. */ if (!this.terminated) { /* Otherwise, perform the actual tick by calling "internalTick()". */ Status newStatus = this.internalTick(); /* Check if the value that is returned by "internalTick()" is valid. */ if (!validInternalTickStatus(newStatus)) { throw new IllegalReturnStatusException(newStatus.toString() + " cannot be returned by ExecutionTask.internalTick()"); } this.status = newStatus; /* * If the task has finished (either successfully or in failure), a * TaskEvent has to be fired in order to notify its parent about the * termination. Before firing the event, the current state of the * task has to be stored into the BTExecutor, just in case it needs * to be restored in the future. The task also requests to be * removed from both the list of tickable and open nodes. * * Otherwise the task has not finished, so nothing in particular is * done. */ if (newStatus != Status.RUNNING) { ITaskState taskState = storeState(); this.executor.setTaskState(getPosition(), taskState); this.executor.requestRemovalFromList(BTExecutorList.TICKABLE, this); this.executor.requestRemovalFromList(BTExecutorList.OPEN, this); fireTaskEvent(newStatus); } return newStatus; } else { return Status.TERMINATED; } } /** * internalTick() is the method that actually carries out the * ticking process of an ExecutionTask. Subclasses must define it, since the * ticking process varies depending on the type of the task. *

* internalTick() is called from the {@link #tick()} method. * When it is called, it must assume that the task has already been spawned * ({@link #spawn(IContext)}) and that the context of the task is already * accessible through {@link #getContext()}. *

* internalTick() is the method that is used to update an * ExecutionTask. Behaviour trees are driven by ticks, which means that they * only evolve when they are ticked (otherwise put, behaviour trees are * given CPU time only when they are ticked). internalTick() is * the method that implements the ticking process of the task. Therefore, * when it is called, and according to the semantics of the task, it will * have to do some processes to make the task go on. This processes may * include spawning other children or even terminating currently running * children. *

* For instance, let us suppose that there is a ModelSequence class * subclassing ModelTask. The ExecutionTask associated to ModelSequence is * ExecutionSequence. An ExecutionSequence has a reference to the * ModelSequence it is running. When ExecutionSequence is ticked, it has to * update the task according to the semantics of the task. In this * case it means that it has to analyze the current status of the current * active child (through {@link #getStatus()}). If the child is still * running, the ticking process just does nothing, since the sequence cannot * go on unless the current child finishes. Nevertheless, if the child has * successfully finished, the ExecutionSequence will have to spawn the next * task of the sequence. In order to do so, the ExecutionSequence will * access it through its ModelSequence. A new ExecutionTask will be created * for the next child of the ModelSequence (via the * ModelTask.createExecutor()) method, and then it will be * spawned (in this case, internalTick() will return * {@link Status#RUNNING}). However, if the child has not finished * successfully, the sequence has to be aborted, so the ticking process will * just return the failure status code {@link Status#FAILURE} (from the * outside, the tick() method will catch this termination code * and, as a result, it will fire a TaskEvent to notify the parent of the * ExecutionSequence). *

* The ticking process of the ExecutionParallel task is very different. When * internalTick() is called, the ExecutionParallel has to check * the current status of all of its children. If one of them has failed, * then all the children must be terminated, and the failure code * {@link Status#FAILURE} must be returned. If all of its children have * successfully finished, then the ExecutionParallel will just return * {@link Status#SUCCESS}. Otherwise, it will return {@link Status#RUNNING}. *

* Leaf tasks (low level task), such as actions and conditions, are * ticked in a different way. They do not have to analyze the termination * status of any child, since they have none. When a leaf task is ticked, it * should check the termination status of the process associated to the * task, and return a termination status accordingly. It should be noted * that when a task has been terminated ( {@link #terminate()}), * tick() does nothing. *

* It should be noted that when a task has been terminated ( * {@link #terminate()}), tick() does nothing. In particular, * tick() will not call internalTick(). * Therefore, it can be assumed that if internalTick() is * called, then this task has not been terminated, so the implementation * of this method should not even consider other cases. *

* An important aspect of this method is that, even though it returns an * Status object, only certain return values are allowed. In particular, * only {@link Status#SUCCESS}, {@link Status#FAILURE} and * {@link Status#RUNNING} can be returned. * * @return the status of the task after being ticked. */ protected abstract Status internalTick(); /** * This method stores the persistent state of an ExecutionTask. Some tasks * need to keep some information throughout the execution of the tree. *

* Some tasks in BTs are persistent in the sense that, after finishing, if * they are spawned again, they remember past information. Take for example * the "limit" task. A "limit" task allows to run its child node only a * certain number of times (for example, 5). After being spawned, it has to * remember how many times it has been run so far, so that, once the * threshold is exceeded, it fails. *

* The problem here is that tasks are destroyed when they leave the list of * tickable tasks. Thus, if the task needs to be used again, a new instance * for the task must be created, which, of course, will not remember past * information since it is a new object. This method is used for storing * information that needs to be used in the future when the task gets * created again. In particular, this method is called in the * {@link #tick()} function just after noticing that the task has finished * (when internalTick() returns a termination status). By doing * so, the task stores its state as soon as possible just in case it needs * to be spawned immediately afterwards. *

* This method must return the information it needs to remember in a a * {@link ITaskState} object, which must be comprehensible by the * {@link #restoreState(ITaskState)}, that is, it is * restoreState() that knows how to restore the state of the * task by reading the information that storeState() returns. *

* This method is called when the task finishes, so its implementation * should take into account that it will be called only when * {@link #internalTick()} returns a Status different from * {@link Status#RUNNING}. *

* This method may return null if the task does not need to store any state * information for future use. * * @return an ITaskState object with the persistent state information of the * task, for future use. The returned ITaskState must be readable by * restoreState(). */ protected abstract ITaskState storeState(); /** * This method follows the same semantics as {@link #storeState()}. However, * it is called when the task is terminated ( {@link #terminate()} ). When a * task is abruptly terminated, it may want to store some state information * for future use. It is in this method where such information should be * returned. * * @return an ITaskState object with the persistent state information of the * task, for future use. The returned ITaskState must be readable by * restoreState(). */ protected abstract ITaskState storeTerminationState(); /** * This method restores the persistent state of an ExecutionTask from an * {@link ITaskState} object. Some tasks need to keep some information * throughout the execution of the tree. *

* Some tasks in BTs are persistent in the sense that, after finishing, if * they are spawned again, they remember past information. Take for example * the "limit" task. A "limit" task allows to run its child node only a * certain number of times (for example, 5). After being spawned, it has to * remember how many times it has been run so far, so that, once the * threshold is exceeded, it fails. *

* The problem here is that tasks are destroyed when they leave the list of * tickable tasks. Thus, if the task needs to be used again, a new instance * for the task must be created, which, of course, will not remember past * information since it is a new object. This method is used for retrieving * from the ITaskState object past information that has previously returned * by either the {@link #storeState()} or the * {@link #storeTerminationState()} method. In particular, this method is * called in the {@link #spawn(IContext)} method, just before * {@link #internalSpawn()} gets called. By doing so, the task that is * created is able to restore past information needed to work. Since this * method is called before the task is spawned (that is, before * internalSpawn() is called), it must be assumed that the task * always keeps its past state. *

* This method reads the information that either storeState() * or storeTerminationState() has previously returned.. * Therefore, it must follow the same format as that of both methods. It the * input ITaskState is null, it means that there is no past information to * retrieve, so the task should be left unchanged. *

* This method may be left empty if the task does not need to restore any * past state. * * @param state * an ITaskState object containing past state information that * should be retrieved, or null in case there is no past * information to remember. */ protected abstract void restoreState(ITaskState state); /** * Returns the context of the task. * * @return the context of the task. */ public IContext getContext() { return this.context; } /** * Adds a task listener to this task. When there is a relevant change in the * status of this task, the listener will be notified by calling its * {@link ITaskListener#statusChanged(TaskEvent)} method. * * @param listener * the listener to add. */ public void addTaskListener(ITaskListener listener) { this.listeners.add(listener); } /** * Removes a listener from this task. * * @param listener * the task listener to remove. */ public void removeTaskListener(ITaskListener listener) { this.listeners.remove(listener); } /** * * @see jbt.execution.core.event.ITaskListener#statusChanged(jbt.execution.core.event.TaskEvent) */ public abstract void statusChanged(TaskEvent e); /** * Returns the current status of the task. * * @return the current status of the task. */ public Status getStatus() { return this.status; } /** * Returns the BTExecutor of this ExecutionTask. * * @return the BTExecutor of this ExecutionTask. */ public BTExecutor getExecutor() { return this.executor; } /** * Returns the ModelTask associated to this ExecutionTask. * * @return the ModelTask associated to this ExecutionTask. */ public ModelTask getModelTask() { return this.modelTask; } /** * Returns the position of the ExecutionTask in the execution tree. Note * that such position is not necessarily that of the underlying ModelTask. * * @return the position of the ExecutionTask in the execution tree. */ public Position getPosition() { return this.position; } /** * Returns true if the task has been spawned, and false otherwise. * * @return true if the task has been spawned, and false otherwise. */ public boolean getSpawned() { return this.spawnable == false; } /** * Returns true if the task has been terminated, and false otherwise. * * @return true if the task has been terminated, and false otherwise. */ public boolean getTerminated() { return this.terminated; } /** * Terminates the execution of this task and all the tasks below it. *

* When this method is called, the task is marked as terminated. From then * on, ticking the task will have no effect, and its status will be * {@link Status#TERMINATED}. Also, when terminating the task, it requests * to be removed from both the list of tickable and open nodes of the * BTExecutor. It also stores the task state after being terminated, by * calling {@link #storeTerminationState()}. *

* Finally, this method calls the abstract method * {@link #internalTerminate()}. internalTerminate() is the * method that actually terminates the execution of the task and all the * tasks below it, usually by calling terminate() on the active * children and by stopping any process associated to the task. * internalTerminate() must be defined for each subclass, since * their termination processes differ from one another. *

* This method cannot be called if the task has not been spawned yet (an * exception is thrown in that case). However, it is valid terminating a * task that has already been terminated, in which case nothing happens. */ public final void terminate() { if (!this.tickable) { throw new RuntimeException("Cannot terminate a task that has not been spawned yet."); } if (!this.terminated) { this.terminated = true; this.status = Status.TERMINATED; this.executor.requestRemovalFromList(BTExecutorList.TICKABLE, this); this.executor.requestRemovalFromList(BTExecutorList.OPEN, this); ITaskState taskState = this.storeTerminationState(); this.executor.setTaskState(getPosition(), taskState); this.internalTerminate(); } } /** * This method is called form {@link #terminate()}, and it is the one that * actually terminates the ExecutionTask as well as all the tasks below it. *

* internalTerminate() has to stop all the processes associated * to the ExecutionTask. For non-leaf tasks, this usually means that it has * to terminate all its active children (by recursively calling * terminate() on them). For leaf tasks, this means that it has * to terminate the processes associated to them, so that they stop doing * things. *

* For instance, an ExecutionParallel task has to call * terminate on all of its alive (still running) children. An * ExecutionSequence has to call terminate() on its current * alive child. A leaf task that, say, is carrying out some process in an * independent execution thread, should stop the thread. *

* This method can be called only once, and only once the task has already * been spawned, so the implementation does not have to even consider what * happens in other cases. */ protected abstract void internalTerminate(); /** * Fires a TaskEvent in all the listeners of this task. The TaskEvent will * inform about an important change in the status of the task. * * @param newStatus * the new status of the task. */ private void fireTaskEvent(Status newStatus) { for (ITaskListener l : this.listeners) { l.statusChanged(new TaskEvent(this, newStatus, this.getStatus())); } } /** * Returns the index that the ModelTask associated to this ExecutionTask * occupies in the list of children of its parent's children. Returns 0 if * the this ExecutionTask's ModelTask cannot be found. *

* The fact that the 0-case is contemplated is due to the existence of the * Subtree Lookup operator. The ModelSubtreeLookup does not have any * children, because it is a leaf node. However, the ExecutionSubtreeLookup * does have one child, which is the root of the tree that it is going to * emulate. As a result, the root of the tree emulated by the Subtree Lookup * cannot find itself as a child of its parent (the parent is the * ExecutionSubtreeLookup); as a workaround, we can return 0, since it is * the 0-th child of the ExecutionSubtreeLookup. */ private int getMove() { List parentsChildren = this.parent.getModelTask().getChildren(); Iterator iterator = parentsChildren.iterator(); ModelTask thisModelTask = this.getModelTask(); for (int i = 0; i < parentsChildren.size(); i++) { if (iterator.next() == thisModelTask) { return i; } } return 0; } /** * Checks if a Status returned by {@link #internalTick()} is valid or not. * internalTick() can only return {@link Status#SUCCESS}, * {@link Status#FAILURE} and {@link Status#RUNNING}. * * @param status * the status to check. * @return true if status can be returned by * internalTick(), and false otherwise. */ private static boolean validInternalTickStatus(Status status) { if (status == Status.TERMINATED || status == Status.UNINITIALIZED) { return false; } return true; } /** * * @see java.lang.Object#toString() */ public String toString() { return "[" + this.getClass().getSimpleName() + ", Status: " + this.status.toString() + "]"; } } ================================================ FILE: JBTCore/src/jbt/execution/core/IBTExecutor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import jbt.execution.core.ExecutionTask.Status; import jbt.model.core.ModelTask; /** * A behaviour tree executor (entity that is able to run a behaviour tree) must * implement this interface. *

* This is the part of the behaviour trees execution interface that is exposed * to clients. The execution process of behaviour trees is driven by ticks, * which means that they only perform calculations when they are ticked. At * every tick, they are given a little amount of time to think and evolve as * expected. If a behaviour tree is not ticked, then it does not go on. *

* An IBTExecutor defines two main methods, one for ticking the behaviour tree * it is running, and another one for terminating the tree. *

* Behaviour trees are represented by the ModelTask class. * * @author Ricardo Juan Palma Durán * */ public interface IBTExecutor { /** * This method gives the underlying BT a little amount of time to run. *

* Initially, a IBTExecutor is created to run a particular BT (ModelTask). * From then on, the tick() method is called to make the tree * evolve. *

* Usually the AI of a game is driven by ticks, which means that * periodically the AI is given some time to update its state (it checks the * current game state and performs some actions). BTs follow this pattern, * so whenever they are ticked, they are given a little amount of time to * think and behave as expected. If BTs are not ticked, they do not consume * CPU time and they not evolve. *

* By calling this method, the underlying BT will be ticked, so it will * think and evolve accordingly. *

* Note that ticking a tree that has already finished should have no effect * on the tree. */ public void tick(); /** * Terminates the execution of the behaviour tree. This method can be called * even if the tree has not started running yet or if it has already been * terminated. */ public void terminate(); /** * Returns the behaviour tree that this IBTExecutor is running. The * behaviour tree is represented by its root, which is a single ModelTask * object. * * @return the behaviour tree that this IBTExecutor is running. */ public ModelTask getBehaviourTree(); /** * Returns the execution status of the behaviour tree. It is the status of * the root of the tree. * * @return the execution status of the behaviour tree. It is the status of * the root of the tree. */ public Status getStatus(); /** * Returns the context that was associated to the root node of the behaviour * tree, and which is being used to run it. * * @return the context that was associated to the root node of the behaviour * tree, and which is being used to run it. */ public IContext getRootContext(); } ================================================ FILE: JBTCore/src/jbt/execution/core/IBTLibrary.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import jbt.model.core.ModelTask; import jbt.util.Pair; /** * Common interface for all behaviour tree libraries. A behaviour tree library * is just a repository from which behaviour trees can be retrieved by name. *

* This is an iterable interface (it extends {@link Iterable}) so that * all the behaviour trees of the library can be easily accessed. * * @author Ricardo Juan Palma Durán * */ public interface IBTLibrary extends Iterable> { /** * Returns the behaviour tree whose name is name. This method * returns the root task of the tree. * * @param name * the name of the tree to retrieve. * @return the behaviour tree whose name is name, or null in * case it does not exist. */ public ModelTask getBT(String name); } ================================================ FILE: JBTCore/src/jbt/execution/core/IContext.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import jbt.model.core.ModelTask; /** * Interface defining the context of a behavior tree task. The context of a task * is a set of variables, each one consisting of a name and a value. This * interface defines some methods for manipulating such variables. *

* A context also contains a set of behaviour trees that can be accessed by * tasks using the context. Reusability is very important for behaviour trees. * In general, a behaviour tree will be made of some tasks of its own as well as * references to other behaviour trees. When tasks need to retrieve references * to other behaviour trees, it will be the context that will provide with them. * Thus, the context defines a method for retrieving behaviour trees by name. * * @author Ricardo Juan Palma Durán * */ public interface IContext { /** * Returns the value of a variable whose name is name, or null * if it is not found. * * @param name * the name of the variable to retrieve. * * @return the value of a variable whose name is name, or null * if it does not exist. */ public Object getVariable(String name); /** * Sets the value of a variable. If the variable already existed, its value * is overwritten. value may be null in order to clear the * value of the variable. * * @param name * the name of the variable. * @param value * the value for the variable. * @return true if a variable with the same name already existed, and false * otherwise. */ public boolean setVariable(String name, Object value); /** * Clears the set of all the variables of the context. */ public void clear(); /** * Clears the value of a variable. This is equivalent to calling * {@link #setVariable(String, Object)} with a value of null for the second * argument. * * @param name * the name of a variable. * @return true if the variable existed, and false otherwise. */ public boolean clearVariable(String name); /** * Returns the behaviour tree whose name is name, or null in * case it does not exist it the context. * * @param name * the name of the tree to retrieve. * @return the root of the behaviour tree whose name is name, * or null in case it does not exist in the context. */ public ModelTask getBT(String name); } ================================================ FILE: JBTCore/src/jbt/execution/core/ITaskState.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; /** * The ITaskState interface represents the persistent state of a task in a * behaviour tree. This state is represented as a set of variables with name and * value. *

* Some tasks in BTs are persistent in the sense that, after finishing, if they * are spawned again, they remember past information. Take for example the * "limit" task. A "limit" task allows to run its child node only a certain * number of times (for example, 5). After being spawned, it has to remember how * many times it has been run so far, so that, once the threshold is exceeded, * it fails. *

* This interface represents the common functionality for classes that represent * the persistent state of a task. It just defines a method for retrieving the * value of a variable of the task's state. They way the task's state is * populated is not defined. * * @author Ricardo Juan Palma Durán * */ public interface ITaskState { /** * Returns the value of a variable whose name is name, or null * if it is not found. * * @param name * the name of the variable to retrieve. * * @return the value of a variable whose name is name, or null * if it does not exist. */ public Object getStateVariable(String name); } ================================================ FILE: JBTCore/src/jbt/execution/core/TaskState.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.Hashtable; import java.util.Map; /** * Default implementation of the {@link ITaskState} interface. It provides * methods for modifying the set of variables stored by the TaskState. * * @author Ricardo Juan Palma Durán * */ public class TaskState implements ITaskState { /** The set of variables. */ private Map variables; /** * Constructs an empty TaskState. */ public TaskState() { this.variables = new Hashtable(); } /** * * @see jbt.execution.core.ITaskState#getStateVariable(java.lang.String) */ public Object getStateVariable(String name) { return this.variables.get(name); } /** * Sets the value of a variable. If the value is null, the variable is * cleared. * * @param name * the name of the variable. * @param value * the value of the variable. * @return true if there was a variable with name name before * calling this method (it is therefore been overwritten), and false * otherwise. */ public boolean setStateVariable(String name, Object value) { if (value == null) { return this.variables.remove(name) == null ? false : true; } return this.variables.put(name, value) == null ? false : true; } /** * Clears all the variables of the TaskState. */ public void clear() { this.variables.clear(); } /** * Clears the value of a variable. * * @param name * the name of the variable. * @return true if the variable existed before calling this method, and * false otherwise. */ public boolean clearStateVariable(String name) { return this.variables.remove(name) == null ? false : true; } } ================================================ FILE: JBTCore/src/jbt/execution/core/TaskStateFactory.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core; import java.util.List; import java.util.Map; import java.util.Map.Entry; import jbt.util.Pair; /** * The TaskStateFactory implements the simple factory pattern, and allows * clients of the framework to create instances of {@link ITaskState} objects. * The methods provided by this factory allows the client to specify the set of * variables that the task state will contain. * * @author Ricardo Juan Palma Durán * */ public class TaskStateFactory { /** * Creates an ITaskState that contains the set of variables specified by * variables. Each variable is a Pair whose first element is * the variable's name and the second element is its value. * * @param variables * the list of variables that the ITaskState will contain. * @return an ITaskState that contains the set of variables in * variables. */ public static ITaskState createTaskState(List> variables) { TaskState taskState = new TaskState(); for (Pair variable : variables) { taskState.setStateVariable(variable.getFirst(), variable.getSecond()); } return taskState; } /** * Creates an ITaskState that contains the set of variables specified by * variables. Variables are stored in a Map whose keys are * variables' names and whose values are the values of the variables. * * @param variables * the list of variables that the ITaskState will contain. * @return an ITaskState that contains the set of variables in * variables. */ public static ITaskState createTaskState(Map variables) { TaskState taskState = new TaskState(); for (Entry variable : variables.entrySet()) { taskState.setStateVariable(variable.getKey(), variable.getValue()); } return taskState; } } ================================================ FILE: JBTCore/src/jbt/execution/core/event/ITaskListener.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core.event; import jbt.execution.core.ExecutionTask; /** * Interface for an entity that is able to receive events from tasks ( * {@link ExecutionTask}) whose status has changed in a relevant way. * * @author Ricardo Juan Palma Durán * */ public interface ITaskListener { /** * Method called when an important change in the status of a task has taken * place. * * @param e * the TaskEvent with all the information about the change in the * status of the task. */ public void statusChanged(TaskEvent e); } ================================================ FILE: JBTCore/src/jbt/execution/core/event/TaskEvent.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.core.event; import java.util.EventObject; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ExecutionTask.Status; /** * A TaskEvent is an event that is generated by tasks ({@link ExecutionTask}) to * signal that a relevant change in the status of a task has taken place. * * @author Ricardo Juan Palma Durán * */ public class TaskEvent extends EventObject { private static final long serialVersionUID = 1L; /** The new status of the task. */ private Status newStatus; /** The previous status of the task. */ private Status previousStatus; /** * Creates a TaskEvent with a particular ExcutionTask as source of the * event. The source (source) is the task whose status has * changed, and newStatus is the new status of the task. * * @param source * the task whose status has changed. * @param newStatus * the new status of the task. */ public TaskEvent(ExecutionTask source, Status newStatus, Status previousStatus) { super(source); this.newStatus = newStatus; this.previousStatus = previousStatus; } /** * Returns the new status associated to the task. * * @return the new status associated to the task. */ public Status getNewStatus() { return this.newStatus; } /** * Returns the previous status associated to the task. * * @return the previous status associated to the task. */ public Status getPreviousStatus() { return this.previousStatus; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionComposite.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelComposite; /** * Base class for all the ExecutionTask subclasses that are able to run * composite tasks (that is, classes that inherit from ModelComposite). * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionComposite extends ExecutionTask { /** * Creates an ExecutionComposite that is able to run a particular * ModelComposite task. * * @param modelTask * the ModelComposite task to run. * @param executor * the BTExecutor that will manage this ExecutionComposite. * @param parent * the parent ExecutionTask of this task. */ public ExecutionComposite(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelComposite)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelComposite.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionDynamicPriorityList.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.IBTExecutor; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelDynamicPriorityList; import jbt.util.Pair; /** * ExecutionDynamicPriorityList is the ExecutionTask that knows how to run a * ModelDynamicPriorityList. * * @author Ricardo Juan Palma Durán * */ public class ExecutionDynamicPriorityList extends ExecutionComposite { /** List of the children (ModelTask) of this task. */ private List children; /** Flag telling if the spawning process has failed. */ private boolean spawnFailed; /** Flag that tells if there is a spawned child. */ private boolean stillNotSpawned; /** Index of the currently active child. */ private int activeChildIndex; /** Currently active child. */ private ExecutionTask activeChild; /** * List containing the IBTExecutors in charge of running the guards. The i-th element of this * list manages the guard of the i-th child ( {@link #children}). Note that if a guard is null, * its corresponding IBTExecutor is also null. */ private List guardsExecutors; /** * This List contains the current evaluation status of all the guards. If a guard is null, its * corresponding status is {@link Status#SUCCESS} (null guards are evaluated to true). */ private List guardsResults; /** * Index of the current most relevant guard. All the guards before it have finished in failure. * This represents the guard such that, if its status changes to success, then it would be the * one selected by the dynamic priority list. */ private int indexMostRelevantGuard = 0; /** * Creates an ExecutionDynamicPriorityList that is able to run a ModelDynamicPriorityList task * and that is managed by a BTExecutor. * * @param modelTask * the ModelDynamicPriorityList that this ExecutionDynamicPriorityList is going to * run. * @param executor * the BTExecutor in charge of running this ExecutionDynamicPriorityList. * @param parent * the parent ExecutionTask of this task. */ public ExecutionDynamicPriorityList(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelDynamicPriorityList)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelDynamicPriorityList.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first child with active guard. It also requests to be inserted into the list of * tickable nodes of the BTExecutor, since this task has to check its children's guards all the * time. If there is no active guard, the spawning process is considered to have failed, so * {@link #internalTick()} will return {@link Status#FAILURE}. If some guards are still running * the spawning process is not considered to have started yet. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { /* * The dynamic priority list has to be inserted into the list of tickable nodes because it * has to check its children's guards all the time. */ this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); this.children = this.getModelTask().getChildren(); /* Initialize guard executors. */ this.guardsExecutors = new Vector(); this.guardsResults = new Vector(); for (ModelTask child : this.children) { if (child.getGuard() != null) { this.guardsExecutors.add(new BTExecutor(child.getGuard(), this.getContext())); this.guardsResults.add(Status.RUNNING); } else { this.guardsExecutors.add(null); this.guardsResults.add(Status.SUCCESS); } } /* Evaluate guards. */ resetGuardsEvaluation(); Pair activeGuard = evaluateGuards(); /* If all guards have failed, the spawning process has also failed. */ if (activeGuard.getFirst() == Status.FAILURE) { this.spawnFailed = true; } else if (activeGuard.getFirst() == Status.RUNNING) { /* * If not all the guards have been evaluated yet, the spawning process is not considered * to have started. */ this.stillNotSpawned = true; } else { /* * If all the guards have been evaluated and one succeeded, spawn the corresponding * child. */ this.spawnFailed = false; this.stillNotSpawned = false; this.activeChildIndex = activeGuard.getSecond(); this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); /* Reset the guards evaluators. */ resetGuardsEvaluation(); } } /** * Just terminates the currently active child (if there is one). * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { /* * This null check is necessary. Keep in mind that the dynamic priority list may not have an * active child, since the spawning process may have failed or not started yet. In such a * case, if it is terminated, "this.activeChild" will be null. */ if (this.activeChild != null) { this.activeChild.terminate(); } /* Terminate the guards executors. */ for (IBTExecutor guardExecutor : this.guardsExecutors) { if (guardExecutor != null) { guardExecutor.terminate(); } } } /** * Checks if there is an active guard with a priority higher than that of the active child. If * there is such a task, it terminates the active child and spawns the child of the guard with * higher priority, and {@link Status#RUNNING} is returned. If there is no such task, then the * status of the active child is returned. *

* If the spawning process failed, this method just returns {@link Status#FAILURE}. If the * spawning process has not finished yet, this method keeps evaluating the guards, and returns * {@link Status#RUNNING}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { /* If the spawning process failed, return failure. */ if (this.spawnFailed) { return Status.FAILURE; } /* Evaluate guards. */ Pair activeGuard = evaluateGuards(); /* * If no child has been spawned yet (not all the guards had completed yet in the * internalSpawn() method)... */ if (this.stillNotSpawned) { /* If all the guards have failed, return failure. */ if (activeGuard.getFirst() == Status.FAILURE) { return Status.FAILURE; } else if (activeGuard.getFirst() == Status.RUNNING) { /* * If not all the guards have finished, do no nothing (return RUNNING). */ } else { /* * If all the guards have been evaluated and one succeeded, spawn the child. */ this.spawnFailed = false; this.stillNotSpawned = false; this.activeChildIndex = activeGuard.getSecond(); this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); /* Reset the guards evaluators. */ resetGuardsEvaluation(); } return Status.RUNNING; } /* If this point has been reached, there must be an active child. */ if (activeGuard.getFirst() == Status.FAILURE) { /* If all the guards have failed, return failure. */ return Status.FAILURE; } else if (activeGuard.getFirst() == Status.RUNNING) { /* * If the guards are being evaluated, return the status of the active child. */ return this.activeChild.getStatus(); } else { if (activeGuard.getSecond() != this.activeChildIndex) { /* * If the child with the highest priority guard has changed, terminate the currently * active child. */ this.activeChild.terminate(); this.activeChildIndex = activeGuard.getSecond(); /* * Spawn the new child. */ this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); resetGuardsEvaluation(); return Status.RUNNING; } else { /* * If the child with the highest priority guard has not changed, return the status * of the active child. */ resetGuardsEvaluation(); return this.activeChild.getStatus(); } } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) { } /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Resets the evaluation of all the guards. This method leaves all the guard executors ( * {@link #guardsExecutors}) ready to start again the evaluation of the guards. It internally * terminates the IBTExecutor of each guard, creates a new one, and then ticks it. */ private void resetGuardsEvaluation() { for (int i = 0; i < this.guardsExecutors.size(); i++) { BTExecutor guardExecutor = this.guardsExecutors.get(i); if (guardExecutor != null) { guardExecutor.terminate(); this.guardsResults.set(i, Status.RUNNING); BTExecutor newExecutor = new BTExecutor(guardExecutor.getBehaviourTree(), this.getContext()); newExecutor.copyTasksStates(guardExecutor); newExecutor.tick(); this.guardsExecutors.set(i, newExecutor); } } this.indexMostRelevantGuard = 0; } /** * Evaluate all the guards that have not finished yet, that is, those whose result in * {@link #guardsResults} is {@link Status#RUNNING}, by ticking them. *

* If all the guards have finished in failure, this method returns a Pair whose first element is * {@link Status#FAILURE}. If guards' evaluation has not completed yet, the first element of the * Pair contains {@link Status#RUNNING}. If all the guards have been evaluated and at least one * has succeeded, the first element of the Pair is {@link Status#SUCCESS}, and the second one is * the index, over the list of guards ({@link #guardsExecutors}) , of the first guard (that with * the highest priority) that has succeeded. * */ private Pair evaluateGuards() { /* * Tick all the guards that are still running. If one changes its status to SUCCESS and it * matches the guard associated to "indexMostRelevantGuard", then the guards' evaluation is * over and that is the selected guard. */ for (int i = 0; i < this.guardsExecutors.size(); i++) { IBTExecutor guardExecutor = this.guardsExecutors.get(i); if (guardExecutor != null) { if (this.guardsResults.get(i) == Status.RUNNING) { longTick(guardExecutor); this.guardsResults.set(i, guardExecutor.getStatus()); if (guardExecutor.getStatus() != Status.RUNNING) { /* * If the guard has finished, we check if it matches the * "most relevant guard". */ if (i == this.indexMostRelevantGuard) { if (guardExecutor.getStatus() == Status.SUCCESS) { return new Pair(Status.SUCCESS, i); } else { /* * If the guard failed, we have to find the next * "most relevant guard" and update "indexMostRelevantGuard" * accordingly. For that we check the status of the following * guards. If we find a successful guard before any running guard, * then the guards' evaluation is over, and that is the selected * guard. If we find a running guard before, then that's the new * "most relevant guard". Otherwise, the evaluation has failed, and * there is no successful guard. */ boolean oneRunning = false; for (int k = this.indexMostRelevantGuard + 1; k < this.guardsExecutors .size(); k++) { if (this.guardsExecutors.get(k) != null) { Status currentResult = this.guardsExecutors.get(k) .getStatus(); if (currentResult == Status.RUNNING) { this.indexMostRelevantGuard = k; oneRunning = true; break; } else if (currentResult == Status.SUCCESS) { return new Pair(Status.SUCCESS, k); } } else { return new Pair(Status.SUCCESS, k); } } if (!oneRunning) { return new Pair(Status.FAILURE, -1); } } } } } } else { /* Remember, null guard means successful evaluation. */ if (i == this.indexMostRelevantGuard) { return new Pair(Status.SUCCESS, i); } } } return new Pair(Status.RUNNING, -1); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } /** * This method ticks executor {@value #NUM_TICKS_LONG_TICK} times. If the executor * finishes earlier, it is not ticked anymore, and the ticking process stops. * * @param executor * the IBTExecutor that is ticked. */ private void longTick(IBTExecutor executor) { if (executor.getStatus() == Status.RUNNING || executor.getStatus() == Status.UNINITIALIZED) { int counter = 0; do { executor.tick(); counter++; } while (executor.getStatus() == Status.RUNNING && counter < NUM_TICKS_LONG_TICK); } } /** Number of ticks performed in each long tick ({@link #longTick(IBTExecutor)}). */ private static final int NUM_TICKS_LONG_TICK = 20; } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionParallel.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.LinkedList; import java.util.List; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelParallel; import jbt.model.task.composite.ModelParallel.ParallelPolicy; /** * ExecutionParallel is the ExecutionTask that knows how to run a ModelParallel * task. * * @author Ricardo Juan Palma Durán * */ public class ExecutionParallel extends ExecutionComposite { /** Policy of the parallel task. */ private ParallelPolicy policy; /** List of the ModelTask children of this task. */ private List modelChildren; /** List of the ExecutionTask children of this task. */ private List executionChildren; /** * Creates an ExecutionParallel that is able to run a ModelParallel task and * that is managed by a BTExecutor. * * @param modelTask * the ModelParallel that this ExecutionParallel is going to run. * @param executor * the BTExecutor in charge of running this ExecutionParallel. * @param parent * the parent ExecutionTask of this task. */ public ExecutionParallel(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelParallel)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelParallel.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } this.policy = ((ModelParallel) modelTask).getPolicy(); this.modelChildren = modelTask.getChildren(); this.executionChildren = new LinkedList(); } /** * Spawns every single child of the task. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { if (this.policy == ParallelPolicy.SEQUENCE_POLICY) { sequencePolicySpawn(); } else { selectorPolicySpawn(); } } /** * Terminates all of its children. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { if (this.policy == ParallelPolicy.SEQUENCE_POLICY) { sequencePolicyTerminate(); } else { selectorPolicyTerminate(); } } /** * Ticks this ExecutionParallel. This process varies depending on the * policy. that is being followed. See {@link ModelParallel} for more * information. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { if (this.policy == ParallelPolicy.SEQUENCE_POLICY) { return sequencePolicyTick(); } else { return selectorPolicyTick(); } } /** * Carries out the spawning process when the policy is * {@link ParallelPolicy#SEQUENCE_POLICY}. */ private void sequencePolicySpawn() { /* First, create an ExecutionTask for all of the childre. */ for (ModelTask t : this.modelChildren) { this.executionChildren.add(t.createExecutor(this.getExecutor(), this)); } /* Then, spawn them all. */ for (ExecutionTask t : this.executionChildren) { t.addTaskListener(this); t.spawn(this.getContext()); } } /** * Carries out the spawning process when the policy is * {@link ParallelPolicy#SELECTOR_POLICY}. */ private void selectorPolicySpawn() { sequencePolicySpawn(); } /** * Carries out the termination process when the policy is * {@link ParallelPolicy#SEQUENCE_POLICY}. */ private void sequencePolicyTerminate() { /* Just terminate all of its children. */ for (ExecutionTask t : this.executionChildren) { t.terminate(); } } /** * Carries out the termination process when the policy is * {@link ParallelPolicy#SELECTOR_POLICY}. */ private void selectorPolicyTerminate() { sequencePolicyTerminate(); } /** * Carries out the ticking process when the policy is * {@link ParallelPolicy#SEQUENCE_POLICY}. * * @return the task status after the tick. */ private Status sequencePolicyTick() { /* * If one child has failed, then return Status.FAILURE. Otherwise, if * there is at least one child still running, return Status.RUNNING. * Otherwise, return Status.SUCCESS. */ boolean oneRunning = false; for (ExecutionTask t : this.executionChildren) { Status currentStatus = t.getStatus(); if (currentStatus == Status.RUNNING) { oneRunning = true; } else if (currentStatus == Status.FAILURE || currentStatus == Status.TERMINATED) { sequencePolicyTerminate(); return Status.FAILURE; } } if (!oneRunning) { return Status.SUCCESS; } else { return Status.RUNNING; } } /** * Carries out the ticking process when the policy is * {@link ParallelPolicy#SELECTOR_POLICY}. * * @return the task status after the tick. */ private Status selectorPolicyTick() { /* * If one child has succeeded, then return Status.SUCCESS. Otherwise, if * there is at least one child still running, return Status.RUNNING. * Otherwise, return Status.FAILURE. */ boolean oneRunning = false; for (ExecutionTask t : this.executionChildren) { Status currentStatus = t.getStatus(); if (currentStatus == Status.SUCCESS) { sequencePolicyTerminate(); return Status.SUCCESS; } else if (currentStatus == Status.RUNNING) { oneRunning = true; } } if (!oneRunning) { return Status.FAILURE; } else { return Status.RUNNING; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the ExecutionParallel task evolve * according to the status of its children. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { /* * TODO: the TaskEvent could be used to improve the efficiency of this * method, since we only have to analyse the status of the task that * fired the event, not the status of all the tasks (which is what * tick() does). */ this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionRandomSelector.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.Collections; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelRandomSelector; /** * ExecutionRandomSelector is the ExecutionTask that knows how to run a * ModelRandomSelector. * * @author Ricardo Juan Palma Durán * */ public class ExecutionRandomSelector extends ExecutionComposite { /** * Currently active child. */ private ExecutionTask activeChild; /** * The currently active child of the selector. This integer is an index over * the elements of {@link #order}. */ private int activeChildIndex; /** * The list of children of this task. */ private List children; /** * List storing a sequence of integers with the order in which the children * of this task must be evaluated. This list is computed when the task is * spawned. */ private List order; /** * Constructs an ExecutionRandomSelector to run a specific * ModelRandomSelector. * * @param modelTask * the ModelRandomSelector to run. * @param executor * the BTExecutor that will manage this ExecutionRandomSelector. * @param parent * the parent ExecutionTask of this task. */ public ExecutionRandomSelector(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelRandomSelector)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelRandomSelector.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first task (randomly selected). * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.children = this.getModelTask().getChildren(); /* * First we initialize the list with the order in which the list of * children will be evaluated. */ this.order = new Vector(); for (int i = 0; i < this.children.size(); i++) { this.order.add(i); } Collections.shuffle(this.order); /* * Then we spawn the first child. */ this.activeChildIndex = 0; this.activeChild = this.children.get(this.order.get(this.activeChildIndex)).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); } /** * Just terminates the currently active child. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.activeChild.terminate(); } /** * Checks the status of the currently active child. If it is running, * {@link Status#RUNNING} is returned. If it has successfully finished, it * returns {@link Status#SUCCESS}. If it has failed, it tries to spawn the * next child (returning {@link Status#RUNNING}). If it was the last child, * returns {@link Status#FAILURE}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.activeChild.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } else if (childStatus == Status.SUCCESS) { return Status.SUCCESS; } else { /* If it was the last child of the list, return failure. */ if (this.activeChildIndex == this.children.size() - 1) { return Status.FAILURE; } this.activeChildIndex++; this.activeChild = this.children.get(this.order.get(this.activeChildIndex)) .createExecutor(this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); return Status.RUNNING; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionRandomSequence.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.Collections; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelRandomSequence; /** * ExecutionRandomSequence is the ExecutionTask that knows how to run a * ModelRandomSequence. * * @author Ricardo Juan Palma Durán * */ public class ExecutionRandomSequence extends ExecutionComposite { /** * Currently active child. */ private ExecutionTask activeChild; /** * The currently active child of the sequence. This integer is an index over * the elements of {@link #order}. */ private int activeChildIndex; /** * The list of children of this task. */ private List children; /** * List storing a sequence of integers with the order in which the children * of this task must be evaluated. This list is computed when the task is * spawned. */ private List order; /** * Constructs an ExecutionRandomSequence to run a specific * ModelRandomSequence. * * @param modelTask * the ModelRandomSequence to run. * @param executor * the BTExecutor that will manage this ExecutionRandomSequence. * @param parent * the parent ExecutionTask of this task. */ public ExecutionRandomSequence(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelRandomSequence)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelRandomSequence.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first task (randomly selected). * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.children = this.getModelTask().getChildren(); /* * First we initialize the list with the order in which the list of * children will be evaluated. */ this.order = new Vector(); for (int i = 0; i < this.children.size(); i++) { this.order.add(i); } Collections.shuffle(this.order); /* * Then we spawn the first child. */ this.activeChildIndex = 0; this.activeChild = this.children.get(this.order.get(this.activeChildIndex)).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); } /** * Just terminates the currently active child. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.activeChild.terminate(); } /** * Checks the status of the currently active child. If it is running, * {@link Status#RUNNING} is returned. If it has finished in failure, * {@link Status#FAILURE} is returned. If it has finished successfully, it * tries to spawn the next child (and returns {@link Status#RUNNING}). If it * was the last child of the sequence, returns {@link Status#SUCCESS}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.activeChild.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } else if (childStatus == Status.SUCCESS) { /* If it was the last child of the sequence, returns success. */ if (this.activeChildIndex == this.children.size() - 1) { return Status.SUCCESS; } this.activeChildIndex++; this.activeChild = this.children.get(this.order.get(this.activeChildIndex)) .createExecutor(this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); return Status.RUNNING; } else { return Status.FAILURE; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionSelector.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.List; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelSelector; /** * ExecutionSelector is the ExecutionTask that is able to run a ModelSelector * task. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSelector extends ExecutionComposite { /** Index of the active child. */ private int activeChildIndex; /** The currently active child. */ private ExecutionTask activeChild; /** List of the ModelTask children of the selector. */ private List children; /** * Creates an ExecutionSelector that is able to run a ModelSelector task and * that is managed by a BTExecutor. * * @param modelTask * the ModelSelector that this ExecutionSelector is going to run. * @param executor * the BTExecutor in charge of running this ExecutionSelector. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSelector(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSelector)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSelector.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first child. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.activeChildIndex = 0; this.children = this.getModelTask().getChildren(); this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); } /** * Terminates the current active child. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.activeChild.terminate(); } /** * Checks if the currently active child has finished. It it has not, it * returns {@link Status#RUNNING}. If it has finished successfully, it * returns {@link Status#SUCCESS}. If it has finished in failure, then: *

    *
  • If it was the last child of the selector, returns * {@link Status#FAILURE}. *
  • Otherwise, it spawns the next child of the selector and returns * {@link Status#RUNNING}. *
* * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.activeChild.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } else if (childStatus == Status.SUCCESS) { return Status.SUCCESS; } else { /* * If the current child has failed, and it was the last one, return * failure. */ if (this.activeChildIndex == this.children.size() - 1) { return Status.FAILURE; } else { /* * Otherwise, if it was not the last child, spawn the next * child. */ this.activeChildIndex++; this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); return Status.RUNNING; } } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the ExecutionSelector evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionSequence.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.List; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelSequence; /** * ExecutionSequence is the ExecutionTask that knows how to run a ModelSequence * task. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSequence extends ExecutionTask { /** Index of the active child. */ private int activeChildIndex; /** The currently active child. */ private ExecutionTask activeChild; /** List of the ModelTask children of the sequence. */ private List children; /** * Creates an ExecutionSequence that is able to run a ModelSequence task and * that is managed by a BTExecutor. * * @param modelTask * the ModelSequence that this ExecutionSequence is going to run. * @param executor * the BTExecutor in charge of running this ExecutionSequence. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSequence(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSequence)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSequence.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first child of the sequence. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { /* * Spawn the first child of the sequence. */ this.activeChildIndex = 0; this.children = this.getModelTask().getChildren(); this.activeChild = this.children.get(0).createExecutor(this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); } /** * Terminates the currently active child. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { /* Just terminate the active child. */ this.activeChild.terminate(); } /** * Checks if the currently active child has finished. If it has not * finished, returns {@link Status#SUCCESS}. If it has finished in failure, * returns {@link Status#FAILURE}. If it has finished successfully, it * checks if there is any remaining child. If so, it spawns it. Otherwise, * returns {@link Status#SUCCESS}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.activeChild.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } else if (childStatus == Status.FAILURE || childStatus == Status.TERMINATED) { return Status.FAILURE; } else { if (this.activeChildIndex == this.children.size() - 1) { /* * If this was the last child, return success. */ return Status.SUCCESS; } else { /* * If the current child has finished successfully, but it is not * the last one, spawn the next child. */ this.activeChildIndex++; this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); return Status.RUNNING; } } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to check if the ExecutionSequence can evolve * due to the change in the state of the task that was listening to. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/composite/ExecutionStaticPriorityList.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.composite; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ExecutionTask; import jbt.execution.core.IBTExecutor; import jbt.execution.core.ITaskState; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.composite.ModelStaticPriorityList; import jbt.util.Pair; /** * ExecutionStaticPriorityList is the ExecutionTask that knows how to run a * ModelStaticPriorityList. * * @author Ricardo Juan Palma Durán * */ public class ExecutionStaticPriorityList extends ExecutionComposite { /** List of the children (ModelTask) of this task. */ private List children; /** Flag telling if the spawning process has failed. */ private boolean spawnFailed; /** Flag that tells if there is a spawned child. */ private boolean stillNotSpawned; /** Index of the currently active child. */ private int activeChildIndex; /** Currently active child. */ private ExecutionTask activeChild; /** * List containing the IBTExecutors in charge of running the guards. The * i-th element of this list manages the guard of the i-th child ( * {@link #children}). Note that if a guard is null, its corresponding * IBTExecutor is also null. */ private List guardsExecutors; /** * This List contains the current evaluation status of all the guards. If a * guard is null, its corresponding status is {@link Status#SUCCESS} (null * guards are evaluated to true). */ private List guardsResults; /** * Creates an ExecutionStaticPriorityList that is able to run a * ModelStaticPriorityList task and that is managed by a BTExecutor. * * @param modelTask * the ModelStaticPriorityList that this * ExecutionStaticPriorityList is going to run. * @param executor * the BTExecutor in charge of running this * ExecutionStaticPriorityList. * @param parent * the parent ExecutionTask of this task. */ public ExecutionStaticPriorityList(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelStaticPriorityList)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelStaticPriorityList.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the first child with active guard. If there is no active guard, * the spawning process is considered to have failed, so * {@link #internalTick()} will return {@link Status#FAILURE}. If some * guards are still running the spawning process is not considered to have * started yet. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.children = this.getModelTask().getChildren(); /* Initialize guard executors. */ this.guardsExecutors = new Vector(); this.guardsResults = new Vector(); for (ModelTask child : this.children) { if (child.getGuard() != null) { this.guardsExecutors.add(new BTExecutor(child.getGuard(), this.getContext())); this.guardsResults.add(Status.RUNNING); } else { this.guardsExecutors.add(null); this.guardsResults.add(Status.SUCCESS); } } /* Evaluate guards. */ resetGuardsEvaluation(); Pair activeGuard = evaluateGuards(); /* * Flag that tells if the static priority list must be inserted into the * list of tickable nodes. */ boolean insertIntoTickableNodesList = false; /* * If all the guards have failed, the spawning process has also failed. * In such a case, the task must be inserted into the list of tickable * nodes. */ if (activeGuard.getFirst() == Status.FAILURE) { this.spawnFailed = true; insertIntoTickableNodesList = true; } else if (activeGuard.getFirst() == Status.RUNNING) { /* * If not all the guards have been evaluated yet, the spawning * process is not considered to have started. In such a case, the * task must be inserted into the list of tickable nodes. */ this.stillNotSpawned = true; insertIntoTickableNodesList = true; } else { /* * If all the guards have been evaluated and one succeeded, spawn * the corresponding child. */ this.spawnFailed = false; this.stillNotSpawned = false; this.activeChildIndex = activeGuard.getSecond(); this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); } /* Insert into the list of tickable nodes if required. */ if (insertIntoTickableNodesList) { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); } } /** * If the spawning process has not finished yet (because there are some * guards running), then this method keeps evaluating the guards, and * returns {@link Status#RUNNING}. Whenever there is an active child * (because the spawning process has finished), its status is returned. *

* If the spawning process failed, this method just returns * {@link Status#FAILURE}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { /* If the spawning process failed, return failure. */ if (this.spawnFailed) { return Status.FAILURE; } /* * If no child has been spawned yet (not all the guards had completed in * the internalSpawn() method)... */ if (this.stillNotSpawned) { /* Evaluate guards. */ Pair activeGuard = evaluateGuards(); /* If all the guards have failed, return failure. */ if (activeGuard.getFirst() == Status.FAILURE) { return Status.FAILURE; } else if (activeGuard.getFirst() == Status.RUNNING) { /* * If not all the guards have finished, do no nothing (return * RUNNING). */ } else { /* * If all the guards have been evaluated and one succeeded, * spawn the child. In this case, the static priority list * must be removed from the list of tickable nodes. */ this.spawnFailed = false; this.stillNotSpawned = false; this.activeChildIndex = activeGuard.getSecond(); this.activeChild = this.children.get(this.activeChildIndex).createExecutor( this.getExecutor(), this); this.activeChild.addTaskListener(this); this.activeChild.spawn(this.getContext()); this.getExecutor().requestRemovalFromList(BTExecutorList.TICKABLE, this); } return Status.RUNNING; } /* If this point has been reached, there must be an active child. */ return this.activeChild.getStatus(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(jbt.execution.core.ITaskState) */ protected void restoreState(ITaskState state) { } /** * Just ticks this task. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Just terminates the currently active child (if there is one). * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { /* * This null check is necessary. Keep in mind that the static priority * list may not have an active child, since the spawning process may * have failed or not started yet. In such a case, if it is terminated, * "this.activeChild" will be null. */ if (this.activeChild != null) { this.activeChild.terminate(); } /* Terminate the guards executors. */ for (IBTExecutor guardExecutor : this.guardsExecutors) { if (guardExecutor != null) { guardExecutor.terminate(); } } } /** * Resets the evaluation of all the guards. This method leaves all the guard * executors ({@link #guardsExecutors}) ready to start again the evaluation * of the guards. It internally terminates the IBTExecutor of each guard, * creates a new one, and then ticks it. */ private void resetGuardsEvaluation() { for (int i = 0; i < this.guardsExecutors.size(); i++) { BTExecutor guardExecutor = this.guardsExecutors.get(i); if (guardExecutor != null) { guardExecutor.terminate(); this.guardsResults.set(i, Status.RUNNING); BTExecutor newExecutor = new BTExecutor(guardExecutor.getBehaviourTree(), this.getContext()); newExecutor.copyTasksStates(guardExecutor); newExecutor.tick(); this.guardsExecutors.set(i, newExecutor); } } } /** * Evaluate all the guards that have not finished yet, that is, those whose * result in {@link #guardsResults} is {@link Status#RUNNING}, by ticking * them. *

* If all the guards have finished in failure, this method returns a Pair * whose first element is {@link Status#FAILURE}. If there is at least one * guard still being evaluated, the first element of the Pair contains * {@link Status#RUNNING}. If all the guards have been evaluated and at * least one has succeeded, the first element of the Pair is * {@link Status#SUCCESS}, and the second one is the index, over the list of * guards ({@link #guardsExecutors}) , of the first guard (that with the * highest priority) that has succeeded. * */ private Pair evaluateGuards() { boolean oneRunning = false; /* First, evaluate all the guards that have not finished yet. */ for (int i = 0; i < this.guardsExecutors.size(); i++) { IBTExecutor guardExecutor = this.guardsExecutors.get(i); if (guardExecutor != null) { if (this.guardsResults.get(i) == Status.RUNNING) { guardExecutor.tick(); this.guardsResults.set(i, guardExecutor.getStatus()); if (this.guardsResults.get(i) == Status.RUNNING) { oneRunning = true; } } } } /* If there is at least one still running... */ if (oneRunning) { return new Pair(Status.RUNNING, -1); } /* If all of them have finished we check which one succeeded first. */ for (int i = 0; i < this.guardsResults.size(); i++) { if (this.guardsResults.get(i) == Status.SUCCESS) { return new Pair(Status.SUCCESS, i); } } /* Otherwise, the evaluation has failed. */ return new Pair(Status.FAILURE, -1); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionDecorator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelDecorator; /** * Base class for all the ExecutionTask subclasses that are able to run * decorator tasks (that is, classes that inherit from ModelDecorator). * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionDecorator extends ExecutionTask { /** * Creates an ExecutionDecorator that is able to run a particular * ModelDecorator task. * * @param modelTask * the ModelDecorator task to run. * @param executor * the BTExecutor that will manage this ExecutionDecorator. * @param parent * the parent ExecutionTask of this task. */ public ExecutionDecorator(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelDecorator)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelDecorator.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionHierarchicalContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.context.HierarchicalContext; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelHierarchicalContextManager; import jbt.model.task.decorator.ModelDecorator; /** * ExecutionHierarchicalContextManager is the ExecutionTask that knows how to run a * ModelHierarchicalContextManager. * * @author Ricardo Juan Palma Durán * */ public class ExecutionHierarchicalContextManager extends ExecutionDecorator { /** The child task. */ private ExecutionTask child; /** * Constructs an ExecutionHierarchicalContextManager that knows how to run a * ModelHierarchicalContextManager. * * @param modelTask * the ModelHierarchicalContextManager to run. * @param executor * the BTExecutor that will manage this ExecutionHierarchicalContextManager. * @param parent * the parent ExecutionTask of this task. */ public ExecutionHierarchicalContextManager(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelHierarchicalContextManager)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelHierarchicalContextManager.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the child task. This method creates a new HierarchicalContext, * sets its parent to the context of the ExecutionHierarchicalContextManager, and spawns * the child task using this HierarchicalContext. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { HierarchicalContext newContext = new HierarchicalContext(); newContext.setParent(this.getContext()); this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(newContext); } /** * Just terminates the child task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } /** * Returns the current status of the child. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return this.child.getStatus(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the tass evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionInterrupter.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelInterrupter; /** * ExecutionInterrupter is the ExecutionTask that knows how to run a * ModelInterrupter. In order to interrupt the ExecutionInterrupter, * {@link #interrupt(Status)} must be called. * * @author Ricardo Juan Palma Durán * */ public class ExecutionInterrupter extends ExecutionDecorator { /** * Flag that tells if the ExecutionInterrupter has already been interrupted. */ private boolean interrupted; /** * Status code that this ExecutionInterrupter should return in case it is * interrupted. */ private Status statusSet; /** * The child ExecutionTask of this ExecutionInterrupter. */ private ExecutionTask executionChild; /** * Creates an ExecutionInterrupter that is able to run a ModelInterrupter * task and that is managed by a BTExecutor. * * @param modelTask * the ModelInterrupter that this ExecutionInterrupter is going * to run. * @param executor * the BTExecutor in charge of running this ExecutionInterrupter. * @param parent * the parent ExecutionTask of this task. */ public ExecutionInterrupter(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelInterrupter)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelInterrupter.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } this.interrupted = false; } /** * Spawns its child and registers itself into the list of interrupters of * the BTExecutor. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.executionChild = ((ModelInterrupter) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.executionChild.addTaskListener(this); /* * Register the ExecutionInterrupter so that * ExecutionPerformInterruption can find it. */ this.getExecutor().registerInterrupter(this); this.executionChild.spawn(this.getContext()); } /** * Terminates the child task and unregister itself from the list of * interrupters of the BTExecutor. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { /* * Unregister the ExecutionInterrupter so that it is no longer available * to ExecutionPerformInterruption. */ this.getExecutor().unregisterInterrupter(this); /* * It is important to cancel any request for insertion that this task * has. If the task has been interrupted, it will have requested to be * inserted into the list of tickable nodes. However, if it is then * terminated, we do not want it to be inserted into the list of * tickable nodes, so the request made in "interrupt()" must be * cancelled. */ if (this.interrupted) { this.getExecutor().cancelInsertionRequest(BTExecutorList.TICKABLE, this); } this.executionChild.terminate(); } /** * If the ExecutionInterrupter has been interrupted, returns the status that * was passed to the {@link #interrupt(Status)} method. Otherwise, returns * the current status of the child task. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { if (this.interrupted) { /* * Unregister the ExecutionInterrupter so that it is no longer * available to ExecutionPerformInterruption. */ this.getExecutor().unregisterInterrupter(this); return this.statusSet; } else { Status childStatus = this.executionChild.getStatus(); if (childStatus != Status.RUNNING) { /* * If the child has finished, unregister the * ExecutionInterrupter so that it is no longer available to * ExecutionPerformInterruption. */ this.getExecutor().unregisterInterrupter(this); } return childStatus; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the ExecutionInterrupter evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Interrupts the ExecutionInterrupter. This method causes the * ExecutionInterrupter to terminate its child and set the status that will * be returned to status. Also, it requests to be inserted into * the list of tickable nodes, since the terminated child will no longer * react to ticks after being terminated. *

* A task that have not been spawned cannot be interrupted. It should be * noted that if the task has already been terminated, this method does * nothing. Also, if the task has already been interrupted, this method does * nothing too. * * @param status * the status that the ExecutionInterrupter will return. */ public void interrupt(Status status) { if (!this.interrupted) { /* * If the task has not been spawned, throw an exception. */ if (!this.getSpawned()) { throw new RuntimeException( "Cannot interrupt an ExecutionInterrupter that has not been spawned"); } /* * Also it is important to note that that if the task has been * terminated it cannot be interrupted, since by doing so the task * would insert itself into the list of tickable nodes (see below), * which should not be done since the task has been terminated. */ if (!this.getTerminated()) { if (status != Status.FAILURE && status != Status.SUCCESS) { throw new IllegalArgumentException( "The specified status is not valid. Must be either Status.FAILURE or Status.SUCCESS"); } /* Terminate the child. */ this.executionChild.terminate(); /* * It is very important for the ExecutionInterrupter to be * inserted into the list of tickable nodes. If not, after being * interrupted, it will not inform its parent about the * termination of its child. Keep in mind that after terminating * its child, the child will not react to ticks (actually it * will leave the list of tickable nodes in the next AI cycle), * so it has to be the interrupter itself that informs its * parent about termination. */ this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); this.interrupted = true; this.statusSet = status; } } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionInverter.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelInverter; /** * ExecutionInverter is the ExecutionTask that knows how to run a ModelInverter. * * @author Ricardo Juan Palma Durán * */ public class ExecutionInverter extends ExecutionDecorator { /** The child. */ private ExecutionTask child; /** * Creates an ExecutionInverter that is able to run a ModelInverter task and * that is managed by a BTExecutor. * * @param modelTask * the ModelInverter that this ExecutionInverter is going to run. * @param executor * the BTExecutor in charge of running this ExecutionInverter. * @param parent * the parent ExecutionTask of this task. */ public ExecutionInverter(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelInverter)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelInverter.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the only child. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { /* Just spawn the only child. */ this.child = ((ModelInverter) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } /** * Terminates the only child. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { /* Just terminates the only child. */ this.child.terminate(); } /** * Checks if the only child has already finished. If so, it inverts its * status code. Otherwise, it returns {@link Status#RUNNING}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { /* Just inverts the status code. */ Status childStatus = this.child.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } else if (childStatus == Status.FAILURE || childStatus == Status.TERMINATED) { return Status.SUCCESS; } else { return Status.FAILURE; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} so that the ExecutionInverter can evolve * according to the termination of its child. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionLimit.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.execution.core.TaskStateFactory; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelLimit; import jbt.util.Pair; /** * ExecutionLimit is the ExecutionTask that knows how to run a ModelLimit. * * @author Ricardo Juan Palma Durán * */ public class ExecutionLimit extends ExecutionDecorator { /** Maximum number of times that the child task can be executed. */ private int maxNumTimes; /** * Number of times that the child task has been run so far. Initially, its * value is restored from the context. */ private int numRunsSoFar; /** * The child of this decorator. */ private ExecutionTask child; /** * Name of the variable that is stored in the context and that represents * the number of times that the decorator has been run so far. */ protected String STATE_VARIABLE_NAME = "RunsSoFar"; /** * Creates an ExecutionLimit that knows how to run a ModelLimit. * * @param modelTask * the ModelLimit to run. * @param executor * the BTExecutor that will manage this ExecutionLimit. * @param parent * the parent ExecutionTask of this task. */ public ExecutionLimit(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelLimit)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelLimit.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } this.maxNumTimes = ((ModelLimit) this.getModelTask()).getMaxNumTimes(); this.numRunsSoFar = 0; } /** * Spawns the child task if it has not been run more than the maximum * allowed number of times. Otherwise, it requests to be inserted into the * list of tickable nodes, since the child is not spawned. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { if (this.numRunsSoFar < this.maxNumTimes) { this.numRunsSoFar++; this.child = ((ModelLimit) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } else { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); } } /** * Terminates the child task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { if (this.child != null) { this.child.terminate(); } } /** * Returns the status of the child task, or {@link Status#FAILURE} in case * it could not be spawned. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { if (this.child != null) { return this.child.getStatus(); } else { return Status.FAILURE; } } /** * Restore from the ITaskState the number of times that the child task of * this decorator has been run so far. It is read from the variable whose * name is {@link #STATE_VARIABLE_NAME}. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) { try { this.numRunsSoFar = (Integer) state.getStateVariable(STATE_VARIABLE_NAME); } catch (Exception e) {} } /** * Just calls {@link #tick()} to make this task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Returns an ITaskState witht a variable with name * {@link #STATE_VARIABLE_NAME} , and whose value is the number of times * that the child task of this decorator has been run so far. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { List> variables=new Vector>(); variables.add(new Pair(STATE_VARIABLE_NAME, this.numRunsSoFar)); return TaskStateFactory.createTaskState(variables); } /** * Returns an ITaskState witht a variable with name * {@link #STATE_VARIABLE_NAME} , and whose value is the number of times * that the child task of this decorator has been run so far. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { List> variables=new Vector>(); variables.add(new Pair(STATE_VARIABLE_NAME, this.numRunsSoFar)); return TaskStateFactory.createTaskState(variables); } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionRepeat.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelDecorator; import jbt.model.task.decorator.ModelRepeat; /** * ExecutionRepeat is the ExecutionTask that knows how to run a ModelForever. * * @author Ricardo Juan Palma Durán * */ public class ExecutionRepeat extends ExecutionDecorator { /** The child task. */ private ExecutionTask child; /** * Constructs an ExecutionRepeat that knows how to run a ModelForever. * * @param modelTask * the ModelForever to run. * @param executor * the BTExecutor that will manager this ExecutionRepeat. * @param parent * the parent ExecutionTask of this task. */ public ExecutionRepeat(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelRepeat)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelRepeat.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Just spawns its child task. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.child = ((ModelRepeat) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } /** * Terminates the child task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } /** * If the child task has finished, it spawns it again. Always returns * {@link Status#RUNNING}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.child.getStatus(); /* * If the child has finished, spawn it again */ if (childStatus != Status.RUNNING) { this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } return Status.RUNNING; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionSafeContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.context.SafeContext; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelDecorator; import jbt.model.task.decorator.ModelSafeContextManager; /** * ExecutionSafeContextManager is the ExecutionTask that knows how to run a * ModelSafeContextManager. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSafeContextManager extends ExecutionDecorator { /** The child task. */ private ExecutionTask child; /** * Constructs an ExecutionSafeContextManager that knows how to run a * ModelSafeContextManager. * * @param modelTask * the ModelSafeContextManager to run. * @param executor * the BTExecutor that will manage this * ExecutionSafeContextManager. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSafeContextManager(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSafeContextManager)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSafeContextManager.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the child task. This method creates a new SafeContext, and spawns * the child task using this SafeContext. The input context of the * SafeContext is that of this ExecutionSafeContextManager task. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { SafeContext newContext = new SafeContext(this.getContext()); this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(newContext); } /** * Just terminates the child task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } /** * Returns the current status of the child. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return this.child.getStatus(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) { } /** * Just calls {@link #tick()} to make the tass evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionSafeOutputContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.context.SafeOutputContext; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelDecorator; import jbt.model.task.decorator.ModelSafeOutputContextManager; /** * ExecutionSafeOutputContextManager is the ExecutionTask that knows how to run * a ModelSafeOutputContextManager. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSafeOutputContextManager extends ExecutionDecorator { /** The child task. */ private ExecutionTask child; /** * Constructs an ExecutionSafeOutputContextManager that knows how to run a * ModelSafeOutputContextManager. * * @param modelTask * the ModelSafeOutputContextManager to run. * @param executor * the BTExecutor that will manage this * ExecutionSafeOutputContextManager. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSafeOutputContextManager(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSafeOutputContextManager)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSafeOutputContextManager.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the child task. This method creates a new SafeOutputContext, and * spawns the child task using this SafeContext. The input context of the * SafeOutputContext is that of this ExecutionSafeOutputContextManager task. * The list of output variables of the SafeOutputContext is retrieved from * the ModelSafeOutputContextManager associated to this task. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { SafeOutputContext newContext = new SafeOutputContext(this.getContext(), ((ModelSafeOutputContextManager) this.getModelTask()).getOutputVariables()); this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(newContext); } /** * Just terminates the child task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } /** * Returns the current status of the child. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return this.child.getStatus(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) { } /** * Just calls {@link #tick()} to make the tass evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionSucceeder.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelSucceeder; /** * ExecutionSucceeder is the ExecutionTask that knows how to run a ModelSucceeder. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSucceeder extends ExecutionDecorator { /** The child that is being decorated. */ private ExecutionTask child; /** * Creates an ExecutionSucceeder that knows how to run a ModelSucceeder. * * @param modelTask * the ModelSucceeder to run. * @param executor * the BTExecutor that will manage this ExecutionSucceeder. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSucceeder(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSucceeder)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSucceeder.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Just spawns its child. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.child = ((ModelSucceeder) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } /** * Just ticks its child. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.child.getStatus(); if (childStatus == Status.RUNNING) { return Status.RUNNING; } return Status.SUCCESS; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(jbt.execution.core.ITaskState) */ protected void restoreState(ITaskState state) { } /** * Just ticks the task. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } } ================================================ FILE: JBTCore/src/jbt/execution/task/decorator/ExecutionUntilFail.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelDecorator; import jbt.model.task.decorator.ModelUntilFail; /** * ExecutionUntilFail is the ExecutionTask that knows how to run a * ModelUntilFail. * * @author Ricardo Juan Palma Durán * */ public class ExecutionUntilFail extends ExecutionDecorator { /** The task that is being decorated. */ private ExecutionTask child; /** * Constructs and ExecutionUntilFail that knows how to run a ModelUntilFail. * * @param modelTask * the ModelUntilFail that this ExecutionUntilFail will run. * @param executor * the BTExecutor that will manage this ExecutionUntilFail. * @param parent * the parent ExecutionTask of this task. */ public ExecutionUntilFail(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelUntilFail)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelUntilFail.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Spawns the child task. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } /** * Just terminates the child of this task. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { this.child.terminate(); } /** * If the child has finished in failure or been terminated, return * {@link Status#SUCCESS}. Otherwise, {@link Status#RUNNING} is returned. If * the child has finished successfully, it is spawned again. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { Status childStatus = this.child.getStatus(); /* * If the child has finished in failure or been terminated, return * success. */ if (childStatus == Status.FAILURE || childStatus == Status.TERMINATED) { return Status.SUCCESS; } else { /* If the child has finished successfully, spawn it again. */ if (childStatus == Status.SUCCESS) { this.child = ((ModelDecorator) this.getModelTask()).getChild().createExecutor( this.getExecutor(), this); this.child.addTaskListener(this); this.child.spawn(this.getContext()); } /* * In case the child has not finished in failure, return * Status.RUNNING. */ return Status.RUNNING; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionFailure.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelFailure; /** * An ExecutionFailure is the ExecutionTask that knows how to run a * ModelFailure. * * @author Ricardo Juan Palma Durán * */ public class ExecutionFailure extends ExecutionLeaf { /** * Constructs an ExecutionFailure that knows how to run a ModelFailure. * * @param modelTask * the ModelFailure to run. * @param executor * the BTExecutor managing this ExecutionFailure. * @param parent * the parent ExecutionTask. */ public ExecutionFailure(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelFailure)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelFailure.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); } /** * Returns {@link Status#FAILURE}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return Status.FAILURE; } /** * Returns null. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Returns null. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(jbt.execution.core.ITaskState) */ protected void restoreState(ITaskState state) { } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionLeaf.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelLeaf; /** * Base class for all the ExecutionTask classes that are able to run leaf tasks, * that is, classes that inherit from ModelLeaf. * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionLeaf extends ExecutionTask { /** * Constructs an ExecutionLeaf to run a specific ModelLeaf. * * @param modelTask * the ModelLeaf to run. * @param executor * the BTExecutor that will manage this ExecutionLeaf. * @param parent * the parent ExecutionTask of this task. */ public ExecutionLeaf(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelLeaf)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelLeaf.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Does nothing by default, since a leaf task has no children. * * @see jbt.execution.core.ExecutionTask#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) {} } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionPerformInterruption.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.execution.task.decorator.ExecutionInterrupter; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelPerformInterruption; /** * ExecutionPerformInterruption is the ExecutionTask that knows how to run a * ModelPerformInterrupter. * * @author Ricardo Juan Palma Durán * */ public class ExecutionPerformInterruption extends ExecutionLeaf { /** * Creates an ExecutionPerformInterruption that is able to run a * ModelPerformInterruption task and that is managed by a BTExecutor. * * @param modelTask * the ModelPerformInterruption that this * ExecutionPerformInterruption is going to run. * @param executor * the BTExecutor in charge of running this * ExecutionPerformInterruption. * @param parent * the parent ExecutionTask of this task. */ public ExecutionPerformInterruption(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelPerformInterruption)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelPerformInterruption.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Calls {@link ExecutionInterrupter#interrupt(Status)} on the * ExecutionInterrupter associated to this ExecutionPerformInterruption. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); /* * First, retrieve the ExecutionInterrupter that is going to be * interrupted. */ ExecutionInterrupter interrupter = this.getExecutor().getExecutionInterrupter( ((ModelPerformInterruption) this.getModelTask()).getInterrupter()); /* If we could find the ExecutionInterrupter, interrupt it. */ if (interrupter != null) { interrupter.interrupt(((ModelPerformInterruption) this.getModelTask()) .getDesiredResult()); } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() {} /** * Returns {@link Status#SUCCESS}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return Status.SUCCESS; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionSubtreeLookup.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.execution.core.event.TaskEvent; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelSubtreeLookup; /** * ExecutionSubtreeLookup is the ExecutionTask that knows how to run a * ModelExecutionSubtreeLookup. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSubtreeLookup extends ExecutionLeaf { /** * Behaviour tree that is retrieved from the context and that is going to be * run. */ private ModelTask treeToRun; /** * The tree that is actually being run (constructed from {@link #treeToRun} * ). */ private ExecutionTask executionTree; /** Flag that tells if the tree could be retrieved from the context. */ private boolean treeRetrieved; /** * Constructs an ExecutionSubtreeLookup that knows how to run a * ModelSubtreeLookup. * * @param modelTask * the ModelSubtreeLookup to run. * @param executor * the BTExecutor that will manage this ExecutionSubtreeLookup. * @param parent * the parent ExecutionTask of this task. */ public ExecutionSubtreeLookup(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSubtreeLookup)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSubtreeLookup.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * This method first retrieve from the context the tree (ModelTask) that is * going to be emulated by this task. Then, it creates its corresponding * executor and finally spawns it. If the tree cannot be found in the * context, does nothing. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { /* Retrieve the tree to run from the context. */ this.treeToRun = this.getContext().getBT( ((ModelSubtreeLookup) this.getModelTask()).getTreeName()); if (this.treeToRun == null) { this.treeRetrieved = false; /* * Must request to be inserted into the list of tickable nodes, * since no tree has been retrieved and as a result it must be the * task the one continuin the work. */ this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); System.err.println("Could not retrieve tree " + ((ModelSubtreeLookup) this.getModelTask()).getTreeName() + " from the context. Check if the context has been properly initialized."); } else { this.treeRetrieved = true; /* Compute positions for the retrieved tree. */ this.treeToRun.computePositions(); this.executionTree = this.treeToRun.createExecutor(this.getExecutor(), this); this.executionTree.addTaskListener(this); this.executionTree.spawn(this.getContext()); } } /** * Just terminates the tree that it is emulating, or does nothing if the * tree could not be retrieved. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { if (this.treeRetrieved) { this.executionTree.terminate(); } } /** * Returns the status of the tree it is running, or null if the tree could * not be retrieved. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { if (this.treeRetrieved) { return this.executionTree.getStatus(); } else { return Status.FAILURE; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) {} /** * Just calls {@link #tick()} to make the task evolve. * * @see jbt.execution.task.leaf.ExecutionLeaf#statusChanged(jbt.execution.core.event.TaskEvent) */ public void statusChanged(TaskEvent e) { this.tick(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionSuccess.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelSuccess; /** * An ExecutionSuccess is the ExecutionTask that knows how to run a * ModelSuccess. * * @author Ricardo Juan Palma Durán * */ public class ExecutionSuccess extends ExecutionLeaf { /** * Constructs an ExecutionSuccess that knows how to run a ModelSuccess. * * @param modelTask * the ModelSuccess to run. * @param executor * the BTExecutor managing this ExecutionSuccess. * @param parent * the parent ExecutionTask. */ public ExecutionSuccess(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelSuccess)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelSuccess.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); } /** * Returns {@link Status#SUCCESS}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return Status.SUCCESS; } /** * Returns null. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Returns null. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(jbt.execution.core.ITaskState) */ protected void restoreState(ITaskState state) { } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() { } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionVariableRenamer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ITaskState; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelVariableRenamer; /** * EsecutionVariableRenamer is the ExecutionTask that knows how to run a * {@link ModelVariableRenamer}. * * @author Ricardo Juan Palma Durán * */ public class ExecutionVariableRenamer extends ExecutionLeaf { /** The name of the variable that must be renamed. */ private String variableName; /** The new name for the variable that must be renamed. */ private String newVariableName; /** * Constructs an ExecutionVariableRenamer that knows how to run a * ModelVariableRenamer. * * @param modelTask * the ModelVariableRenamer to run. * @param executor * the BTExecutor in charge of running this * ExecutionVariableRenamer. * @param parent * the parent ExecutionTask of this task. */ public ExecutionVariableRenamer(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelVariableRenamer)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelVariableRenamer.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } this.variableName = ((ModelVariableRenamer) modelTask).getVariableName(); this.newVariableName = ((ModelVariableRenamer) modelTask).getNewVariableName(); } /** * Renames the variable in the context. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); Object variable = this.getContext().getVariable(this.variableName); this.getContext().clearVariable(this.variableName); this.getContext().setVariable(this.newVariableName, variable); } /** * Returns {@link Status#SUCCESS}. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { return Status.SUCCESS; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(jbt.execution.core.ITaskState) */ protected void restoreState(ITaskState state) {} /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() {} } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/ExecutionWait.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ITaskState; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelWait; /** * ExecutionWait is the ExecutionTask that knows how to run a ModelWait task. * * @author Ricardo Juan Palma Durán * */ public class ExecutionWait extends ExecutionLeaf { /** Duration of the wait task. */ private long duration; /** * Starting time, measured in nanoseconds. Note that this value is obtained * by {@link System#nanoTime()}, so it is not related to any notion of * system or wall-clock time. Therefore, it can only be used to measure time * intervals. */ private long startTime; /** * Creates an ExecutionWait that is able to run a ModelWait task and that is * managed by a BTExecutor. * * @param modelTask * the ModelWait that this ExecutionWait is going to run. * @param executor * the BTExecutor in charge of running this ExecutionWait. * @param parent * the parent ExecutionTask of this task. */ public ExecutionWait(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelWait)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelWait.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } this.duration = ((ModelWait) modelTask).getDuration(); } /** * Starts measuring the time interval. * * @see jbt.execution.core.ExecutionTask#internalSpawn() */ protected void internalSpawn() { this.getExecutor().requestInsertionIntoList(BTExecutorList.TICKABLE, this); this.startTime = System.nanoTime(); } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#internalTerminate() */ protected void internalTerminate() {} /** * Returns Status.SUCCESS or Status.RUNNING depending on whether the task * has waited long enough or not. * * @see jbt.execution.core.ExecutionTask#internalTick() */ protected Status internalTick() { long estimatedTime = System.nanoTime() - this.startTime; if ((estimatedTime / 1000000.0) >= this.duration) { return Status.SUCCESS; } else { return Status.RUNNING; } } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#restoreState(ITaskState) */ protected void restoreState(ITaskState state) { } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeState() */ protected ITaskState storeState() { return null; } /** * Does nothing. * * @see jbt.execution.core.ExecutionTask#storeTerminationState() */ protected ITaskState storeTerminationState() { return null; } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/action/ExecutionAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf.action; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionLeaf; import jbt.model.core.ModelTask; import jbt.model.task.leaf.action.ModelAction; /** * ExecutionAction is the base class of all of the class that are able to run * actions in the game (that is, subclasses of ModelAction). * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionAction extends ExecutionLeaf { /** * Constructs an ExecutionAction that knows how to run a ModelAction. * * @param modelTask * the ModelAction to run. * @param executor * the BTExecutor that will manage this ExecutionAction. * @param parent * the parent ExecutionTask of this task. */ public ExecutionAction(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelAction)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelAction.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } } ================================================ FILE: JBTCore/src/jbt/execution/task/leaf/condition/ExecutionCondition.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.execution.task.leaf.condition; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionLeaf; import jbt.model.core.ModelTask; import jbt.model.task.leaf.condition.ModelCondition; /** * ExecutionCondition is the base class of all of the class that are able to run * conditions in the game (that is, subclasses of ModelCondition). * * @author Ricardo Juan Palma Durán * */ public abstract class ExecutionCondition extends ExecutionLeaf { /** * Constructs an ExecutionCondition that knows how to run a ModelCondition. * * @param modelTask * the ModelCondition to run. * @param executor * the BTExecutor that will manage this ExecutionCondition. * @param parent * the parent ExecutionTask of this task. */ public ExecutionCondition(ModelTask modelTask, BTExecutor executor, ExecutionTask parent) { super(modelTask, executor, parent); if (!(modelTask instanceof ModelCondition)) { throw new IllegalArgumentException("The ModelTask must subclass " + ModelCondition.class.getCanonicalName() + " but it inherits from " + modelTask.getClass().getCanonicalName()); } } } ================================================ FILE: JBTCore/src/jbt/model/core/ModelTask.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.core; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Vector; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; /** * ModelTask is a class that models a node (task) of a behaviour tree in an * conceptual way. A ModelTask does not have execution capabilities, since it * only purpose is to serve as a way of modeling a behaviour tree conceptually. *

* ModelTask is an abstract class, and it just acts as a container of other * child tasks, with maybe a guard. *

* As stated above, a ModelTask cannot be run. The idea behind this model is * that an external interpreter should be in charge of running the behaviour * tree (ModelTask) instead of the task itself. By doing so, there is a clear * separation between both the conceptual and execution model, thus allowing to * have a unique model shared by many interpreters (which, in other words, means * that a single behaviour tree can be run by many entities at the same time). *

* The interpreter that is used to run a behaviour tree -that is, a ModelTask * and all the tasks below it- is the BTExecutor class. The BTExecutor class is * used to run a ModelTask. A BTExecutor runs a behaviour tree by ticks. This * means that the tree is given some time to think and evolve only at certain * moments (ticks), and is doing nothing otherwise. *

* Every ModelTask is able to issue an ExecutionTask capable of running it ( * {@link #createExecutor(BTExecutor, ExecutionTask)}). Actually, the BTExecutor * uses ExecutionTask objects in order to run the conceptual behaviour tree. An * ExecutionTask is just another type of task that knows how to run its * corresponding ModelTask (by interacting with other tasks as well as with the * BTExecutor). For instance, a ModelSequence, which represents a sequence task * in a behaviour tree, has got an ExecutionTask that knows how to run it, the * ExecutionSequence. The createExecutor() method of ModelSequence * just returns an instance of ExecutionSequence. Therefore, the * createExecutor() method should just return an ExecutionTask that * knows how to run the ModelTask. * * @see ExecutionTask * @see BTExecutor * * @author Ricardo Juan Palma Durán * */ public abstract class ModelTask { /** List of the children of the ModelTask. */ private List children; /** The position of the ModelTask in the behaviour tree. */ private Position position; /** * The guard of the ModelTask. It may be null, in which case it will always * be evaluated to true. */ private ModelTask guard; /** * The position of a ModelTask in a behaviour tree. It contains the sequence * of moves that must be performed to go from the root to the node itself. * Each of the moves of the sequence represents what child of the current * node must be selected. For instance, if the position represents the * sequence of moves {1,4,0}, the node that it points to is the first child * (0) of the fifth child (4) of the second child (1) of the root. En empty * list of moves represents the root. * * @author Ricardo Juan Palma Durán * */ public static class Position { /** * The list of moves that this position represents. */ private List moves; /** * Constructs an Position that contains the moves specified in its * constructor, in the same order. If no move is specified, the Position * will represent an empty sequence of moves. */ public Position(Integer... moves) { this.moves = new LinkedList(); for (Integer i : moves) { this.moves.add(i); } } /** * Constructs a Position from a sequence of moves represented as a List. * * @param moves * the sequence of moves that this Position will represent. */ public Position(List moves) { if (moves == null) { throw new RuntimeException("The list of moves cannot be null"); } this.moves = new LinkedList(); for (Integer i : moves) { this.moves.add(i); } } /** * Constructs a copy of the Position pos. * * @param pos * the Position that is copied. */ public Position(Position pos) { this.moves = new LinkedList(); for (Integer i : pos.moves) { this.moves.add(i); } } /** * Returns the sequence of moves that this Position represents. * * @return the sequence of moves that this Position represents. */ public List getMoves() { return new LinkedList(this.moves); } /** * Adds a move to this Position. The move is inserted as the last one of * the sequence. * * @param move * the move to add. * @return this Position. */ public Position addMove(Integer move) { this.moves.add(move); return this; } /** * Adds a list of moves to this Position. The moves are inserted in the * order specified in the moves list. * * @param moves * the list of moves to add. * @return this Position. */ public Position addMoves(List moves) { for (Integer i : moves) { this.moves.add(i); } return this; } /** * Adds the moves of a Position to this Position. * * @param position * the position whose moves are going to be added to this * one. * @return this Position. */ public Position addMoves(Position position) { addMoves(position.getMoves()); return this; } // /** // * Compares this Position object to another one. Let A and // * B be two Position objects. If A is at a higher // level // in // * the tree, then A is less than B. If A is at // a // * lower lever in the tree, then A is greater than B. // If // * A is at the same level in the tree as that of B, // then: // *

    // *
  • If A is at the left of B, then A is less // * than B. // *
  • If A represents the same sequence of moves as that of // * B, then A equals B. // *
  • Otherwise, A is greater than B. // *
// * // * @see java.lang.Comparable#compareTo(java.lang.Object) // */ // public int compareTo(Position o) { // if (this.moves.size() > o.moves.size()) { // return 1; // } else if (this.moves.size() < o.moves.size()) { // return -1; // } else { // Iterator thisIt = this.moves.iterator(); // Iterator otherIt = o.moves.iterator(); // // while (thisIt.hasNext()) { // Integer thisElem = thisIt.next(); // Integer otherElem = otherIt.next(); // // if (thisElem < otherElem) { // return -1; // } else if (thisElem > otherElem) { // return 1; // } // } // // return 0; // } // } /** * * @see java.lang.Object#toString() */ public String toString() { String result = new String(); if (this.moves.size() != 0) { for (Integer i : this.moves) { result += i + " "; } return "[" + result.substring(0, result.length() - 1) + "]"; } else { return "[]"; } } /** * Returns true if o is a Position object that contains the * same sequence of moves as that of this. Returns false otherwise. * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Position)) { return false; } Position oPosition = (Position) o; List thisMoves = this.getMoves(); List oMoves = oPosition.getMoves(); if (oMoves.size() != thisMoves.size()) { return false; } Iterator thisIt = thisMoves.iterator(); Iterator oIt = oMoves.iterator(); while (thisIt.hasNext()) { Integer thisElem = thisIt.next(); Integer oElem = oIt.next(); if (!thisElem.equals(oElem)) { return false; } } return true; } /** * * @see java.lang.Object#hashCode() */ public int hashCode() { return this.moves.hashCode(); } } /** * Creates a new ModelTask with a guard and several children. The guard may * be null, in which case it is always evaluated to true. The task may also * have no children. * * @param guard * the guard, which may be null. * @param children * the list of children. */ public ModelTask(ModelTask guard, ModelTask... children) { this.guard = guard; this.children = new Vector(); for (ModelTask t : children) { this.children.add(t); } this.position = new Position(); } /** * Returns the list of children of this task, or an empty list if it has no * children. It should be noted that the children of a task are ordered, and * the order influences the way the task runs, reason why a List is * returned. Note that the returned list is the underlying list of children * used by the task, so it should be used carefully (in general, it should * never be modified). * * @return the list of children of this task. */ public List getChildren() { return this.children; } /** * Returns the guard of the task, which may be null. * * @return the guard of the task, which may be null. */ public ModelTask getGuard() { return this.guard; } /** * Creates a suitable ExecutionTask that will be able to run this ModelTask * through the management of a BTExecutor. * * @param executor * the BTExecutor that will manage the returned ExecutionTask. * @param parent * the parent ExecutionTask for the returned ExecutionTask. * * @return an ExecutionTask that is able to run this ModelTask. */ public abstract ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent); /** * Returns the position that this task occupies in the behaviour tree. If it * has not been computed yet (see {@link #computePositions()}), it returns a * Position object with both x and y equal to -1. * * @return the position that this task occupies in the behaviour tree. */ public Position getPosition() { return this.position; } /** * This method computes the positions of all the tasks of the behaviour tree * whose root is this node. After calling this method, the positions of all * the tasks below this one will be available and accessible through * {@link #getPosition()}. *

* It is important to note that, when calling this method, this task is * considered to be the root of the behaviour tree, so its position will be * set to an empty sequence of moves, with no offset, and the positions of * the tasks below it will be computed from it. */ public void computePositions() { /* Assume this node is the root of the tree. */ this.position = new Position(new LinkedList()); /* * Set the position of all of the children of this task and recursively * compute the position of the rest of the tasks. */ for (int i = 0; i < this.children.size(); i++) { ModelTask currentChild = this.children.get(i); Position currentChildPos = new Position(this.position); currentChildPos.addMove(i); currentChild.position = currentChildPos; recursiveComputePositions(currentChild); } } /** * This function searches for a ModelTask according to a particular * Position. *

* Conceptually, a Position represents a sequence of moves. This method just * applies all moves in moves starting from this ModelTask, and * returns the reached ModelTask, or null in case it does not exist. * * @param moves * the sequence of moves that must be performed to retrieve the * ModelTask. * @return the ModelTask obtained by moving down the tree according to the * sequence of moves moves, or null in case no * ModelTask could be found. */ public ModelTask findNode(Position moves) { List m = moves.getMoves(); ModelTask currentTask = this; for (Integer currentMove : m) { List children = currentTask.getChildren(); if (currentMove >= children.size()) { return null; } currentTask = children.get(currentMove); } return currentTask; } /** * This method sets the positions of all tasks below t in the * tree. * * @param t * the task whose descendants will be computed their positions. */ private void recursiveComputePositions(ModelTask t) { /* * Set the position of all of the children of this task and recursively * compute the position of the rest of the tasks. */ for (int i = 0; i < t.children.size(); i++) { ModelTask currentChild = t.children.get(i); Position currentChildPos = new Position(t.getPosition()); currentChildPos.addMove(i); currentChild.position = currentChildPos; recursiveComputePositions(currentChild); } } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelComposite.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.model.core.ModelTask; /** * A ModelComposite task is a task with several children, whose evaluation * depends on the evaluation of its children. * * @author Ricardo Juan Palma Durán * */ public abstract class ModelComposite extends ModelTask { /** * Constructor. *

* Constructs a ModelComposite with some children. A ModelComposite must * have at least one child. * * @param guard * the guard of the ModelComposite. * @param children * the list of children. Must have at least one element. */ public ModelComposite(ModelTask guard, ModelTask... children) { super(guard, children); if (children.length == 0) { throw new IllegalArgumentException("The list of children cannot be empty"); } } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelDynamicPriorityList.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionDynamicPriorityList; import jbt.model.core.ModelTask; /** * This class represents a task with one or more children, only one being * evaluated. *

* A ModelDynamicPriorityList has a current active child, which is the task that * is being evaluated. The very first time the ModelDynamicPriorityList is * spawned, the active child is set to the left most task whose guard is * evaluated to true. However, the current active task may change when the task * is ticked, according to the guards of the other tasks: if there is a task to * the left of the current active task whose guard is true, the latter is * terminated, and the new current active task is set to the former. In case * there are several tasks to the left of the current active task whose guards * are evaluated to true, the current active task will be the left most one. * * @author Ricardo Juan Palma Durán * */ public class ModelDynamicPriorityList extends ModelComposite { /** * Creates a ModelDynamicPriorityList task with a guard, and a list of * children to run. A ModelDynamicPriorityList must have at least one child. * * @param guard * the guard, which may be null. * @param children * the list of children. Must have at least one element. */ public ModelDynamicPriorityList(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionDynamicPriorityList that is able to run this * ModelDynamicPriorityList. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionDynamicPriorityList(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelParallel.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionParallel; import jbt.model.core.ModelTask; /** * ModelParallel is a task that runs all its children simultaneously. A * ModelParallel is constantly checking the evolution of its children. *

* The parallel task has a policy that defines the way it behaves. There are to * policies for parallel: *

    *
  • {@link ParallelPolicy#SEQUENCE_POLICY}: meaning the parallel behaves like * a sequence task, that is, it fails as soon as one of its children fail, and * it only succeed if all of its children succeed. Otherwise it is running. *
  • {@link ParallelPolicy#SELECTOR_POLICY}: meaning the parallel behaves like * a selector task, that is, if succeeds as soon as one of its children succeed, * and it only fails of all of its children fail. Otherwise it is running. *
* * @author Ricardo Juan Palma Durán * */ public class ModelParallel extends ModelComposite { /** Policy of this ModelParallel task. */ private ParallelPolicy policy; /** * Enum defining the different policies for a parallel task (ModelParallel): *
    *
  • {@link ParallelPolicy#SEQUENCE_POLICY}: means the parallel behaves * like a sequence task, that is, it fails as soon as one of its children * fail, and it only succeed if all of its children succeed. Otherwise it is * running. *
  • {@link ParallelPolicy#SELECTOR_POLICY}: means the parallel behaves * like a selector task, that is, if succeeds as soon as one of its children * succeed, and it only fails of all of its children fail. Otherwise it is * running. *
* * @author Ricardo Juan Palma Durán * */ public static enum ParallelPolicy { /** * Policy meaning that the parallel behaves like a sequence task, that * is, it fails as soon as one of its children fail, and it only succeed * if all of its children succeed. */ SEQUENCE_POLICY, /** * Policy meaning the parallel behaves like a selector task, that is, if * succeeds as soon as one of its children succeed, and it only fails of * all of its children fail. */ SELECTOR_POLICY } /** * Creates a ModelParallel task with a guard, a policy and a list of * children to run. A ModelParallel must have at least one child. * * @param guard * the guard, which may be null. * @param policy * the policy for the ModelParallel. * @param children * the list of children. Must have at least one element. */ public ModelParallel(ModelTask guard, ParallelPolicy policy, ModelTask... children) { super(guard, children); this.policy = policy; } /** * Returns the policy of this ModelParallel. * * @return the policy of this ModelParallel. */ public ParallelPolicy getPolicy() { return this.policy; } /** * Returns an ExecutionParallel that can run this ModelParallel. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionParallel(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelRandomSelector.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionRandomSelector; import jbt.model.core.ModelTask; /** * A ModelRandomSelector is a task that behaves just like a ModelSelector, but * which walk through its children in a random order. Instead of evaluating its * children from left to right, this task evaluate them in a random order. * * @author Ricardo Juan Palma Durán * */ public class ModelRandomSelector extends ModelComposite { /** * Creates a ModelRandomSelector with a guard and several children. The list * of children cannot be empty. * * @param guard * the guard of the ModelRandomSelector, which may be null. * @param children * the list of children, which cannot be empty. */ public ModelRandomSelector(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionRandomSelector that knows how to run this * ModelRandomSelector. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionRandomSelector(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelRandomSequence.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionRandomSequence; import jbt.model.core.ModelTask; /** * A ModelRandomSequence is a task that behaves just like a ModelSequence, but * which walk through its children in a random order. Instead of evaluating its * children from left to right, this task evaluate them in a random order. * * @author Ricardo Juan Palma Durán * */ public class ModelRandomSequence extends ModelComposite { /** * Creates a ModelRandomSequence with a guard and several children. The list * of children cannot be empty. * * @param guard * the guard of the ModelRandomSequence, which may be null. * @param children * the list of children, which cannot be empty. */ public ModelRandomSequence(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionRandomSequence that knows how to run this * ModelRandomSequence. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionRandomSequence(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelSelector.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionSelector; import jbt.model.core.ModelTask; /** * This class represents a task with one or more children, which are run * sequentially. *

* A selector tries to run all its children sequentially. Therefore, there is an * active child task. However, when the current active task fails, the selector * does not fail, but goes on to the next child task, which is evaluated. A * selector succeeds if one of the tasks succeeds, and fails if all the child * tasks fail. * * @author Ricardo Juan Palma Durán * */ public class ModelSelector extends ModelComposite { /** * Constructor. *

* Constructs a ModelSelector with some children. A ModelSelector must have * at least one child. * * @param guard * the guard of the ModelSelector, which may be null. * @param children * the list of children. Must have at least one element. */ public ModelSelector(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionSelector that is able to run this ModelSelector. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSelector(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelSequence.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionSequence; import jbt.model.core.ModelTask; /** * A ModeSequence is a task with one or more children which are evaluated * sequentially. *

* A ModeSequence has an active child, which is the child task currently being * evaluated. If the execution of the current child finishes successfully, the * next child of the sequence is spawned and evaluated. However, if the * execution of the currently active child ends in failure, the whole * ModeSequence also fails. * * @author Ricardo Juan Palma Durán * */ public class ModelSequence extends ModelComposite { /** * Constructor. *

* Constructs a ModeSequence with some children. A ModeSequence must have at * least one child. * * @param guard * the guard of the ModeSequence, which may be null. * @param children * the list of children. Must have at least one element. */ public ModelSequence(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionSequence that can run this ModelSequence. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSequence(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/composite/ModelStaticPriorityList.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.composite; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.composite.ExecutionStaticPriorityList; import jbt.model.core.ModelTask; /** * This class represents a task with one or more children, only one being * evaluated. *

* A ModelStaticPriorityList has a current active child, which is the task that * is being evaluated. The very first time the ModelStaticPriorityList is * spawned, the active child is set to the left most task whose guard is * evaluated to true. From then on, that child will run as normal, and the * ModelStaticPriorityList will finish as soon as its child finishes. * * @author Ricardo Juan Palma Durán * */ public class ModelStaticPriorityList extends ModelComposite { /** * Creates a ModelStaticPriorityList task with a guard, and a list of * children to run. A ModelStaticPriorityList must have at least one child. * * @param guard * the guard, which may be null. * @param children * the list of children. Must have at least one element. */ public ModelStaticPriorityList(ModelTask guard, ModelTask... children) { super(guard, children); } /** * Returns an ExecutionStaticPriorityList that is able to run this * ModelStaticPriorityList. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionStaticPriorityList(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelDecorator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.model.core.ModelTask; /** * This class represents a decorator of a task. A decorator is a task with only * one child, whose behavior it modifies. A decorator is used in situations in * which we want to execute a particular task but in a little different way. *

* Typical examples of decorators are: * *

    *
  • Filters: they decide whether the child task can continue running or not. * Some examples of filters are: *
      *
    • Limit filter: which limits the number of times a task can execute. *
    • Until fail filter: which repeats a task until it fails. *
    *
  • Inverter: inverts the status code of a task. *
  • Semaphore guard: they are associated to resources. If the resource is * currently being used by another task, the new task cannot start running, so * it fails. *
* * @author Ricardo Juan Palma Durán * */ public abstract class ModelDecorator extends ModelTask { /** * Constructor. *

* Constructs a ModelDecorator with one child. * * @param guard * the guard of the ModelDecorator. which may be null. * @param child * the child of the ModelDecorator. */ public ModelDecorator(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns the child of this decorator. * * @return the child of this decorator. */ public ModelTask getChild() { return this.getChildren().get(0); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelHierarchicalContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionHierarchicalContextManager; import jbt.model.core.ModelTask; /** * A ModelHierarchicalContextManager is a decorator that creates a new context for its child * task. The context that it creates is a {@link HierarchicalContext}. The * parent context of the HierarchicalContext is the context of the * ModelHierarchicalContextManager, so if the child task does not find a variable in its * context, the context of the ModelHierarchicalContextManager will be used instead. *

* The spawning and updating of the child task are carried out as usual. * * @author Ricardo Juan Palma Durán * */ public class ModelHierarchicalContextManager extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelHierarchicalContextManager, which may be null. * @param child * the child of the ModelHierarchicalContextManager. */ public ModelHierarchicalContextManager(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionContextManager that knows how to run this * ModelHierarchicalContextManager. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionHierarchicalContextManager(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelInterrupter.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionInterrupter; import jbt.model.core.ModelTask; /** * An ModelInterrupter is a decorator that controls the termination of a child * task. An ModelInterrupter simply lets its child task run normally. If the * child returns a result, the ModelInterrupter will return it. However, the * ModelInterrupter can be asked to terminate the child task and return an * specified status when done so. * * @author Ricardo Juan Palma Durán * */ public class ModelInterrupter extends ModelDecorator { /** * Constructor. *

* Constructs a ModelInterrupter with one child. * * @param guard * the guard of the ModelInterrupter, which may be null. * @param child * the child of the ModelInterrupter. */ public ModelInterrupter(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionInterrupter that is able to run this * ModelInterrupter. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionInterrupter(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelInverter.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionInverter; import jbt.model.core.ModelTask; /** * ModelInverter is a decorator used to invert the status code returned by its * child. *

* When the decorated task finishes, its status code gets inverted according to: * *

    *
  • Status.SUCCESS -> Status.FAILURE. *
  • Status.FAILURE -> Status.SUCCESS. *
  • Status.TERMINATED -> Status.SUCCESS. *
* * If the child task has not finished yet, the ModelInverter returns * Status.RUNNING (that is, Status.RUNNING is not * inverted). * * @author Ricardo Juan Palma Durán * */ public class ModelInverter extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelInverter, which may be null. * @param child * the child task to invert. */ public ModelInverter(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionInverter that is able to run this ModelInverter. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionInverter(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelLimit.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionLimit; import jbt.model.core.ModelTask; /** * Limit is a decorator that limits the number of times a task can be executed. * This decorator is used when a task (the child of the decorator) must be run a * maximum number of times. When the maximum number of times is exceeded, the * decorator will fail forever on. * * @author Ricardo Juan Palma Durán * */ public class ModelLimit extends ModelDecorator { /** Maximum number of times that the decorated task can be run. */ private int maxNumTimes; /** * Constructor. * * @param guard * the guard of the ModelLimit, which may be null. * @param maxNumTimes * the maximum number of times that child will be * run. * @param child * the child of this task. */ public ModelLimit(ModelTask guard, int maxNumTimes, ModelTask child) { super(guard, child); this.maxNumTimes = maxNumTimes; } /** * Returns an ExecutionLimit that knows how to run this ModelLimit. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionLimit(this, executor, parent); } /** * Returns the maximum number of times that the decorated task can be run. * * @return the maximum number of times that the decorated task can be run. */ public int getMaxNumTimes() { return this.maxNumTimes; } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelRepeat.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.task.decorator.ExecutionRepeat; import jbt.model.core.ModelTask; /** * ModelRepeat represents a decorator that runs its child task forever. When its * child task finishes, it runs it once more. This decorator always return * {@link Status#RUNNING}. * * @author Ricardo Juan Palma Durán * */ public class ModelRepeat extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelRepeat, which may be null. * @param child * the child that will be run forever. */ public ModelRepeat(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionForever that knows how to run this ModelRepeat. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionRepeat(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelSafeContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.context.SafeContext; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionSafeContextManager; import jbt.model.core.ModelTask; /** * A ModelSafeContextManager is a decorator that creates a new context for its * child task. The context that it creates is a {@link SafeContext}, and the * input context that the SafeContext receives is the context of the * ModelSafeContextManager. *

* The spawning and updating of the child task are carried out as usual. * * @author Ricardo Juan Palma Durán * */ public class ModelSafeContextManager extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelSafeContextManager, which may be null. * @param child * the child of the ModelSafeContextManager. */ public ModelSafeContextManager(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionSafeContextManager that knows how to run this * ModelSafeContextManager. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSafeContextManager(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelSafeOutputContextManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import java.util.List; import jbt.execution.context.SafeOutputContext; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionSafeOutputContextManager; import jbt.model.core.ModelTask; /** * A ModelSafeOutputContextManager is a decorator that creates a new context for * its child task. The context that it creates is a {@link SafeOutputContext}, * and the input context that the SafeOutputContext receives is that of the * ModelSafeOutputContextManager. *

* The spawning and updating of the child task are carried out as usual. * * @author Ricardo Juan Palma Durán * */ public class ModelSafeOutputContextManager extends ModelDecorator { /** * The list of output variables of the SafeOutputContext. */ private List outputVariables; /** * Constructor. * * @param guard * the guard of the ModelSafeOutputContextManager, which may be * null. * @param child * the child of the ModelSafeOutputContextManager. * @param outputVariables * the list of output variables of the SafeOutputContext that is * created. */ public ModelSafeOutputContextManager(ModelTask guard, List outputVariables, ModelTask child) { super(guard, child); this.outputVariables = outputVariables; } /** * Returns an ExecutionSafeOutputContextManager that knows how to run this * ModelSafeOutputContextManager. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSafeOutputContextManager(this, executor, parent); } /** * Returns a list with the set of output variables of the SafeOutputContext. * The list cannot be modified. * * @return a list with the set of output variables of the SafeOutputContext. */ public List getOutputVariables() { return this.outputVariables; } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelSucceeder.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionSucceeder; import jbt.model.core.ModelTask; /** * A ModelSucceeder is a decorator that makes its child succeeds no matter it it actually fails. * * @author Ricardo Juan Palma Durán * */ public class ModelSucceeder extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelSucceeder, which may be null. * @param child * the child task. */ public ModelSucceeder(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionSucceeder that is able to run this ModelSucceeder. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSucceeder(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/decorator/ModelUntilFail.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.decorator; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.decorator.ExecutionUntilFail; import jbt.model.core.ModelTask; /** * The ModelUntilFail class represents a decorator used to run a task as long as * it does not fail. *

* ModelUntilFail just keeps executing its child task as long as it does not * fail. When the child task fails, ModelUntilFail returns * {@link Status#SUCCESS}. Otherwise it returns {@link Status#RUNNING}. * * @author Ricardo Juan Palma Durán * */ public class ModelUntilFail extends ModelDecorator { /** * Constructor. * * @param guard * the guard of the ModelUntilFail, which may be null. * @param child * the task that will be run until it fails. */ public ModelUntilFail(ModelTask guard, ModelTask child) { super(guard, child); } /** * Returns an ExecutionUntilFail that knows how to run this ModelUntilFail. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionUntilFail(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelFailure.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionFailure; import jbt.model.core.ModelTask; /** * A ModelFailure represents a task that always fails. * * @author Ricardo Juan Palma Durán * */ public class ModelFailure extends ModelLeaf { /** * Constructor. * * @param guard * the guard of the ModelFailure, which may be null. */ public ModelFailure(ModelTask guard) { super(guard); } /** * Returns an {@link ExecutionFailure} that knows how to run this * ModelFailure. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * jbt.execution.core.ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionFailure(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelLeaf.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.model.core.ModelTask; /** * Base class for all the tasks that have no children. * * @author Ricardo Juan Palma Durán * */ public abstract class ModelLeaf extends ModelTask { /** * Constructs a ModelLeaf with a guard. * * @param guard * the guard, which may be null. */ public ModelLeaf(ModelTask guard) { super(guard); } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelPerformInterruption.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.task.leaf.ExecutionPerformInterruption; import jbt.model.core.ModelTask; import jbt.model.task.decorator.ModelInterrupter; /** * A ModelPerformInterruption is a task that interacts with a ModelInterrupter * decorator, interrupting it when it (the ModelPerformInterruption) is spawned. * A ModelPerformInterruption always succeeds when it is spawned. When the * ModelInterrupter gets interrupted, the status code it returns is also set by * the ModelPerformInterruption. * * @author Ricardo Juan Palma Durán * */ public class ModelPerformInterruption extends ModelLeaf { /** * The ModelInterrupter that this ModelPerformInterruption is going to * interrupt. */ private ModelInterrupter interrupter; /** * The status code that the ModelInterrupter should return in case it is * interrupted. */ private Status desiredResult; /** * Constructor. * * @param guard * the guard of the ModelPerformInterruption, which may be null. * @param interrupter * the ModelInterrupter that this ModelPerformInterruption will * interrupt. May be null. * @param desiredResult * the result that the ModelInterrupter should return in case it * is interrupted. */ public ModelPerformInterruption(ModelTask guard, ModelInterrupter interrupter, Status desiredResult) { super(guard); this.interrupter = interrupter; this.desiredResult = desiredResult; } /** * Returns an ExecutionPerformInterruption that is able to run this * ModelPerformInterruption. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionPerformInterruption(this, executor, parent); } /** * Sets the interrupter that this PerformInterruption is going to interrupt. * * @param interrupter * the ModelInterrupter that this PerformInterruption is going to * interrupt. May be null. */ public void setInterrupter(ModelInterrupter interrupter) { this.interrupter = interrupter; } /** * Returns the ModelInterrupter that this PerformInterruption is going to * interrupt, or null if not set. * * @return the ModelInterrupter that this PerformInterruption is going to * interrupt, or null if not set. */ public ModelInterrupter getInterrupter() { return this.interrupter; } /** * Returns the result that the ModelInterrupter should return in case it is * interrupted. * * @return e result that the ModelInterrupter should return in case it is * interrupted. */ public Status getDesiredResult() { return this.desiredResult; } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelSubtreeLookup.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionSubtreeLookup; import jbt.model.core.ModelTask; /** * A ModelSubtreeLookup is a leaf node that emulates the behaviour of another * behaviour tree. *

* One of the key features of behaviour trees is that they can be reused in many * places. This reusability is implemented through the ModelSubreeLookup task. * When a tree A must be reused within another tree B, this task * is used to retrieve A and use it within B. Trees are indexed by * names, so this task needs the name of the tree that it will emulate. * * @author Ricardo Juan Palma Durán * */ public class ModelSubtreeLookup extends ModelLeaf { /** The name of the tree that this task is going to emulate. */ private String treeName; /** * Constructor. * * @param guard * the guard of the task, which may be null. * @param treeName * the name of the tree that this task is going to emulate. */ public ModelSubtreeLookup(ModelTask guard, String treeName) { super(guard); this.treeName = treeName; } /** * Returns an ExecutionSubtreeLookup that is able to run this * ModelSubtreeLookup. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSubtreeLookup(this, executor, parent); } /** * Returns the name of the tree that this task is going to emulate. * * @return the name of the tree that this task is going to emulate. */ public String getTreeName() { return this.treeName; } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelSuccess.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionSuccess; import jbt.model.core.ModelTask; /** * A ModelSuccess represents a task that always succeeds. * * @author Ricardo Juan Palma Durán * */ public class ModelSuccess extends ModelLeaf { /** * Constructor. * * @param guard * the guard of the ModelSuccess, which may be null. */ public ModelSuccess(ModelTask guard) { super(guard); } /** * Returns an {@link ExecutionSuccess} that knows how to run this * ModelSuccess. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * jbt.execution.core.ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionSuccess(this, executor, parent); } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelVariableRenamer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionVariableRenamer; import jbt.model.core.ModelTask; /** * A ModelVariableRenamer is a task that renames a variable of the context. This * task just takes one variable of the context and changes its name. * * @author Ricardo Juan Palma Durán * */ public class ModelVariableRenamer extends ModelLeaf { /** The name of the variable that must be renamed. */ private String variableName; /** The new name for the variable that must be renamed. */ private String newVariableName; /** * Constructor. * * @param guard * the guard of the task, which may be null. * @param variableName * the name of the variable to rename. * @param newVariableName * the new name for the variable. */ public ModelVariableRenamer(ModelTask guard, String variableName, String newVariableName) { super(guard); this.variableName = variableName; this.newVariableName = newVariableName; } /** * Returns a new {@link ExecutionVariableRenamer} that knows how to run this * ModelVariableRenamer. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * jbt.execution.core.ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionVariableRenamer(this, executor, parent); } /** * Returns the name of the variable to rename. * * @return the name of the variable to rename. */ public String getVariableName() { return this.variableName; } /** * Returns the new name for the variable that must be renamed. * * @return the new name for the variable that must be renamed. */ public String getNewVariableName() { return this.newVariableName; } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/ModelWait.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.ExecutionWait; import jbt.model.core.ModelTask; /** * A ModelWait task is a task that keeps running for a period of time, and then * succeeds. The user can specify for how long the ModelWait task should be * running. For that period of time, the task will be evaluated to * Status.RUNNING. Then, the task will return Status.SUCCESS. * * @author Ricardo Juan Palma Durán * */ public class ModelWait extends ModelLeaf { /** * Duration, measured in milliseconds, of the period of time the task will * be running. */ private long duration; /** * Constructor. Constructs a ModelWait task that will keep running for * duration milliseconds. * * @param guard * the guard of the ModelWait task, which may be null. * @param duration * the ModelWait of the Wait task, in milliseconds. */ public ModelWait(ModelTask guard, long duration) { super(guard); this.duration = duration; } /** * Returns an ExecutionWait that can run this ModelWait. * * @see jbt.model.core.ModelTask#createExecutor(jbt.execution.core.BTExecutor, * ExecutionTask) */ public ExecutionTask createExecutor(BTExecutor executor, ExecutionTask parent) { return new ExecutionWait(this, executor, parent); } /** * Returns the duration of this ModelWait task. * * @return the duration of this ModelWait task. */ public long getDuration() { return this.duration; } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/action/ModelAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf.action; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelLeaf; /** * Class representing an abstract basic action to be executed in the game. An * action is a task with no children (that is, it is a leaf in the behavior * tree) and with no connection to any other task in the tree. * * @author Ricardo Juan Palma Durán * */ public abstract class ModelAction extends ModelLeaf { /** * Constructs the ModelAction. * * @param guard * the guard of the ModelAction, which may be null. */ public ModelAction(ModelTask guard) { super(guard); } } ================================================ FILE: JBTCore/src/jbt/model/task/leaf/condition/ModelCondition.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.model.task.leaf.condition; import jbt.model.core.ModelTask; import jbt.model.task.leaf.ModelLeaf; /** * Class representing an abstract condition to be tested within the game. * Conditions are tasks with no children (that is, they are leaves in the * behavior tree) and with no connection to any other task in the tree. * * @author Ricardo Juan Palma Durán * */ public abstract class ModelCondition extends ModelLeaf { /** * Constructs a ModelCondition. * * @param guard * the guard of the ModelCondition, which may be null. */ public ModelCondition(ModelTask guard) { super(guard); } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/ActionsAndConditionsGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator; import gatech.mmpm.ActionParameterType; import gatech.mmpm.tools.parseddomain.ParseException; import gatech.mmpm.tools.parseddomain.ParsedAction; import gatech.mmpm.tools.parseddomain.ParsedDomain; import gatech.mmpm.tools.parseddomain.ParsedMethod; import jargs.gnu.CmdLineParser; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import jbt.execution.task.leaf.action.ExecutionAction; import jbt.model.task.leaf.action.ModelAction; import jbt.model.task.leaf.condition.ModelCondition; import jbt.tools.btlibrarygenerator.lowlevelgenerator.ActionsGenerator; import jbt.tools.btlibrarygenerator.lowlevelgenerator.ConditionsGenerator; import jbt.tools.btlibrarygenerator.util.Util; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; /** * ActionsAndConditionsGenerator is an application that generates the skeleton * for low level actions and conditions specified in one or several MMPM domain * files. *

* For every MMPM action, two classes are created: one extending * {@link ModelAction}, which conceptually represents the action, and another * one extending {@link ExecutionAction}, which represents how the action * actually works -whose abstract methods must be completed in order for the * action to perform any task at all-. *

* Also, for every MMPM boolean sensor, two classes are created: one extending * {@link ModelCondition}, which conceptually represents the condition (sensor), * and another one extending {@link ExecutionCondition}, which represents how * the condition actually works -whose abstract methods must be completed in * order for the condition to perform any task at all-. *

* Created execution classes define getter methods for each MMPM parameter. *

* The syntax of this program is as follows: * *

 * ActionsAndConditionsGenerator -c configurationFile [-r relativePath] [-o]
 * 
* * Where configurationFile is an XML file that contains all the * information required to run this application. The syntax of such a file is: * *
 * <Configuration>
 * 	
 *  <DomainFile>MMPMDomainFile1</DomainFile>
 *  <DomainFile>MMPMDomainFile2</DomainFile>
 *  ...
 *  <DomainFile>MMPMDomainFileN</DomainFile>
 *  
 *  <ModelActionsPackage>Name of the package for generated model action classes</ModelActionsPackage>
 *  
 *  <ModelConditionsPackage>Name of the package for generated model condition classes</ModelConditionsPackage>
 *  
 *  <ModelActionsOutputDirectory>Name of the directory where model actions are created</ModelActionsOutputDirectory>
 *  
 *  <ModelConditionsOutputDirectory>Name of the directory where model conditions are created</ModelConditionsOutputDirectory>
 *  
 *  <ExecutionActionsPackage>Name of the package for generated execution action classes</ExecutionActionsPackage>
 *  
 *  <ExecutionConditionsPackage>Name of the package for generated execution condition classes</ExecutionConditionsPackage>
 *  
 *  <ExecutionActionsOutputDirectory>Name of the directory where execution actions are created</ExecutionActionsOutputDirectory>
 *  
 *  <ExecutionConditionsOutputDirectory>Name of the directory where execution conditions are created</ExecutionConditionsOutputDirectory>
 *  
 * </Configuration>
 * 
* * The order in which the elements are specified is not relevant. If the input * files do contain only actions, parameters related to conditions may not be * specified, and vice versa. *

* The -r option is used to add a path to the beginning of the files listed in * the configuration file; as a result, each file is considered to be placed at * the path specified in the -r option. The -r option may not be specified, in * which case the files are considered to be at the current execution directory. *

* The -o option (standing for overwrite) is either is specified or not. * If it is not specified, generated output files will not overwrite any * existing file in the file system, and as a result, the corresponding class * file will not be produced in case there is a file with the same name in the * file system. If the option -o is specified, then generated output files will * overwrite any file in the file system whose name matches. * * @author Ricardo Juan Palma Durán * */ public class ActionsAndConditionsGenerator { /** Tag of the XML configuration file. */ private static final String CONFIGURATION_TAG = "Configuration"; /** Tag of the XML configuration file. */ private static final String DOMAIN_FILE_TAG = "DomainFile"; /** Tag of the XML configuration file. */ private static final String MODEL_ACTIONS_PACKAGE_TAG = "ModelActionsPackage"; /** Tag of the XML configuration file. */ private static final String MODEL_CONDITIONS_PACKAGE_TAG = "ModelConditionsPackage"; /** Tag of the XML configuration file. */ private static final String MODEL_ACTIONS_OUTPUT_DIRECTORY_TAG = "ModelActionsOutputDirectory"; /** Tag of the XML configuration file. */ private static final String MODEL_CONDITIONS_OUTPUT_DIRECTORY_TAG = "ModelConditionsOutputDirectory"; /** Tag of the XML configuration file. */ private static final String EXECUTION_ACTIONS_PACKAGE_TAG = "ExecutionActionsPackage"; /** Tag of the XML configuration file. */ private static final String EXECUTION_CONDITIONS_PACKAGE_TAG = "ExecutionConditionsPackage"; /** Tag of the XML configuration file. */ private static final String EXECUTION_ACTIONS_OUTPUT_DIRECTORY_TAG = "ExecutionActionsOutputDirectory"; /** Tag of the XML configuration file. */ private static final String EXECUTION_CONDITIONS_OUTPUT_DIRECTORY_TAG = "ExecutionConditionsOutputDirectory"; public static void main(String[] args) { /* Parse command line arguments. */ CmdLineParser parser = new CmdLineParser(); CmdLineParser.Option configurationFileOption = parser.addStringOption('c', "config"); CmdLineParser.Option relativePathOption = parser.addStringOption('r', "relativepath"); CmdLineParser.Option overwriteOption = parser.addBooleanOption('o', "overwrite"); try { parser.parse(args); } catch (Exception e) { printUsage(); System.exit(1); } try { /* Relative path read from the command line. */ String relativePath = (String) parser.getOptionValue(relativePathOption); if (relativePath == null) { relativePath = ""; } else { relativePath = Util.addDirectorySeparator(relativePath); } /* Overwrite option. */ Boolean overwrite = (Boolean) parser.getOptionValue(overwriteOption, Boolean.FALSE); /* Open the configuration file and read file names. */ String configurationFileName; configurationFileName = (String) parser.getOptionValue(configurationFileOption); if (configurationFileName == null) { printUsage(); System.exit(1); } FileInputStream file = new FileInputStream(configurationFileName); SAXBuilder confFileBuilder = new SAXBuilder(); Document confFileDoc = confFileBuilder.build(file); file.close(); List fileNames = new LinkedList(); Element root = confFileDoc.getRootElement(); List childrenElements = root.getChildren(DOMAIN_FILE_TAG); for (Element e : childrenElements) { fileNames.add(e.getText()); } /* * First, we process all MMPM domain files and extract a global list * of actions and conditions. */ List actions = new LinkedList(); List conditions = new LinkedList(); for (String currentFileName : fileNames) { try { currentFileName = relativePath + currentFileName; System.out.println("Reading and parsing " + currentFileName + "..."); FileInputStream currentFile = new FileInputStream(currentFileName); SAXBuilder mmpmFileBuilder = new SAXBuilder(); Document mmpmDoc = mmpmFileBuilder.build(currentFile); currentFile.close(); ParsedDomain domain = new ParsedDomain(); domain.init(mmpmDoc.getRootElement(), null); actions.addAll(domain.getActionSet().getAction()); for (ParsedMethod method : domain.getSensorSet().getMethods()) { if (method.getReturnedType() == ActionParameterType.BOOLEAN) { conditions.add(method); } } } catch (IOException e) { System.out.println("Could not open file: " + currentFileName + ": " + e.getMessage()); } catch (ParseException e) { System.out.println("There were errors while parsing the MMPM domain file " + currentFileName + ": " + e.getMessage()); } } /* Now create all the action classes. */ if (actions.size() != 0) { try { /* * Get output directories and packages from the XML * configuration file. */ String modelActionsPackage = root.getChildText(MODEL_ACTIONS_PACKAGE_TAG); String modelActionsOutputDirectory = relativePath + root.getChildText(MODEL_ACTIONS_OUTPUT_DIRECTORY_TAG); /* If directory ends in separator, remove it. */ modelActionsOutputDirectory = Util .removeDirectorySeparator(modelActionsOutputDirectory); String executionActionsPackage = root .getChildText(EXECUTION_ACTIONS_PACKAGE_TAG); String executionActionsOutputDirectory = relativePath + root.getChildText(EXECUTION_ACTIONS_OUTPUT_DIRECTORY_TAG); /* If directory ends in separator, remove it. */ executionActionsOutputDirectory = Util .removeDirectorySeparator(executionActionsOutputDirectory); File modelActionsDirectory = new File(modelActionsOutputDirectory); /* Create output directories. */ if (!modelActionsDirectory.isDirectory()) { System.out.println("Creating output directory: " + modelActionsOutputDirectory + "..."); modelActionsDirectory.mkdirs(); } File executionActionsDirectory = new File(executionActionsOutputDirectory); if (!executionActionsDirectory.isDirectory()) { System.out.println("Creating output directory: " + executionActionsOutputDirectory + "..."); executionActionsDirectory.mkdirs(); } /* Create action classes. */ ActionsGenerator actionGenerator = new ActionsGenerator(); for (ParsedAction currentAction : actions) { try { /* Create model action. */ String modelOutputFileName = modelActionsOutputDirectory + File.separator + currentAction.getName() + ".java"; boolean skipModelAction = false; if (Util.fileExists(modelOutputFileName)) { System.out.print(modelOutputFileName + " already exists. "); if (overwrite) { System.out.println("It will be overwritten."); } else { System.out.println(modelOutputFileName + " will not be generated."); skipModelAction = true; } } if (!skipModelAction) { System.out.println("Creating model action class: " + modelOutputFileName + "..."); String modelActionFileContent = new String(); modelActionFileContent += getModelClassesFileHeader(); modelActionFileContent += "package " + modelActionsPackage + ";\n\n"; modelActionFileContent += actionGenerator.getModelActionClass( currentAction, executionActionsPackage); BufferedWriter modelOutputFile = new BufferedWriter(new FileWriter( modelOutputFileName)); modelOutputFile.write(Util.format(modelActionFileContent)); modelOutputFile.close(); } /* Create execution action. */ String executionOutputFileName = executionActionsOutputDirectory + File.separator + currentAction.getName() + ".java"; boolean skipExecutionAction = false; if (Util.fileExists(executionOutputFileName)) { System.out.print(executionOutputFileName + " already exists. "); if (overwrite) { System.out.println("It will be overwritten."); } else { System.out.println(executionOutputFileName + " will not be generated."); skipExecutionAction = true; } } if (!skipExecutionAction) { System.out.println("Creating execution action class: " + executionOutputFileName + "..."); BufferedWriter executionOutputFile = new BufferedWriter( new FileWriter(executionOutputFileName)); String executionActionFileContent = new String(); executionActionFileContent += getExecutionClassesFileHeader(); executionActionFileContent += "package " + executionActionsPackage + ";\n\n"; executionActionFileContent += actionGenerator .getExecutionActionClass(currentAction, modelActionsPackage); executionOutputFile.write(Util.format(executionActionFileContent)); executionOutputFile.close(); } } catch (IOException e) { System.out .println("There were errors while creating classes for the MMPM action " + currentAction.getName() + ": " + e.getMessage()); } } } catch (Exception e) { System.out .println("There was an unexpected error while creating actions. Actions generation aborted"); e.printStackTrace(); } } /* Now create all the condition classes. */ if (conditions.size() != 0) { try { /* * Get output directories and packages from the XML * configuration file. */ String modelConditionsPackage = root.getChildText(MODEL_CONDITIONS_PACKAGE_TAG); String modelConditionsOutputDirectory = relativePath + root.getChildText(MODEL_CONDITIONS_OUTPUT_DIRECTORY_TAG); /* If directory ends in separator, remove it. */ modelConditionsOutputDirectory = Util .removeDirectorySeparator(modelConditionsOutputDirectory); String executionConditionsPackage = root .getChildText(EXECUTION_CONDITIONS_PACKAGE_TAG); String executionConditionsOutputDirectory = relativePath + root.getChildText(EXECUTION_CONDITIONS_OUTPUT_DIRECTORY_TAG); /* Create ouput directories. */ File modelConditionsDirectory = new File(modelConditionsOutputDirectory); if (!modelConditionsDirectory.isDirectory()) { System.out.println("Creating output directory: " + modelConditionsOutputDirectory + "..."); modelConditionsDirectory.mkdirs(); } File executionConditionsDirectory = new File(executionConditionsOutputDirectory); if (!executionConditionsDirectory.isDirectory()) { System.out.println("Creating output directory: " + executionConditionsOutputDirectory + "..."); executionConditionsDirectory.mkdirs(); } /* Create condition classes. */ ConditionsGenerator conditionGenerator = new ConditionsGenerator(); for (ParsedMethod currentCondition : conditions) { try { /* Create model condition. */ String modelOutputFileName = modelConditionsOutputDirectory + File.separator + currentCondition.getName() + ".java"; boolean skipModelCondition = false; if (Util.fileExists(modelOutputFileName)) { System.out.print(modelOutputFileName + " already exists. "); if (overwrite) { System.out.println("It will be overwritten."); } else { System.out.println(modelOutputFileName + " will not be generated."); skipModelCondition = true; } } if (!skipModelCondition) { System.out.println("Creating model condition class: " + modelOutputFileName + "..."); BufferedWriter modelOutputFile = new BufferedWriter(new FileWriter( modelOutputFileName)); String modelConditionFileContent = new String(); modelConditionFileContent += getModelClassesFileHeader(); modelConditionFileContent += "package " + modelConditionsPackage + ";\n\n"; modelConditionFileContent += conditionGenerator .getModelConditionClass(currentCondition, executionConditionsPackage); modelOutputFile.write(Util.format(modelConditionFileContent)); modelOutputFile.close(); } /* Create execution condition. */ boolean skipExecutionCondition = false; String executionOutputFileName = executionConditionsOutputDirectory + File.separator + currentCondition.getName() + ".java"; if (Util.fileExists(executionOutputFileName)) { System.out.print(executionOutputFileName + " already exists. "); if (overwrite) { System.out.println("It will be overwritten."); } else { System.out.println(executionOutputFileName + " will not be generated."); skipExecutionCondition = true; } } if (!skipExecutionCondition) { System.out.println("Creating execution condition class: " + executionOutputFileName + "..."); String executionConditionFileContent = new String(); executionConditionFileContent += getExecutionClassesFileHeader(); executionConditionFileContent += "package " + executionConditionsPackage + ";\n\n"; executionConditionFileContent += conditionGenerator .getExecutionConditionClass(currentCondition, modelConditionsPackage); BufferedWriter executionOutputFile = new BufferedWriter( new FileWriter(executionOutputFileName)); executionOutputFile.write(Util .format(executionConditionFileContent)); executionOutputFile.close(); } } catch (IOException e) { System.out .println("There were errors while creating classes for the MMPM sensor " + currentCondition.getName() + ": " + e.getMessage()); } } } catch (Exception e) { System.out .println("There was an unexpected error while creating conditions. Conditions generation aborted"); e.printStackTrace(); } } System.out.println("Finished successfully"); } catch (Exception e) { System.out.println("An error occurred while creating actions and conditions classes"); e.printStackTrace(); System.exit(1); } } /** * Prints the usage syntax of the application. */ private static void printUsage() { System.out.println("Syntax error. Usage: \n"); System.out .println("ActionsAndConditionsGenerator -c configurationFile [-r relativePath] [-o]\n"); System.out.println("-\"configurationFile\" is the configuration file that includes all"); System.out.println("the information required to run the actions and conditions generator."); System.out .println("-\"r\" is the root path (directory) of all the files specified within the "); System.out.println("configuration file. This is an optional option. If not specified, "); System.out.println("if will be considered to be the current execution directory."); System.out .println("-\"o\" is an optional option. If not specified, generated output files will not"); System.out .println("overwrite files in the file system. As a result, if there are files in the file"); System.out .println("system with the same name as those generated by the application, they will not"); System.out.println("be overwritten. Otherwise, it will overwrite any existing file."); } /** * Returns the file header for model actions and conditions. */ private static String getModelClassesFileHeader() { String result = new String(); DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); Date date = new Date(); String currentDate = dateFormat.format(date); result += "// ******************************************************* \n"; result += "// MACHINE GENERATED CODE \n"; result += "// DO NOT MODIFY \n"; result += "// \n"; result += "// Generated on " + currentDate + "\n"; result += "// ******************************************************* \n"; return result; } /** * Returns the file header for execution actions and conditions. */ private static String getExecutionClassesFileHeader() { String result = new String(); DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); Date date = new Date(); String currentDate = dateFormat.format(date); result += "// ******************************************************* \n"; result += "// MACHINE GENERATED CODE \n"; result += "// MUST BE CAREFULLY COMPLETED \n"; result += "// \n"; result += "// ABSTRACT METHODS MUST BE IMPLEMENTED \n"; result += "// \n"; result += "// Generated on " + currentDate + "\n"; result += "// ******************************************************* \n"; return result; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/BTLibraryGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator; import gatech.mmpm.ActionParameterType; import gatech.mmpm.tools.parseddomain.ParsedAction; import gatech.mmpm.tools.parseddomain.ParsedDomain; import gatech.mmpm.tools.parseddomain.ParsedMethod; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import jargs.gnu.CmdLineParser; import jbt.execution.core.IBTLibrary; import jbt.tools.btlibrarygenerator.util.Util; /** * BTLibraryGenerator is an application that generates a behaviour tree * libraries (classes implementing {@link IBTLibrary}) from: *

    *
  • A set of behaviour trees specified in XML files. *
  • The MMPM definition of the low level actions and conditions that are used * in the trees. *
*

* The syntax of this program is as follows: * *

 * BTLibraryGenerator -c configurationFile [-r relativePath] [-o]
 * 
* * Where configurationFile is an XML file that contains all the * information required to run this application. The syntax of such a file is: * *
 * <Configuration>
 * 
 *  <BTLibrary>
 * 
 *    <BTFile>BTFile1</BTFile>
 *    <BTFile>BTFile2</BTFile>
 *    ...
 *    <BTFile>BTFileN</BTFile>	
 * 
 *    <DomainFile>MMPMDomainFile1</DomainFile>
 *    <DomainFile>MMPMDomainFile2</DomainFile>
 *    ...
 *    <DomainFile>MMPMDomainFileN</DomainFile>
 *  
 *    <ModelActionsPackage>Name of the package where model action classes are placed</ModelActionsPackage>
 *  
 *    <ModelConditionsPackage>Name of the package where model condition classes are placed</ModelConditionsPackage>
 *  
 *    <LibraryClassName>Name of the class that is going to be created</LibraryClassName>
 *  
 *    <LibraryPackage>Name of the package for the generated BT library</LibraryPackage>
 * 	
 *    <LibraryOutputDirectory>Name of the directory where the generated library is going to be stored</LibraryOutputDirectory>
 *  
 *    </BTLibrary>
 *  
 *  <BTLibrary>
 *  ...
 *  </BTLibrary>
 *  
 *  ...
 * </Configuration>
 * 
* *

* The order in which the elements are specified is not relevant. *

* In the file the user can define several BT libraries, each one within the * BTLibrary element. For each BT library defined in a BTLibrary * element, the program will produce an output file (class implementing the * IBTLibrary interface) for the library. *

* The -r option is used to add a path to the beginning of the files listed in * the configuration file; as a result, each file is considered to be placed at * the path specified in the -r option. The -r option may not be specified, in * which case the files are considered to be at the current execution directory. *

* The -o option (standing for overwrite) is either is specified or not. * If it is not specified, the generated output files will not overwrite any * existing file in the file system, and as a result, a behaviour tree library * may not be produced in case there is a file with the same name in the file * system. If the option -o is specified, then generated output files will * overwrite any file in the file system whose name matches. * * @author Ricardo Juan Palma Durán * */ public class BTLibraryGenerator { /** Tag of the XML configuration file. */ private static final String CONFIGURATION_TAG = "Configuration"; /** Tag of the XML configuration file. */ private static final String BT_LIBRARY = "BTLibrary"; /** Tag of the XML configuration file. */ private static final String BT_FILE_TAG = "BTFile"; /** Tag of the XML configuration file. */ private static final String DOMAIN_FILE_TAG = "DomainFile"; /** Tag of the XML configuration file. */ private static final String LIBRARY_CLASS_NAME_TAG = "LibraryClassName"; /** Tag of the XML configuration file. */ private static final String LIBRARY_OUTPUT_DIRECTORY_TAG = "LibraryOutputDirectory"; /** Tag of the XML configuration file. */ private static final String LIBRARY_PACKAGE_TAG = "LibraryPackage"; /** Tag of the XML configuration file. */ private static final String MODEL_ACTIONS_PACKAGE_TAG = "ModelActionsPackage"; /** Tag of the XML configuration file. */ private static final String MODEL_CONDITIONS_PACKAGE_TAG = "ModelConditionsPackage"; public static void main(String[] args) { /* Parse command line arguments. */ CmdLineParser parser = new CmdLineParser(); CmdLineParser.Option configurationFileOption = parser.addStringOption('c', "config"); CmdLineParser.Option relativePathOption = parser.addStringOption('r', "relativepath"); CmdLineParser.Option overwriteOption = parser.addBooleanOption('o', "overwrite"); try { parser.parse(args); } catch (Exception e) { printUsage(); System.exit(1); } try { /* Read relative path from command line. */ String relativePath = (String) parser.getOptionValue(relativePathOption); if (relativePath == null) { relativePath = ""; } else { relativePath = Util.addDirectorySeparator(relativePath); } /* Overwrite option. */ Boolean overwrite = (Boolean) parser.getOptionValue(overwriteOption, Boolean.FALSE); /* * Open the configuration file and read behaviour trees file names. */ String configurationFileName; configurationFileName = (String) parser.getOptionValue(configurationFileOption); if (configurationFileName == null) { printUsage(); System.exit(1); } FileInputStream file = new FileInputStream(configurationFileName); SAXBuilder confFileBuilder = new SAXBuilder(); Document confFileDoc = confFileBuilder.build(file); file.close(); /* * Parse and create all the libraries specified in the configuration * file. */ Element root = confFileDoc.getRootElement(); List btLibrariesDescription = root.getChildren(BT_LIBRARY); for (Element currentBTLibrary : btLibrariesDescription) { try { /* * Read library class name, packages and output directory * from the configuration file. */ System.out.print("Creating next BT library "); String libraryClassName = currentBTLibrary.getChildText(LIBRARY_CLASS_NAME_TAG); if (libraryClassName == null) { System.out .println("There were errors while parsing the configuration file: a name for the BT library class must be specified"); continue; } System.out.println("\"" + libraryClassName + "\"..."); String libraryDirectory = relativePath + currentBTLibrary.getChildText(LIBRARY_OUTPUT_DIRECTORY_TAG); libraryDirectory = Util.removeDirectorySeparator(libraryDirectory); String libraryPackage = currentBTLibrary.getChildText(LIBRARY_PACKAGE_TAG); if (libraryPackage == null) { System.out .println("There were errors while parsing the configuration file: a name for the BT library Java package must be specified"); continue; } String modelActionsPackage = currentBTLibrary .getChildText(MODEL_ACTIONS_PACKAGE_TAG); if (modelActionsPackage == null) { System.out .println("There were errors while parsing the configuration file: the name of the model low level actions Java package must be specified"); continue; } String modelConditionsPackage = currentBTLibrary .getChildText(MODEL_CONDITIONS_PACKAGE_TAG); if (modelConditionsPackage == null) { System.out .println("There were errors while parsing the configuration file: the name of the model low level conditions Java package must be specified"); continue; } /* Read the names of the files that contain the BTs. */ List btFileNames = new LinkedList(); List childrenElements = currentBTLibrary.getChildren(BT_FILE_TAG); for (Element e : childrenElements) { btFileNames.add(relativePath + e.getText()); } /* * Now read all the domain actions and conditions (boolean * conditions) stored in domain files. */ List actions = new LinkedList(); List conditions = new LinkedList(); List domainFileNames = new LinkedList(); childrenElements = currentBTLibrary.getChildren(DOMAIN_FILE_TAG); for (Element e : childrenElements) { domainFileNames.add(e.getText()); } for (String domainFile : domainFileNames) { /* Get the parsed domain of the current file. */ domainFile = relativePath + domainFile; System.out.println("Reading and parsing " + domainFile + "..."); FileInputStream currentFile = new FileInputStream(domainFile); SAXBuilder mmpmFileBuilder = new SAXBuilder(); Document mmpmDoc = mmpmFileBuilder.build(currentFile); currentFile.close(); ParsedDomain domain = new ParsedDomain(); domain.init(mmpmDoc.getRootElement(), null); actions.addAll(domain.getActionSet().getAction()); List booleanSensors = new LinkedList(); for (ParsedMethod sensor : domain.getSensorSet().getMethods()) { if (sensor.getReturnedType() == ActionParameterType.BOOLEAN) { booleanSensors.add(sensor); } } conditions.addAll(booleanSensors); } /* Now generate the library. */ String outputFileName = libraryDirectory + File.separator + libraryClassName + ".java"; if (Util.fileExists(outputFileName)) { System.out.print(outputFileName + " already exists. "); if (overwrite) { System.out.println("It will be overwritten."); } else { System.out.println("The behaviour tree library will not be generated."); System.out.println("Finished"); System.exit(0); } } System.out.println("Creating library " + outputFileName + "..."); String outputFileContent = new String(); outputFileContent += getClassFileHeader(); outputFileContent += "package " + libraryPackage + ";\n\n"; jbt.tools.btlibrarygenerator.librarygenerator.BTLibraryGenerator generator = new jbt.tools.btlibrarygenerator.librarygenerator.BTLibraryGenerator(); outputFileContent += generator.getBTLibraryDeclaration(libraryClassName, modelActionsPackage, modelConditionsPackage, actions, conditions, btFileNames); /* And create the output file for it. */ File libraryOutputDirectory = new File(libraryDirectory); if (!libraryOutputDirectory.isDirectory()) { libraryOutputDirectory.mkdirs(); } BufferedWriter outputFile = new BufferedWriter(new FileWriter(outputFileName)); outputFile.write(Util.format(outputFileContent)); outputFile.close(); System.out .println("\"" + libraryClassName + "\"" + " was successfully created"); } catch (Exception e) { System.out .println("There was an unexpected error while creating the current BT library. Skipped to next"); e.printStackTrace(); } } } catch (Exception e) { System.out .println("There was an unrecoverable error while creating the BT libraries. Program aborted"); e.printStackTrace(); System.exit(1); } } /** * Prints the usage syntax of the application. */ private static void printUsage() { System.out.println("Syntax error. Usage: \n"); System.out.println("BTLibraryGenerator -c configurationFile [-r relativePath] [-o]"); System.out.println("-\"configurationFile\" is the configuration file that includes all"); System.out.println("the information required to run the behaviour tree library generator."); System.out .println("-\"r\" is the root path (directory) of all the files specified within the "); System.out.println("configuration file. This is an optional option. If not specified, "); System.out.println("if will be considered to be the current execution directory."); System.out .println("-\"o\" is an optional option. If not specified, the generated output files will not"); System.out .println("overwrite files in the file system, and as a result, a behaviour tree library"); System.out .println("may not be produced in case there is a file with the same name in the file system"); System.out.println("system. Otherwise, it will overwrite any existing file."); } /** * Returns the file header for the BT library. */ private static String getClassFileHeader() { String result = new String(); DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); Date date = new Date(); String currentDate = dateFormat.format(date); result += "// ******************************************************* \n"; result += "// MACHINE GENERATED CODE \n"; result += "// DO NOT MODIFY \n"; result += "// \n"; result += "// Generated on " + currentDate + "\n"; result += "// ******************************************************* \n"; return result; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/librarygenerator/BTLibraryGenerationException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.librarygenerator; /** * Exception thrown when there is an error creating an IBTLibrary. * * @author Ricardo Juan Palma Durán * */ public class BTLibraryGenerationException extends Exception { private static final long serialVersionUID = 1L; public BTLibraryGenerationException(String message) { super(message); } public BTLibraryGenerationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/librarygenerator/BTLibraryGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.librarygenerator; import gatech.mmpm.tools.parseddomain.ParsedAction; import gatech.mmpm.tools.parseddomain.ParsedMethod; import java.io.FileInputStream; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import jbt.execution.core.IBTLibrary; import jbt.model.core.ModelTask; import jbt.tools.btlibrarygenerator.modelbtgenerator.ModelBTGenerator; import jbt.util.Pair; /** * BTLibraryGenerator is used for generating String expressions representing the * declaration of a Java class that implements the {@link IBTLibrary} interface, * which also includes a list of behaviour trees that can be accessed. * * @author Ricardo Juan Palma Durán * */ public class BTLibraryGenerator { /** * The name of the class of the elements through which the iterator of * IBTLibrary iterates. */ private static final String ITERABLE_ELEMENTS_CLASS_NAME = Pair.class.getCanonicalName() + "<" + String.class.getCanonicalName() + ", " + ModelTask.class.getCanonicalName() + ">"; /** * The name of the class of the iterator of the IBTLibrary. */ private static final String ITERATOR_CLASS_NAME = Iterator.class.getCanonicalName() + "<" + ITERABLE_ELEMENTS_CLASS_NAME + ">"; /** Attribute "name" of the BT XML file. */ private static final String NAME_ATTR = "name"; /** * This method creates an String representing an expression that declares a * class that implements the {@link IBTLibrary} interface and contains all * the behaviour trees in the files specified in treeFileNames. * The created library will have libraryClassName as name. *

* In order to generate the expression for the library, some additional * information must be provided: *

    *
  • actionsPackageName is the name of the Java package that * contains all the low level actions that are used in the tree. *
  • conditionsPackageName is the name of the Java package * that contains all the low level conditions that are used in the tree. *
  • actionsDefinition is a list containing the MMPM * definition of all the low level actions that are used in the tree. *
  • conditionsDefinition is a list containing the MMPM * definition of all the low level conditions that are used in the tree. *
* * @param libraryClassName * name of the class that will be generated. * @param actionsPackageName * name of the Java package that contains all the low level * actions that are used in the tree. * @param conditionsPackageName * name of the Java package that contains all the low level * conditions that are used in the tree. * @param actionsDefinition * list containing the MMPM definition of all the low level * actions that are used in the tree. * @param conditionsDefinition * list containing the MMPM definition of all the low level * conditions that are used in the tree. * @param treeFileNames * names of the files that contain all the behaviour trees that * the generated library will contain. * @return a String as described above. * @throws BTLibraryGenerationException * if there is an error creating the library. */ public String getBTLibraryDeclaration(String libraryClassName, String actionsPackageName, String conditionsPackageName, List actionsDefinition, List conditionsDefinition, List treeFileNames) throws BTLibraryGenerationException { try { String result = new String(); result += "/** BT library that includes the trees read from the following files:\n
    "; for (String file : treeFileNames) { result += "
  • " + file + "
  • \n"; } result += "
*/"; /* Class header. */ result += "public class " + libraryClassName + " implements " + IBTLibrary.class.getCanonicalName() + "{\n"; /* * Node declare all the behaviour trees as static variables. First, * we declare them. Then we will generate appropriate expressions * for them. */ List treeNames = new LinkedList(); for (String fileName : treeFileNames) { String treeName = getTreeName(fileName); treeNames.add(treeName); result += "/**Tree generated from file " + fileName + ".*/"; result += "private static " + ModelTask.class.getCanonicalName() + " " + treeName + ";\n"; } result += "\n"; result += "/*Static initialization of all the trees.*/\n"; result += "static{\n"; /* Now we generate expressions for each behaviour tree. */ ModelBTGenerator generator = new ModelBTGenerator(); Iterator fileNamesIt = treeFileNames.iterator(); Iterator treeNamesIt = treeNames.iterator(); while (fileNamesIt.hasNext()) { String treeVariableName = treeNamesIt.next(); String fileName = fileNamesIt.next(); String modelBTDeclaration = generator.getModelBTDeclaration(treeVariableName, fileName, actionsPackageName, conditionsPackageName, actionsDefinition, conditionsDefinition, false); result += modelBTDeclaration + "\n\n"; } result += "}\n\n"; /* Adding the getBT() method of the IBTLibrary interface. */ result += getGetBTMethod(treeNames) + "\n\n"; /* * Adding the iterator() method if the Iterable interface (which is * implemented by IBTLibrary). */ result += getIteratorMethod(treeNames) + "\n\n"; /* * Creates an internal iterator class that knows how to iterate * through the trees of the created library. */ result += getLibraryIterator(treeNames) + "\n"; result += "}\n"; return result; } catch (Exception e) { throw new BTLibraryGenerationException("Could not generate the BT library: " + e.getMessage(), e); } } /** * Given the name of a file storing a behaviour tree in XML format (the * format of BT Editor), this method returns the name of the tree * (which is extracted from the Root node of the XML file). * */ private String getTreeName(String treeFileName) throws Exception { FileInputStream file = new FileInputStream(treeFileName); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(file); Element documentRoot = doc.getRootElement(); Element treeRoot = (Element) documentRoot.getChildren().get(0); String result = treeRoot.getAttributeValue(NAME_ATTR); file.close(); return result; } /** * Creates the {@link IBTLibrary#getBT(String)} method for the BT library. * treeNames is the list of names of the trees in the library. */ private String getGetBTMethod(List treeNames) { String result = new String(); result += "/**Returns a behaviour tree by its name, or null in case it cannot be found. " + "It must be noted that the trees that are retrieved belong to the class, not to the instance (that is, the trees are static members of the class), " + "so they are shared among all the instances of this class.*/"; result += "public " + ModelTask.class.getCanonicalName() + " getBT(" + String.class.getCanonicalName() + " name){\n"; for (String treeName : treeNames) { result += "if(name.equals(" + "\"" + treeName + "\")){\n"; result += "return " + treeName + ";\n"; result += "}\n"; } result += "return null;\n"; result += "}"; return result; } /** * Creates an expression for the {@link Iterable#iterator()} method of the * IBTLibrary that is created. treeNames is the names of the * trees that the library contains. The function will just return an * instance of the private class BTLibraryIterator (see * {@link #getLibraryIterator(List)}). Note that the remove() * operation is not supported by this iterator. */ private String getIteratorMethod(List treeNames) { String result = new String(); result += "/**Returns an Iterator that is able to iterate through all the elements in the library.\n " + "It must be noted that the iterator does not support the \"remove()\" operation.\n"; result += "It must be noted that the trees that are retrieved belong to the class, " + "not to the instance (that is, the trees are static members of the class), so " + "they are shared among all the instances of this class.*/"; result += "public " + ITERATOR_CLASS_NAME + " iterator(){\n"; result += "return new BTLibraryIterator();\n"; result += "}"; return result; } /** * Declares a class called BTLibraryIterator, which implements the * {@link Iterator} interface, and that is able to iterate through the trees * of the library. treeNames contains the names of the trees in * the library. Note that the remove() operation is not * supported by this iterator. */ private String getLibraryIterator(List treeNames) { String result = new String(); result += "private class BTLibraryIterator implements " + ITERATOR_CLASS_NAME + "{\n"; result += "static final long numTrees = " + treeNames.size() + ";\n"; result += "long currentTree = 0;\n\n"; result += "public boolean hasNext(){\n"; result += "return this.currentTree < numTrees;\n"; result += "}\n\n"; result += "public " + ITERABLE_ELEMENTS_CLASS_NAME + " next(){\n"; result += "this.currentTree++;\n\n"; for (int i = 0; i < treeNames.size(); i++) { String treeName = treeNames.get(i); result += "if((this.currentTree - 1) == " + i + "){\n"; result += "return new " + ITERABLE_ELEMENTS_CLASS_NAME + "(\"" + treeName + "\", " + treeName + ")" + ";\n"; result += "}\n\n"; } result += "throw new " + NoSuchElementException.class.getCanonicalName() + "();\n"; result += "}\n\n"; result += "public void remove(){\n"; result += "throw new " + UnsupportedOperationException.class.getCanonicalName() + "();\n"; result += "}\n"; result += "}"; return result; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/lowlevelgenerator/ActionsGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.lowlevelgenerator; import java.util.LinkedList; import java.util.List; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.action.ExecutionAction; import jbt.model.core.ModelTask; import jbt.model.task.leaf.action.ModelAction; import jbt.tools.btlibrarygenerator.util.Util; import jbt.util.Pair; import gatech.mmpm.tools.parseddomain.ParsedAction; import gatech.mmpm.tools.parseddomain.ParsedActionParameter; /** * Class used for generating Java-BT expressions for MMPM actions (represented * as {@link ParsedAction} objects). *

* Given a ParsedAction, this class can be used for generating a * {@link ModelAction} or an {@link ExecutionAction} for such an action. In * reality, this class does not produce instances of ModelAction and * ExecutionAction, but String representations of the Java implementation of * those classes. * * @see ConditionsGenerator * * @author Ricardo Juan Palma Durán * */ public class ActionsGenerator { /** * This method is used for creating a String representation of the * definition of a Java class for action. *

* The output class extends {@link ModelAction}, and it is the conceptual * representation of action. *

* The generated class contains two private fields for each parameter of * action. Given a parameter with name pName, two * class variables are created in the output class: *

    *
  • pName. The type of this variable will be a Java type * compatible with that of the MMPM definition of pName in * action (according to * {@link Util#fromMMPMParameterType(gatech.mmpm.ActionParameterType)}). * This variable represents the value of the parameter in case a value has * been specified when constructing an instance of the output class. *
  • pNameLoc. This is a String representing where the * variable pName can be located in the context. In case a * value is not specified for the variable, the task's context must be * searched for a variable whose name is pNameLoc. *
* * The * {@link ModelTask#createExecutor(jbt.execution.core.BTExecutor, ExecutionTask)} * method of the output class returns an instance of the corresponding * {@link ExecutionAction}. This method assumes that the corresponding * ExecutionTask's name is action's name too, and that the * class is located in the Java package * executionActionPackageName. The corresponding * ExecutionAction receives in its constructor all the private class * variables of the output class, in the same order as they are declared in * the output class. In a similar way, * {@link #getExecutionActionClass(ParsedAction)} can be used to construct a * String expression for action. *

* The first argument of the constructor of the output class is the guard. * The rest of them are values for each private class variable of the output * class, in the same order as they are declared. * * @param action * the action whose representation as a ModelAction is going to * be created. * @param executionActionPackageName * the name of the package that contains the ExecutionAction * associated to action. It must not be empty, and * it has to be of the form level1.level2. ... .levelN. * @return a String representation of action as a ModelAction * class. */ public String getModelActionClass(ParsedAction action, String executionActionPackageName) { String result = new String(); /* First element is the type, and the second element is the value. */ List> params = new LinkedList>(); /* Extract all the parameters. */ for (ParsedActionParameter parameter : action.getParameters()) { params.add(new Pair(Util.fromMMPMParameterType(parameter.getType()), parameter.getName())); } result += "/** ModelAction class created from MMPM action " + action.getName() + ". */"; /* Action class header. */ result += getModelActionClassHeader(action) + "\n"; /* Class variables. */ result += CommonCodeGenerationUtilities.getClassVariables(params) + "\n\n"; /* Constructor. */ result += CommonCodeGenerationUtilities.getModelConstructor(action.getName(), params) + "\n\n"; /* "createExecutor()" method. */ result += CommonCodeGenerationUtilities.getCreateExecutorMethod(action.getName(), executionActionPackageName, params) + "\n"; result += "}"; return result; } /** * This method is used for creating a String representation of the * definition of a Java class for action. *

* The output class extends {@link ExecutionAction}, and it defines how * action actually works. *

* The generated class contains two private fields for each parameter of * action. Given a parameter with name pName, two * class variables are created in the output class: *

    *
  • pName. The type of this variable will be a Java type * compatible with that of the MMPM definition of pName in * action (according to * {@link Util#fromMMPMParameterType(gatech.mmpm.ActionParameterType)}). * This variable represents the value of the parameter in case a value has * been specified when constructing an instance of the output class. *
  • pNameLoc. This is a String representing where the * variable pName can be located in the context. In case a * value is not specified for the variable, the task's context must be * searched for a variable whose name is pNameLoc. *
*

* For all of action's parameters, a getter method is * constructed. Given a parameter with name pName, a getter * named getPName() is constructed. If at construction time a * value was specified for pName, then getPName() * returns such a value. Otherwise, getPName() will search for * a variable of name pNameLoc in the context, and will return * its value. *

* This method also constructs an empty skeleton for all abstract methods of * ExecutionAction. The getter methods can be used in the implementation of * those abstract methods in order to retrieve the action's parameters. *

* The first argument of the constructor of the output class is its * corresponding ModelAction. The second one is its corresponding * BTExecutor. The rest of them are values for each private class variable * of the output class, in the same order as they are declared. * * @param action * the action whose representation as an ExecutionAction is going * to be created. * @return a String representation of action as an * ExecutionAction class. */ public String getExecutionActionClass(ParsedAction action, String modelActionPackageName) { String result = new String(); /* First element is the type, and the second element is the value. */ List> params = new LinkedList>(); for (ParsedActionParameter parameter : action.getParameters()) { params.add(new Pair(Util.fromMMPMParameterType(parameter.getType()), parameter.getName())); } result += "/** ExecutionAction class created from MMPM action " + action.getName() + ". */"; /* Action class header. */ result += getExecutionActionClassHeader(action) + "\n"; /* Class variables. */ result += CommonCodeGenerationUtilities.getClassVariables(params) + "\n\n"; /* Constructor. */ result += CommonCodeGenerationUtilities.getExecutionConstructor(action.getName(), modelActionPackageName, params) + "\n\n"; /* Getters. */ result += CommonCodeGenerationUtilities.getGetters(params) + "\n\n"; /* Abstract methods. */ result += CommonCodeGenerationUtilities.getAbstractMethods(); result += "}"; return result; } private String getModelActionClassHeader(ParsedAction action) { return "public class " + action.getName() + " extends " + ModelAction.class.getCanonicalName() + "{"; } private String getExecutionActionClassHeader(ParsedAction action) { return "public class " + action.getName() + " extends " + ExecutionAction.class.getCanonicalName() + "{"; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/lowlevelgenerator/CommonCodeGenerationUtilities.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.lowlevelgenerator; import java.util.List; import jbt.execution.core.BTExecutor; import jbt.execution.core.ExecutionTask; import jbt.execution.core.BTExecutor.BTExecutorList; import jbt.execution.core.ExecutionTask.Status; import jbt.execution.core.ITaskState; import jbt.execution.task.leaf.action.ExecutionAction; import jbt.execution.task.leaf.condition.ExecutionCondition; import jbt.model.core.ModelTask; import jbt.model.task.leaf.action.ModelAction; import jbt.model.task.leaf.condition.ModelCondition; import jbt.util.Pair; /** * Some helper functions used for easily generating String representations of {@link ModelAction}s, * {@link ModelCondition}s, {@link ExecutionAction}s and {@link ExecutionCondition}s from MMPM * actions and sensors. *

* {@link ActionsGenerator} and {@link ConditionsGenerator} share most of their functionality, so * this class just gathers all of the common parts when they are generating classes. * * @see ActionsGenerator * @see ConditionsGenerator * * @author Ricardo Juan Palma Durán * */ public class CommonCodeGenerationUtilities { /** Message for a "to do". */ static final String TODO_MESSAGE = "/* TODO: this method's implementation must be completed. */"; /** * Suffix that is placed at the end of the class's variables to represent the variable that will * store the location in the context of the original variable. */ static final String PARAM_LOCATION_SUFFIX = "Loc"; /** * Given a list of class "parameters", this method returns a String representation of the * declaration of those parameters as private members of a class. *

* params contains all the parameters. Each Pair represents a parameter, being the * first element its class and the second element its name. *

* The returned representation is tabulated one unit. * * @param params * the set of parameters from which a String representation is created. * @return a String representation of the declaration of the parameters in params. */ static String getClassVariables(List> params) { String result = new String(); for (Pair currentParam : params) { result += "/**Value of the parameter \"" + currentParam.getSecond() + "\" in case its value is specified at construction time. null otherwise.*/"; result += "private " + currentParam.getFirst().getCanonicalName() + " " + currentParam.getSecond() + ";" + "\n"; result += "/**Location, in the context, of the parameter \"" + currentParam.getSecond() + "\" in case its value is not specified at construction time. null otherwise.*/"; result += "private " + String.class.getCanonicalName() + " " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ";" + "\n"; } if (params.size() != 0) { result = result.substring(0, result.length() - 1); } return result; } /** * Creates a String expression for the constructor of a ModelAction or ModelCondition. The name * of the class is modelClassName, and params is a list of parameters * (the first element of each pair is the class of the parameter and the second element is its * name). *

* For each parameter of name pName in params, the constructor will * receive two parameters, one of name pName, whose type is that specified in the * list of parameters, and another one of name pNameLoc, whose type is String, and * which represents the place in the context where pName must be looked for in case * a value is not specified for it. *

* The constructor assigns the value of every input argument to its corresponding class * variable. * * @param modelClassName * the name of the ModelAction or ModelCondition class whose constructor is going to * be created. * @param params * the list of parameters to be used when building the constructor. * @return a String expression for the constructor, as described above. */ static String getModelConstructor(String modelClassName, List> params) { String result = new String(); result += "/** Constructor. Constructs an instance of " + modelClassName + ".\n"; for (Pair currentParam : params) { result += "@param " + currentParam.getSecond() + " value of the parameter \"" + currentParam.getSecond() + "\", or null in case it should be read from the context. If null, " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " cannot be null.\n"; result += "@param " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " in case " + currentParam.getSecond() + " is null, this variable represents the place in the context where the parameter's value will be retrieved from.\n"; } result += "*/"; result += "public " + modelClassName + "("; String stringParams = new String(); stringParams += ModelTask.class.getCanonicalName() + " guard, "; if (params.size() != 0) { for (Pair currentParam : params) { stringParams += currentParam.getFirst().getCanonicalName() + " " + currentParam.getSecond() + ", "; stringParams += String.class.getCanonicalName() + " " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ", "; } } stringParams = stringParams.substring(0, stringParams.length() - 2); result += stringParams + "){\n"; result += "super(guard);\n"; for (Pair currentParam : params) { result += "this." + currentParam.getSecond() + " = " + currentParam.getSecond() + ";\n"; result += "this." + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " = " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ";\n"; } result += "}"; return result; } /** * Creates a String expression for the constructor of a ExecutionAction or ExecutionCondition. * The name of the class is executionClassName, and params is a list * of parameters (the first element of each pair is the class of the parameter and the second * element is its name). *

* For each parameter of name pName in params, the constructor will * receive two parameters, one of name pName, whose type is that specified in the * list of parameters, and another one of name pNameLoc, whose type is String, and * which represents the place in the context where pName must be looked for in case * a value is not specified for it. *

* The constructor assigns the value of every input argument to its corresponding class * variable. *

* modelClassPackageName is the name of the package that contains the corresponding * model class (the name of the corresponding model class is assumed to be * executionClassName. * * @param executionClassName * the name of the ModelAction or ModelCondition class whose constructor is going to * be created. * @param params * the list of parameters to be used when building the constructor. * @param modelClassPackageName * the name of the package that contains the corresponding model class. * @return a String expression for the constructor, as described above. */ static String getExecutionConstructor(String executionClassName, String modelClassPackageName, List> params) { String result = new String(); result += "/** Constructor. Constructs an instance of " + executionClassName + " that is able to run a " + modelClassPackageName + "." + executionClassName + ".\n"; for (Pair currentParam : params) { result += "@param " + currentParam.getSecond() + " value of the parameter \"" + currentParam.getSecond() + "\", or null in case it should be read from the context. If null, " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " cannot be null.\n"; result += "@param " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " in case " + currentParam.getSecond() + " is null, this variable represents the place in the context where the parameter's value will be retrieved from.\n"; } result += "*/"; result += "public " + executionClassName + "("; String stringParams = new String(); stringParams += modelClassPackageName + "." + executionClassName + " modelTask, " + BTExecutor.class.getCanonicalName() + " executor, " + ExecutionTask.class.getCanonicalName() + " parent, "; if (params.size() != 0) { for (Pair currentParam : params) { stringParams += currentParam.getFirst().getCanonicalName() + " " + currentParam.getSecond() + ", "; stringParams += String.class.getCanonicalName() + " " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ", "; } } stringParams = stringParams.substring(0, stringParams.length() - 2); result += stringParams + "){\n"; result += "super(modelTask, executor, parent);\n\n"; if (params.size() != 0) { result += "\n\n"; for (Pair currentParam : params) { result += "this." + currentParam.getSecond() + " = " + currentParam.getSecond() + ";\n"; result += "this." + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + " = " + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ";\n"; } } result += "}"; return result; } /** * This method creates a String representing the definition of the * {@link ModelTask#createExecutor(BTExecutor, ExecutionTask)} method of a class. *

* The method will return an instance of an ExecutionTask whose name is * executionClassName and placed in the package * executionActionPackageName. *

* params represents the list of parameters that the ExecutionTask will receive in * its constructor. The first argument that is passed to the ExecutionTask's constructor is * this; the second argument is executor (executor * represents the {@link BTExecutor} that is passed to the createExecutor() method. * The rest of the parameters that are passed to the constructor are those in * params. Each Pair in params represents a parameter, being the first * element its class and the second element its name. For each parameter in params, * the constructor receives two parameters, the parameter itself (for instance * pName) , and the location of the parameter in the context (pNameLoc * ). * * @param executionClassName * the name of the ExecutionTask that the method will return. * @param params * the list of parameters that the ExecutionTask executionClassName will * receive in its constructor, apart from this and executor * . * @param executionClassPackageName * the name of the Java package that contains the class * executionClassName. * @return a String representation for the createExecutor() method as described * above. */ static String getCreateExecutorMethod(String executionClassName, String executionClassPackageName, List> params) { /* * TODO: this method could be improved, since only the names of the parameters are required. */ String result = new String(); result += "/** Returns a " + executionClassPackageName + "." + executionClassName + " task that is able to run this task. */"; result += "public " + ExecutionTask.class.getCanonicalName() + " createExecutor(" + BTExecutor.class.getCanonicalName() + " executor, " + ExecutionTask.class.getCanonicalName() + " parent" + "){\n"; String returnStatement = "return new " + executionClassPackageName + "." + executionClassName + "("; returnStatement += "this, executor, parent, "; if (params.size() != 0) { for (Pair currentParam : params) { returnStatement += "this." + currentParam.getSecond() + ", "; returnStatement += "this." + currentParam.getSecond() + PARAM_LOCATION_SUFFIX + ", "; } } returnStatement = returnStatement.substring(0, returnStatement.length() - 2); returnStatement += ");\n"; result += returnStatement; result += "}"; return result; } /** * Given a list of parameters, this method creates an expression declaring a public getter * method for each parameter. These getters must be used by a class extending * {@link ExecutionTask}, since the implementation of such getters make use of the context of * the task. *

* params contains all the parameters. Each Pair represents a parameter, being the * first element its class and the second element its name. *

* Given a parameter of name pName, the getter function has as a name * getPName(). If the variable to get is not null, the getter method returns the * variable. Otherwise, it searches for the variable in the context by using the class private * variable getPNameLoc. * * @param params * the set of parameters from which getters are going to be obtained. * @return a String representation of all the getters for the parameters in params> params) { String result = new String(); for (Pair currentParam : params) { String currentGetter = getGetter(currentParam); result += currentGetter + "\n\n"; } if (params.size() != 0) { result = result.substring(0, result.length() - 2); } return result; } /** * Given a parameter, this method returns a getter method for it. This getter must be used by a * class extending {@link ExecutionTask}, since the implementation of such getter make use of * the context of the task. *

* param represents the parameter, being the first element its class and the second * element its name. *

* Given a parameter of name pName, the getter function has as a name * getPName(). If the variable to get is not null, the getter method returns the * variable. Otherwise, it searches for the variable in the context by using the class private * variable getPNameLoc. * * @param param * the parameter from which the getter method is obtained. * @return a String representation for the parameter param. */ static String getGetter(Pair param) { String result = new String(); result += "/** Returns the value of the parameter \"" + param.getSecond() + "\", or null in case it has not been specified or it cannot be found in the context. */"; result += "public " + param.getFirst().getCanonicalName() + " get" + Character.toUpperCase(param.getSecond().charAt(0)) + param.getSecond().substring(1) + "(){\n"; result += "if(this." + param.getSecond() + " != null){\n"; result += "return this." + param.getSecond() + ";\n"; result += "}\n"; result += "else{\n"; result += "return (" + param.getFirst().getCanonicalName() + ")" + "this.getContext().getVariable(" + "this." + param.getSecond() + PARAM_LOCATION_SUFFIX + ");\n"; result += "}\n"; result += "}"; return result; } /** * Returns a String representation of the declaration of the abstract methods of the * {@link ExecutionTask} class. The String is tabulated one unit. * * @return a String representation of the declaration of the abstract methods of the * {@link ExecutionTask} class. */ static String getAbstractMethods() { String result = new String(); result += "protected void internalSpawn(){\n"; result += "/* Do not remove this first line unless you know what it does and you need not do it. */\n"; result += "this.getExecutor().requestInsertionIntoList(" + BTExecutorList.class.getCanonicalName() + "." + BTExecutorList.TICKABLE.toString() + ",this);\n"; result += TODO_MESSAGE + "\n"; result += "System.out.println(this.getClass().getCanonicalName() +" + "\" spawned\");"; result += "}\n\n"; result += "protected " + Status.class.getCanonicalName() + " internalTick(){\n"; result += "/* TODO: this method's implementation must be completed. This function should only return Status.SUCCESS, Status.FAILURE or Status.RUNNING. No other values are allowed. */" + "\n"; result += "return " + Status.class.getCanonicalName() + "." + Status.SUCCESS.toString() + ";\n"; result += "}\n\n"; result += "protected void internalTerminate(){\n"; result += TODO_MESSAGE + "\n"; result += "}\n\n"; result += "protected void restoreState(" + ITaskState.class.getCanonicalName() + " state){\n"; result += TODO_MESSAGE + "\n"; result += "}\n\n"; result += "protected " + ITaskState.class.getCanonicalName() + " storeState(){\n"; result += TODO_MESSAGE + "\n"; result += "return null;"; result += "}\n"; result += "protected " + ITaskState.class.getCanonicalName() + " storeTerminationState(){\n"; result += TODO_MESSAGE + "\n"; result += "return null;"; result += "}\n"; return result; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/lowlevelgenerator/ConditionsGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.lowlevelgenerator; import java.util.LinkedList; import java.util.List; import jbt.execution.core.ExecutionTask; import jbt.execution.task.leaf.condition.ExecutionCondition; import jbt.model.core.ModelTask; import jbt.model.task.leaf.condition.ModelCondition; import jbt.tools.btlibrarygenerator.util.Util; import jbt.util.Pair; import gatech.mmpm.tools.parseddomain.ParsedActionParameter; import gatech.mmpm.tools.parseddomain.ParsedMethod; /** * Class used for generating Java-BT expressions for MMPM sensors (represented * as {@link ParsedMethod} objects). *

* Given a ParsedMethod, this class can be used for generating a * {@link ModelCondition} or an {@link ExecutionCondition} for such a sensor. In * reality, this class does not produce instances of ModelCondition and * ExecutionCondition, but String representations of the Java implementation of * those classes. * * @see ActionsGenerator * * @author Ricardo Juan Palma Durán * */ public class ConditionsGenerator { /** * This method is used for creating a String representation of the * definition of a Java class for condition. *

* The output class extends {@link ModelCondition}, and it is the conceptual * representation of condition. *

* The generated class contains two private fields for each parameter of * condition. Given a parameter with name pName, * two class variables are created in the output class: *

    *
  • pName. The type of this variable will be a Java type * compatible with that of the MMPM definition of pName in * condition (according to * {@link Util#fromMMPMParameterType(gatech.mmpm.ActionParameterType)}). * This variable represents the value of the parameter in case a value has * been specified when constructing an instance of the output class. *
  • pNameLoc. This is a String representing where the * variable pName can be located in the context. In case a * value is not specified for the variable, the task's context must be * searched for a variable whose name is pNameLoc. *
* * The * {@link ModelTask#createExecutor(jbt.execution.core.BTExecutor, ExecutionTask)} * method of the output class returns an instance of the corresponding * {@link ExecutionCondition}. This method assumes that the corresponding * ExecutionCondition's name is condition's name too, and that * the class is located in the Java package * executionConditionPackageName. The corresponding * ExecutionCondition receives in its constructor all the private class * variables of the output class, in the same order as they are declared in * the output class. In a similar way, * {@link #getExecutionConditionClass(ParsedMethod)} can be used to * construct a String expression for condition. *

* The first argument of the constructor of the output class is the guard. * The rest of them are values for each private class variable of the output * class, in the same order as they are declared. * * @param condition * the condition whose representation as a ModelCondition is * going to be created. * @param executionConditionPackageName * the name of the package that contains the ExecutionCondition * associated to condition. It must not be empty, * and it has to be of the form level1.level2. ... * .levelN. * @return a String representation of condition as a * ModelCondition class. */ public String getModelConditionClass(ParsedMethod condition, String executionConditionPackageName) { String result = new String(); /* First element is the type, and the second element is the value. */ List> params = new LinkedList>(); for (ParsedActionParameter parameter : condition.getParameters()) { params.add(new Pair(Util.fromMMPMParameterType(parameter.getType()), parameter.getName())); } result += "/** ModelCondition class created from MMPM condition " + condition.getName() + ". */"; /* Class header. */ result += getModelConditionClassHeader(condition) + "\n"; /* Class variables. */ result += CommonCodeGenerationUtilities.getClassVariables(params) + "\n\n"; /* Constructor. */ result += CommonCodeGenerationUtilities.getModelConstructor(condition.getName(), params) + "\n\n"; /* "createExecutor()" method. */ result += CommonCodeGenerationUtilities.getCreateExecutorMethod(condition.getName(), executionConditionPackageName, params) + "\n"; result += "}"; return result; } /** * This method is used for creating a String representation of the * definition of a Java class for condition. *

* The output class extends {@link ExecutionCondition}, and it defines how * condition actually works. *

* The generated class contains two private fields for each parameter of * condition. Given a parameter with name pName, * two class variables are created in the output class: *

    *
  • pName. The type of this variable will be a Java type * compatible with that of the MMPM definition of pName in * condition (according to * {@link Util#fromMMPMParameterType(gatech.mmpm.ActionParameterType)}). * This variable represents the value of the parameter in case a value has * been specified when constructing an instance of the output class. *
  • pNameLoc. This is a String representing where the * variable pName can be located in the context. In case a * value is not specified for the variable, the task's context must be * searched for a variable whose name is pNameLoc. *
*

* For all of condition's parameters, a getter method is * constructed. Given a parameter with name pName, a getter * named getPName() is constructed. If at construction time a * value was specified for pName, then getPName() * returns such a value. Otherwise, getPName() will search for * a variable of name pNameLoc in the context, and will return * its value. *

* This method also constructs an empty skeleton for all abstract methods of * ExecutionCondition. The getter methods can be used in the implementation * of those abstract methods in order to retrieve the condition's * parameters. *

* The first argument of the constructor of the output class is its * corresponding ModelContion. The second one is its corresponding * BTExecutor. The rest of them are values for each private class variable * of the output class, in the same order as they are declared. * * @param condition * the condition whose representation as an ExecutionCondition is * going to be created. * @return a String representation of action as an * ExecutionCondition class. */ public String getExecutionConditionClass(ParsedMethod condition, String modelConditionPackageName) { String result = new String(); /* First element is the type, and the second element is the value. */ List> params = new LinkedList>(); for (ParsedActionParameter parameter : condition.getParameters()) { params.add(new Pair(Util.fromMMPMParameterType(parameter.getType()), parameter.getName())); } result += "/** ExecutionCondition class created from MMPM condition " + condition.getName() + ". */"; /* Class header. */ result += getExecutionConditionClassHeader(condition) + "\n"; /* Class variables. */ result += CommonCodeGenerationUtilities.getClassVariables(params) + "\n\n"; /* Constructor. */ result += CommonCodeGenerationUtilities.getExecutionConstructor(condition.getName(), modelConditionPackageName, params) + "\n\n"; /* Getters. */ result += CommonCodeGenerationUtilities.getGetters(params) + "\n\n"; /* Abstract methods. */ result += CommonCodeGenerationUtilities.getAbstractMethods(); result += "}"; return result; } private String getModelConditionClassHeader(ParsedMethod condition) { return "public class " + condition.getName() + " extends " + ModelCondition.class.getCanonicalName() + "{"; } private String getExecutionConditionClassHeader(ParsedMethod condition) { return "public class " + condition.getName() + " extends " + ExecutionCondition.class.getCanonicalName() + "{"; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/modelbtgenerator/ModelBTGenerationException.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.modelbtgenerator; /** * Exception that is thrown when there is any error when generating an * expression for a model BT. * * @author Ricardo Juan Palma Durán * */ public class ModelBTGenerationException extends Exception { private static final long serialVersionUID = 1L; public ModelBTGenerationException(String message) { super(message); } public ModelBTGenerationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/modelbtgenerator/ModelBTGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.modelbtgenerator; import gatech.mmpm.ActionParameterType; import gatech.mmpm.tools.parseddomain.ParsedAction; import gatech.mmpm.tools.parseddomain.ParsedActionParameter; import gatech.mmpm.tools.parseddomain.ParsedMethod; import java.io.FileInputStream; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import jbt.execution.core.ExecutionTask.Status; import jbt.model.core.ModelTask; import jbt.model.core.ModelTask.Position; import jbt.model.task.composite.ModelDynamicPriorityList; import jbt.model.task.composite.ModelParallel; import jbt.model.task.composite.ModelRandomSelector; import jbt.model.task.composite.ModelRandomSequence; import jbt.model.task.composite.ModelSelector; import jbt.model.task.composite.ModelSequence; import jbt.model.task.composite.ModelParallel.ParallelPolicy; import jbt.model.task.composite.ModelStaticPriorityList; import jbt.model.task.decorator.ModelHierarchicalContextManager; import jbt.model.task.decorator.ModelInterrupter; import jbt.model.task.decorator.ModelInverter; import jbt.model.task.decorator.ModelLimit; import jbt.model.task.decorator.ModelRepeat; import jbt.model.task.decorator.ModelSafeContextManager; import jbt.model.task.decorator.ModelSafeOutputContextManager; import jbt.model.task.decorator.ModelSucceeder; import jbt.model.task.decorator.ModelUntilFail; import jbt.model.task.leaf.ModelFailure; import jbt.model.task.leaf.ModelPerformInterruption; import jbt.model.task.leaf.ModelSubtreeLookup; import jbt.model.task.leaf.ModelSuccess; import jbt.model.task.leaf.ModelVariableRenamer; import jbt.model.task.leaf.ModelWait; import jbt.model.task.leaf.action.ModelAction; import jbt.model.task.leaf.condition.ModelCondition; import jbt.tools.btlibrarygenerator.lowlevelgenerator.ActionsGenerator; import jbt.tools.btlibrarygenerator.lowlevelgenerator.ConditionsGenerator; import jbt.tools.btlibrarygenerator.util.Util; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; /** * This class is used for generating Java expressions for behaviour trees specified in an XML * format. *

* The XML format is that of BT Editor. The expression that is generated from the XML * behaviour tree is just a composition of {@link ModelTask} objects that represent the behaviour * tree in the XML file. An example of such an expression is: * *

 * new ModelSequence(null, new ModelSelector(null, new Condition1ModelCondition(null),
 * 		new Action1ModelAction(null)), new ModelParallel(null, ParallelPolicy.SEQUENCE_POLICY,
 * 		new Action2ModelAction(null), new Action3ModelAction(null)))
 * 
* * Where Condition1ModelCondition, Action1ModelAction, * Action2ModelAction and Action1Mode3Action are user-defined conditions * and actions. *

* The expression also assigns each ModelPerformInterruption task its corresponding ModelInterrupter * task. Thus, the expression that is obtained is a fully functional behaviour tree (ModelTask). * * @see ActionsGenerator * @see ConditionsGenerator * * @author Ricardo Juan Palma Durán * */ public class ModelBTGenerator { /** Representation of the Tree tag of the XML file. */ private static String TAG_TREE = "Tree"; /** Representation of the Node tag of the XML file. */ private static String TAG_NODE = "Node"; /** Representation of the Children tag of the XML file. */ private static String TAG_CHILDREN = "Children"; /** Representation of the Parameters tag of the XML file. */ private static String TAG_PARAMETERS = "Parameters"; /** Representation of the Parameter tag of the XML file. */ private static String TAG_PARAMETER = "Parameter"; /** Representation of the Guard tag of the XML file. */ private static String TAG_GUARD = "Guard"; /** Representation of the "type" attribute of the XML file. */ private static String ATTR_TYPE = "type"; /** Representation of the "name" attribute of the XML file. */ private static String ATTR_NAME = "name"; /** Representation of the "runs" attribute of the XML file. */ private static String ATTR_RUNS = "runs"; /** Representation of the "duration" attribute of the XML file. */ private static String ATTR_DURATION = "duration"; /** Representation of the "policy" attribute of the XML file. */ private static String ATTR_POLICY = "policy"; /** Representation of the "from context" attribute of the XML file. */ private static String ATTR_FROM_CONTEXT = "fromcontext"; /** Representation of the "id" attribute of the XML file. */ private static String ATTR_ID = "id"; /** Representation of the "listOfVariables" attribute of the XML file. */ private static String ATTR_LIST_OF_VARIABLES = "listOfVariables"; /** Representation of the Root value of the XML file. */ private static String VAL_ROOT = "Root"; /** Representation of the Selector value of the XML file. */ private static String VAL_SELECTOR = "Selector"; /** Representation of the Sequence value of the XML file. */ private static String VAL_SEQUENCE = "Sequence"; /** Representation of the Parallel value of the XML file. */ private static String VAL_PARALLEL = "Parallel"; /** Representation of the Action value of the XML file. */ private static String VAL_ACTION = "Action"; /** Representation of the Condition value of the XML file. */ private static String VAL_CONDITION = "Condition"; /** Representation of the Random sequence value of the XML file. */ private static String VAL_RANDOM_SEQUENCE = "RandomSequence"; /** Representation of the Random selector value of the XML file. */ private static String VAL_RANDOM_SELECTOR = "RandomSelector"; /** Representation of the Interrupter value of the XML file. */ private static String VAL_INTERRUPTER = "Interrupter"; /** Representation of the Inverter value of the XML file. */ private static String VAL_INVERTER = "Inverter"; /** Representation of the Limit value of the XML file. */ private static String VAL_LIMIT = "Limit"; /** Representation of the Perform interruption value of the XML file. */ private static String VAL_PERFORM_INTERRUPTION = "PerformInterruption"; /** Representation of the Repeat value of the XML file. */ private static String VAL_REPEAT = "Repeat"; /** Representation of the Until fail value of the XML file. */ private static String VAL_UNTIL_FAIL = "UntilFail"; /** Representation of the Wait value of the XML file. */ private static String VAL_WAIT = "Wait"; /** Representation of the Subtree lookup value of the XML file. */ private static String VAL_SUBTREE_LOOKUP = "SubtreeLookup"; /** Representation of the Dynamic priority list value of the XML file. */ private static String VAL_DYNAMIC_PRIORITY_LIST = "DynamicPriorityList"; /** Representation of the Static priority list value of the XML file. */ private static String VAL_STATIC_PRIORITY_LIST = "StaticPriorityList"; /** * Representation of the Hierarchical context manager value of the XML file. */ private static String VAL_HIERARCHICAL_CONTEXT_MANAGER = "HierarchicalContextManager"; /** Representation of the Safe context manager value of the XML file. */ private static String VAL_SAFE_CONTEXT_MANAGER = "SafeContextManager"; /** Representation of the Safe output context manager value of the XML file. */ private static String VAL_SAFE_OUTPUT_CONTEXT_MANAGER = "SafeOutputContextManager"; /** Representation of the Variable renamer value of the XML file. */ private static String VAL_VARIABLE_RENAMER = "VariableRenamer"; /** Representation of the Success value of the XML file. */ private static String VAL_SUCCESS = "Success"; /** Representation of the Succeeder value of the XML file. */ private static String VAL_SUCCEEDER = "Succeeder"; /** Representation of the Success value of the XML file. */ private static String VAL_FAILURE = "Failure"; /** Representation of the Sequence parallel policy value of the XML file. */ private static String VAL_SEQUENCE_POLICY = "sequence"; /** Representation of the Selector parallel policy value of the XML file. */ private static String VAL_SELECTOR_POLICY = "selector"; /** Representation of the Success status value of the XML file. */ private static String VAL_SUCCESS_STATUS = "success"; /** Representation of the Failure status value of the XML file. */ private static String VAL_FAILURE_STATUS = "failure"; /** Representation of the Variable name value of the XML file. */ private static String VAL_VARIABLE_NAME = "variableName"; /** Representation of the New variable name value of the XML file. */ private static String VAL_NEW_VARIABLE_NAME = "newVariableName"; /** * Representation of the Node ID value of the Perform Interruption task of the XML file. */ private static String VAL_NODE_ID = "nodeID"; /** * Representation of the Expected result value of the Perform Interruption task of the XML file. */ private static String VAL_EXPECTED_RESULT = "expectedResult"; /** Name of the java class that represents a selector node of the tree. */ private static String CLASS_SELECTOR = ModelSelector.class.getCanonicalName(); /** Name of the java class that represents a sequence node of the tree. */ private static String CLASS_SEQUENCE = ModelSequence.class.getCanonicalName(); /** Name of the java class that represents a parallel node of the tree. */ private static String CLASS_PARALLEL = ModelParallel.class.getCanonicalName(); /** Name of the java class that represents an action node of the tree. */ private static String CLASS_ACTION = ModelAction.class.getCanonicalName(); /** Name of the java class that represents a condition node of the tree. */ private static String CLASS_CONDITION = ModelCondition.class.getCanonicalName(); /** * Name of the java class that represents a random sequence node of the tree. */ private static String CLASS_RANDOM_SEQUENCE = ModelRandomSequence.class.getCanonicalName(); /** * Name of the java class that represents a random selector node of the tree. */ private static String CLASS_RANDOM_SELECTOR = ModelRandomSelector.class.getCanonicalName(); /** * Name of the java class that represents an interrupter node of the tree. */ private static String CLASS_INTERRUPTER = ModelInterrupter.class.getCanonicalName(); /** Name of the java class that represents an inverter node of the tree. */ private static String CLASS_INVERTER = ModelInverter.class.getCanonicalName(); /** Name of the java class that represents a limit node of the tree. */ private static String CLASS_LIMIT = ModelLimit.class.getCanonicalName(); /** * Name of the java class that represents a perform interruption node of the tree. */ private static String CLASS_PERFORM_INTERRUPTION = ModelPerformInterruption.class .getCanonicalName(); /** Name of the java class that represents a repeat node of the tree. */ private static String CLASS_REPEAT = ModelRepeat.class.getCanonicalName(); /** Name of the java class that represents an until fail node of the tree. */ private static String CLASS_UNTIL_FAIL = ModelUntilFail.class.getCanonicalName(); /** Name of the java class that represents a wait node of the tree. */ private static String CLASS_WAIT = ModelWait.class.getCanonicalName(); /** * Name of the java class that represents a subtree lookup node of the tree. */ private static String CLASS_SUBTREE_LOOKUP = ModelSubtreeLookup.class.getCanonicalName(); /** * Name of the java class that represents a dynamic priority list node of the tree. */ private static String CLASS_DYNAMIC_PRIORITY_LIST = ModelDynamicPriorityList.class .getCanonicalName(); /** * Name of the java class that represents a static priority list node of the tree. */ private static String CLASS_STATIC_PRIORITY_LIST = ModelStaticPriorityList.class .getCanonicalName(); /** * Name of the java class that represents a hierarchical context manager node of the tree. */ private static String CLASS_HIERARCHICAL_CONTEXT_MANAGER = ModelHierarchicalContextManager.class .getCanonicalName(); /** * Name of the java class that represents a safe context manager node of the tree. */ private static String CLASS_SAFE_CONTEXT_MANAGER = ModelSafeContextManager.class .getCanonicalName(); /** * Name of the java class that represents a safe output context manager node of the tree. */ private static String CLASS_SAFE_OUTPUT_CONTEXT_MANAGER = ModelSafeOutputContextManager.class .getCanonicalName(); /** * Name of the java class that represents the parallel node possible policies. */ private static String CLASS_PARALLEL_POLICY = ParallelPolicy.class.getCanonicalName(); /** * Name of the java class that represents the variable renamer task. */ private static String CLASS_VARIABLE_RENAMER = ModelVariableRenamer.class.getCanonicalName(); /** * Name of the java class that represents the success task. */ private static String CLASS_SUCCESS = ModelSuccess.class.getCanonicalName(); /** * Name of the java class that represents the failure task. */ private static String CLASS_FAILURE = ModelFailure.class.getCanonicalName(); /** * Name of the java class that represents the succeeder task. */ private static String CLASS_SUCCEEDER = ModelSucceeder.class.getCanonicalName(); /** * Name of the java class that represents a task status. */ private static String CLASS_STATUS = Status.class.getCanonicalName(); /** * Pattern for verifying list of variables values. This is used, for instance, for checking the * correctness of the list of variables of the SafeOutputContextManager node. */ private static final Pattern pattern = Pattern.compile("(( )*\"[a-zA-Z_0-9\\s]+\"( )*)+"); /** * List that stores which interrupters are associated to which perform interruption nodes. */ private List interruptersMatchings; /** * The root element of the tree in the BT XML file. This is not the Tree tag, but the root Node * tag. */ private Element root; /** * The name of the package that contains the low level actions of the BT whose expression is * going to be built. */ private String actionsPackage; /** * The name of the package that contains the low level conditions of the BT whose expression is * going to be built. */ private String conditionsPackage; /** * The MMPM definition of the low level actions used when building the BT. */ private List actionsDefinition; /** * The MMPM definition of the low level conditions used when building the BT. */ private List conditionsDefinition; /** * The name of the file that contains the tree. */ private String treeFileName; /** * This method creates a String representing the declaration of a BT ( {@link ModelTask} * variable) read from a BT XML file in the format of BT Editor. *

* This method reads the XML file treeFileName and creates a ModelTask variable of * name treeVariableName that represents the BT in the file. *

* In order to generate the expression for the tree, some additional information must be * provided: *

    *
  • actionsPackage is the name of the Java package that contains all the low * level actions that are used in the tree. *
  • conditionsPackage is the name of the Java package that contains all the low * level conditions that are used in the tree. *
  • actionsDefinition is a list containing the MMPM definition of all the low * level actions that are used in the tree. *
  • conditionsDefinition is a list containing the MMPM definition of all the low * level conditions that are used in the tree. *
  • declareTree is a flag that indicates whether the tree returned expression * should also declare the variable treeVariableName or assume that it already * exists. If true, it will be declared. *
* * @param treeVariableName * name of the variable that will store the tree. * @param treeFileName * XML file that contains the BT. * @param actionsPackage * name of the Java package that contains all the low level actions that are used in * the tree. * @param conditionsPackage * name of the Java package that contains all the low level conditions that are used * in the tree. * @param actionsDefinition * list containing the MMPM definition of all the low level actions that are used in * the tree. * @param conditionsDefinition * list containing the MMPM definition of all the low level conditions that are used * in the tree. * @param declareTree * flag that indicates whether the tree returned expression should also declare the * variable treeVariableName or assume that it already exists. If true, * it will be declared. * @return a String representing a expression that assigns to the variable * treeVariableName a ModelTask representing the behaviour tree in * treeFileName, as explained above. * @throws ModelBTGenerationException * if there is any error when creating the expression for the behaviour tree. */ public String getModelBTDeclaration(String treeVariableName, String treeFileName, String actionsPackage, String conditionsPackage, List actionsDefinition, List conditionsDefinition, boolean declareTree) throws ModelBTGenerationException { try { this.treeFileName = treeFileName; FileInputStream file = new FileInputStream(treeFileName); BTFileParseResult result = parseBTFile(file, actionsPackage, conditionsPackage, actionsDefinition, conditionsDefinition); file.close(); return completeModelBTDeclaration(treeVariableName, result, declareTree); } catch (Exception e) { throw new ModelBTGenerationException( "Could not generate an expresion for the behaviour tree: " + e.getMessage(), e); } } /** * This method parses a BT XML file in the format of BT Editor and mainly generates a * "new" expression that is able to build the tree in the file. *

* This function returns a {@link BTFileParseResult} object that contains: *

    *
  • A "new" expression (as a String) that is able to construct the tree specified in the * file. *
  • A list of all the matches between interrupters and perform interruption tasks in the * tree. *
* * It must be noted that the "new" expression is somehow incomplete, since interrupters are not * assigned to perform interruptions. That is why the list of match is given. By using it, the * external caller should take care of creating another expression that links each perform * interruption with its corresponding interrupter task. * * @param file * the XML file that contains the behaviour tree. * @param actionsPackage * name of the Java package that contains all the low level actions that are used in * the tree. * @param conditionsPackage * name of the Java package that contains all the low level conditions that are used * in the tree. * @param actionsDefinition * list containing the MMPM definition of all the low level actions that are used in * the tree. * @param conditionsDefinition * list containing the MMPM definition of all the low level conditions that are used * in the tree. * @return a BTFileParseResult as described above. */ private BTFileParseResult parseBTFile(FileInputStream file, String actionsPackage, String conditionsPackage, List actionsDefinition, List conditionsDefinition) { this.actionsDefinition = actionsDefinition; this.conditionsDefinition = conditionsDefinition; this.actionsPackage = actionsPackage; this.conditionsPackage = conditionsPackage; SAXBuilder builder = new SAXBuilder(); StringBuffer result = new StringBuffer(); try { Document doc = builder.build(file); Element documentRoot = doc.getRootElement(); this.root = (Element) ((Element) (((Element) documentRoot.getChildren().get(0)) .getChildren().get(0))).getChildren().get(0); this.interruptersMatchings = new LinkedList(); processElement(new LinkedList(), this.root, result, this.root); return new BTFileParseResult(result.toString(), this.interruptersMatchings); } catch (Exception e) { throw new RuntimeException("Could not generate an expresion for the behaviour tree: " + e.getMessage(), e); } } /** * Given the result of the method * {@link #parseBTFile(FileInputStream, String, String, List, List)}, this method completes the * declaration of the behaviour tree, adding whatever stuff is necessary in order for the tree * to be usable. *

* This method returns a String such as: *

    *
  • The "new" expression generationResult is assigned to a variable of name * treeVariableName. *
  • New statements are added so that the interrupters-perform interruptions matches in * generationResult are carried out. *
* * If declareTree is true, then treeVariableName will be declared (as * a {@link ModelTask}). Otherwise, this method will assume that the variable already exists. * * @param treeVariableName * name of the variable that will store the tree. * @param generationResult * the BTFileParseResult from which the final expression for declaring the tree is * constructed. * @param declareTree * flag that indicates whether the tree returned expression should also declare the * variable treeVariableName or assume that it already exists. If true, * it will be declared. * @return a String as described above. */ private String completeModelBTDeclaration(String treeVariableName, BTFileParseResult generationResult, boolean declareTree) { String result = new String(); result += (declareTree ? ModelTask.class.getCanonicalName() + " " : "") + treeVariableName + " = " + generationResult.getModelBTExpression() + ";"; if (generationResult.getInterruptersMatches().size() != 0) { result += "\n\n"; for (InterrupterMatch match : generationResult.getInterruptersMatches()) { result += getInterrupterExpression(treeVariableName, match) + ";"; } } return result; } /** * Given an InterrupterMatch, this method creates a expression that assigns the perform * interrupter its corresponding interrupter. treeVariableName is the name of the * ModelTask variable that stores the tree. */ private String getInterrupterExpression(String treeVariableName, InterrupterMatch match) { /* First, get a "new" expression for the position of the interrupter. */ String interrupterPositionDeclaration = getPositionNewExpression(match .getInterrupterPosition()); /* * Then, get a "new" expression for the position of the perform interruption. */ String performInterruptionPositionDeclaration = getPositionNewExpression(match .getPerformInterruptionPosition()); /* * Expression that points to the subtree where the match between interrupter and perform * interruption takes place. */ String destinationTreeExpression = treeVariableName; for (Position pathToTree : match.getPathToTree()) { destinationTreeExpression += ".findNode(" + getPositionNewExpression(pathToTree) + ").getGuard()"; } /* Finally create the expression that sets the match. */ String result = "((" + ModelPerformInterruption.class.getCanonicalName() + ")" + destinationTreeExpression + ".findNode(" + performInterruptionPositionDeclaration + ")).setInterrupter((" + ModelInterrupter.class.getCanonicalName() + ")" + destinationTreeExpression + ".findNode(" + interrupterPositionDeclaration + "))"; return result; } /** * Returns a "new" expression for a Position object. */ private String getPositionNewExpression(Position pos) { String result = "new " + Position.class.getCanonicalName() + "("; if (pos.getMoves().size() != 0) { for (Integer move : pos.getMoves()) { result += move + ", "; } result = result.substring(0, result.length() - 2); } result += ")"; return result; } /** * This method creates the "new" statement for a BT given its representation in XML format. *

* e is the element that is currently being analyzed, that for which the expression * will be produced (it must be a Node element of the XML file). *

* pathToCurrentTree represents the path to the behaviour tree that is currently * being analyzed. An empty list represents the original behaviour tree. A non-empty list * represents a guard of some node of the tree, or even a guard of a guard of the original tree. * See {@link InterrupterMatch} for more information about this. currentRoot is the * root of the tree that is currently being analyzed. *

* The expression that is returned is complete except for the fact that interrupters are not * assigned to perform interruptions. *

* All the matches among interrupters and perform interruptions are stored in * {@link #interruptersMatchings}, so that they can be used later on to complete the expression * for tree. */ private void processElement(List pathToCurrentTree, Element currentRoot, StringBuffer result, Element e) { if (e.getName().equals(TAG_CHILDREN)) { /* If tag is "children", process all the children. */ List children = e.getChildren(); for (Element child : children) { StringBuffer partialResult = new StringBuffer(); processElement(pathToCurrentTree, currentRoot, partialResult, child); result.append(", " + partialResult); } } else if (e.getName().equals(TAG_NODE)) { /* First of all, process the guard. */ Element guard = e.getChild(TAG_GUARD); String guardExpression; if (guard != null) { StringBuffer guardExpressionBuffer = new StringBuffer(); List nextPath = new LinkedList(pathToCurrentTree); nextPath.add(findNode(currentRoot, e.getAttributeValue(ATTR_ID), new Position())); processElement(nextPath, guard.getChild(TAG_NODE), guardExpressionBuffer, guard.getChild(TAG_NODE)); guardExpression = guardExpressionBuffer.toString(); } else { guardExpression = "null"; } /* Then, construct an expression for the node itself. */ String type = e.getAttributeValue(ATTR_TYPE); if (type != null) { if (type.equals(VAL_SELECTOR)) { result.append("new " + CLASS_SELECTOR + "(" + guardExpression); } else if (type.equals(VAL_SEQUENCE)) { result.append("new " + CLASS_SEQUENCE + "(" + guardExpression); } else if (type.equals(VAL_RANDOM_SEQUENCE)) { result.append("new " + CLASS_RANDOM_SEQUENCE + "(" + guardExpression); } else if (type.equals(VAL_RANDOM_SELECTOR)) { result.append("new " + CLASS_RANDOM_SELECTOR + "(" + guardExpression); } else if (type.equals(VAL_INTERRUPTER)) { result.append("new " + CLASS_INTERRUPTER + "(" + guardExpression); } else if (type.equals(VAL_LIMIT)) { String runs = e.getChild(TAG_PARAMETERS).getChild(TAG_PARAMETER).getText(); result.append("new " + CLASS_LIMIT + "(" + guardExpression + ", " + runs); } else if (type.equals(VAL_INVERTER)) { result.append("new " + CLASS_INVERTER + "(" + guardExpression); } else if (type.equals(VAL_PERFORM_INTERRUPTION)) { /* Note that the interrupter is set to null. */ List parameters = e.getChild(TAG_PARAMETERS) .getChildren(TAG_PARAMETER); /* First, retrieve the expected result parameter. */ Element desiredResultParameter = null; for (Element currentParameter : parameters) { if (currentParameter.getAttributeValue(ATTR_NAME).equals( VAL_EXPECTED_RESULT)) { desiredResultParameter = currentParameter; break; } } Status desiredResult = statusFromString(desiredResultParameter.getText()); result.append("new " + CLASS_PERFORM_INTERRUPTION + "(" + guardExpression + ", null, " + CLASS_STATUS + "." + desiredResult.toString()); /* * Now we have to retrieve the ID of the interrupter that this perform * interruption will interrupt. */ Element nodeIDParameter = null; for (Element currentParameter : parameters) { if (currentParameter.getAttributeValue(ATTR_NAME).equals(VAL_NODE_ID)) { nodeIDParameter = currentParameter; break; } } String nodeID = nodeIDParameter.getText(); /* * Now create the InterrupterMatch that represents the match between the * interrupter and the perform interruption. */ Position interrupterPosition = findNode(currentRoot, nodeID, new Position()); if (interrupterPosition == null) { throw new RuntimeException("Could not find node with ID \"" + nodeID + "\" in the behaviour tree."); } Position performInterruptionPosition = findNode(currentRoot, e.getAttributeValue(ATTR_ID), new Position()); this.interruptersMatchings.add(new InterrupterMatch(interrupterPosition, performInterruptionPosition, pathToCurrentTree)); } else if (type.equals(VAL_REPEAT)) { result.append("new " + CLASS_REPEAT + "(" + guardExpression); } else if (type.equals(VAL_UNTIL_FAIL)) { result.append("new " + CLASS_UNTIL_FAIL + "(" + guardExpression); } else if (type.equals(VAL_WAIT)) { String duration = e.getChild(TAG_PARAMETERS).getChild(TAG_PARAMETER).getText(); result.append("new " + CLASS_WAIT + "(" + guardExpression + ", " + duration); } else if (type.equals(VAL_PARALLEL)) { ParallelPolicy policy = parallelPolicyFromString(e.getChild(TAG_PARAMETERS) .getChild(TAG_PARAMETER).getText()); result.append("new " + CLASS_PARALLEL + "(" + guardExpression + "," + CLASS_PARALLEL_POLICY + "." + policy.toString()); } else if (type.equals(VAL_SUBTREE_LOOKUP)) { String subtreeName = e.getChild(TAG_PARAMETERS).getChild(TAG_PARAMETER) .getText(); result.append("new " + CLASS_SUBTREE_LOOKUP + "(" + guardExpression + "," + "\"" + subtreeName + "\""); } else if (type.equals(VAL_DYNAMIC_PRIORITY_LIST)) { result.append("new " + CLASS_DYNAMIC_PRIORITY_LIST + "(" + guardExpression); } else if (type.equals(VAL_STATIC_PRIORITY_LIST)) { result.append("new " + CLASS_STATIC_PRIORITY_LIST + "(" + guardExpression); } else if (type.equals(VAL_HIERARCHICAL_CONTEXT_MANAGER)) { result.append("new " + CLASS_HIERARCHICAL_CONTEXT_MANAGER + "(" + guardExpression); } else if (type.equals(VAL_SAFE_CONTEXT_MANAGER)) { result.append("new " + CLASS_SAFE_CONTEXT_MANAGER + "(" + guardExpression); } else if (type.equals(VAL_SAFE_OUTPUT_CONTEXT_MANAGER)) { result.append("new " + CLASS_SAFE_OUTPUT_CONTEXT_MANAGER + "(" + guardExpression + ", " + getSafeOutputContextOutputListOfVariables(e)); } else if (type.equals(VAL_SUCCESS)) { result.append("new " + CLASS_SUCCESS + "(" + guardExpression); } else if (type.equals(VAL_FAILURE)) { result.append("new " + CLASS_FAILURE + "(" + guardExpression); } else if (type.equals(VAL_SUCCEEDER)) { result.append("new " + CLASS_SUCCEEDER + "(" + guardExpression); } else if (type.equals(VAL_VARIABLE_RENAMER)) { List parameters = e.getChild(TAG_PARAMETERS).getChildren(); Element param1 = (Element) parameters.get(0); Element param2 = (Element) parameters.get(1); String variableName = new String(); String newVariableName = new String(); if (param1.getAttributeValue(ATTR_NAME).equals(VAL_VARIABLE_NAME)) { variableName = param1.getText(); } else { newVariableName = param1.getText(); } if (param2.getAttributeValue(ATTR_NAME).equals(VAL_VARIABLE_NAME)) { variableName = param2.getText(); } else { newVariableName = param2.getText(); } result.append("new " + CLASS_VARIABLE_RENAMER + "(" + guardExpression + ", \"" + variableName + "\", \"" + newVariableName + "\""); } else if (type.equals(VAL_ACTION)) { String actionName = e.getAttributeValue(ATTR_NAME); ParsedAction mmpmAction = getAction(actionName); if (mmpmAction == null) { throw new RuntimeException("The tree read from " + this.treeFileName + " makes use of an action -" + actionName + "- that could not be found in the domain files"); } String parameters = getActionStaticParameters(e, getAction(actionName)); result.append("new " + actionsPackage + "." + actionName + "(" + guardExpression + (parameters == null ? "" : ", " + parameters)); } else if (type.equals(VAL_CONDITION)) { String conditionName = e.getAttributeValue(ATTR_NAME); ParsedMethod mmpmCondition = getCondition(conditionName); if (mmpmCondition == null) { throw new RuntimeException("The tree read from " + this.treeFileName + " makes use of a condition -" + conditionName + "- that could not be found in the domain files"); } String parameters = getConditionStaticParameters(e, mmpmCondition); result.append("new " + conditionsPackage + "." + e.getAttributeValue(ATTR_NAME) + "(" + guardExpression + (parameters == null ? "" : ", " + parameters)); } List children = e.getChildren(); if (children.size() != 0) { Element childrenNode = null; for (int i = 0; i < children.size(); i++) { Element currentChild = children.get(i); if (currentChild.getName().equals(TAG_CHILDREN)) { childrenNode = currentChild; break; } } if (childrenNode != null) { StringBuffer partialResult = new StringBuffer(); processElement(pathToCurrentTree, currentRoot, partialResult, childrenNode); result.append(partialResult); } } result.append(")"); } } } /** * Given the String representation of a parallel task policy in the XML file, this method * returns the corresponding {@link ParallelPolicy} object. * * @param value * the string representation of the parallel policy. * @return the ParallelPolicy associated to value. */ private ParallelPolicy parallelPolicyFromString(String value) { if (value.equalsIgnoreCase(VAL_SEQUENCE_POLICY)) { return ParallelPolicy.SEQUENCE_POLICY; } if (value.equalsIgnoreCase(VAL_SELECTOR_POLICY)) { return ParallelPolicy.SELECTOR_POLICY; } throw new RuntimeException("Invalid string for parallel policy: " + value); } /** * Given the String representation of a task status in the XML file, this method returns the * corresponding {@link Status} object. * * @param value * the string representation of the task status. * @return the Status associated to value. */ private Status statusFromString(String value) { if (value.equalsIgnoreCase(VAL_SUCCESS_STATUS)) { return Status.SUCCESS; } if (value.equalsIgnoreCase(VAL_FAILURE_STATUS)) { return Status.FAILURE; } throw new RuntimeException("Invalid string for task status: " + value); } /** * e is an Element representing a node ({@link #TAG_NODE}) of type action ( * {@link #VAL_ACTION}) in the BT XML file. actionDefinition represents the MMPM * definition of such action (MMPM action). This method returns a String representing the list * of values for the parameters that the corresponding ModelAction object receives in its * constructor. *

* For each parameter in the XML file, two values are created: *

    *
  • The value itself: if an actual value is specified in the XML file (that is, the value of * the parameter is not going to be read from the context), an object containing that value is * created. Otherwise, it is null. *
  • The context location: if an actual value is not specified (that is, the value is going to * be read from the context), then a String containing the location in the context is created. * Otherwise, it is null. *
* * The parameters are separated by commas. If the element does not have any parameters, null is * returned. * * @param e * the XML node element representing the action. * @param actionDefinition * the MMPM definition of the action. * @return a String as described above. */ private String getActionStaticParameters(Element e, ParsedAction actionDefinition) { String result = null; Element parametersElement = e.getChild(TAG_PARAMETERS); if (parametersElement != null) { List parameters = parametersElement.getChildren(); List parametersDefinition = actionDefinition.getParameters(); if (parameters.size() != parametersDefinition.size()) { throw new RuntimeException( "The number of parameters of the action " + actionDefinition.getName() + " defined in the MMPM domain file does not match that of the action in the XML file"); } if (parameters.size() != 0) { Iterator parametersIterator = parameters.iterator(); Iterator parametersDefinitionIterator = parametersDefinition .iterator(); result = new String(); while (parametersIterator.hasNext()) { Element currentParam = parametersIterator.next(); ParsedActionParameter currentParamDefinition = parametersDefinitionIterator .next(); if (currentParam.getAttributeValue(ATTR_FROM_CONTEXT).equals("true")) { result += "null, " + "\"" + currentParam.getText() + "\"" + ", "; } else { /* * When the parameter does not come from the context, an appropriate object * must be constructed. */ result += getNewExpression(currentParamDefinition.getType(), currentParam.getText()) + ", null, "; } } result = result.substring(0, result.length() - 2); } } return result; } /** * e is an Element representing a node ({@link #TAG_NODE}) of type condition ( * {@link #VAL_CONDITION}) in the BT XML file. conditionDefinition represents the * MMPM definition of such condition (MMPM sensor). This method returns a String representing * the list of values for the parameters that the corresponding ModelCondition object receives * in its constructor. *

* For each parameter in the XML file, two values are created: *

    *
  • The value itself: if an actual value is specified in the XML file (that is, the value of * the parameter is not going to be read from the context), an object containing that value is * created. Otherwise, it is null. *
  • The context location: if an actual value is not specified (that is, the value is going to * be read from the context), then a String containing the location in the context is created. * Otherwise, it is null. *
* * The parameters are separated by commas. If the element does not have any parameters, null is * returned. * * @param e * the XML node element representing the condition. * @param conditionDefinition * the MMPM definition of the condition. * @return a String as described above. */ private String getConditionStaticParameters(Element e, ParsedMethod conditionDefinition) { String result = null; Element parametersElement = e.getChild(TAG_PARAMETERS); if (parametersElement != null) { List parameters = parametersElement.getChildren(); List parametersDefinition = conditionDefinition.getParameters(); if (parameters.size() != parametersDefinition.size()) { throw new RuntimeException( "The number of parameters of the sensor " + conditionDefinition.getName() + " defined in the MMPM domain file does not match that of the condition in the XML file"); } if (parameters.size() != 0) { Iterator parametersIterator = parameters.iterator(); Iterator parametersDefinitionIterator = parametersDefinition .iterator(); result = new String(); while (parametersIterator.hasNext()) { Element currentParam = parametersIterator.next(); ParsedActionParameter currentParamDefinition = parametersDefinitionIterator .next(); if (currentParam.getAttributeValue(ATTR_FROM_CONTEXT).equals("true")) { /* If the parameter comes from the context... */ result += "null, " + "\"" + currentParam.getText() + "\"" + ", "; } else { /* * When the parameter does not come from the context, an appropriate object * must be constructed. */ result += getNewExpression(currentParamDefinition.getType(), currentParam.getText()) + ", null, "; } } result = result.substring(0, result.length() - 2); } } return result; } /** * Given a MMPM parameter type and a String read from the BT XML file, this function returns a * String representing a "new" statement that creates an object of a compatible type with the * specified value. Keep in mind that MMPM types are converted according to * {@link Util#fromMMPMParameterType(ActionParameterType)}. Note that * {@link ActionParameterType#ENTITY_ID} is not allowed, since such MMPM type is treated as a * Java Object that must neccesarily be retrieved from the context. * * @param type * the MMPM type of the expression. * @param value * the value of the expression. * @return the "new" expression that constructs a variable of a type compatible with * type and value value. */ private String getNewExpression(ActionParameterType type, String value) { Class actualClass = Util.fromMMPMParameterType(type); if (actualClass.equals(Integer.class)) { return "(int)" + value; } else if (actualClass.equals(Float.class)) { return "(float)" + value; } else if (actualClass.equals(Boolean.class)) { return "(boolean)" + value; } else if (actualClass.equals(String.class)) { return "\"" + value + "\""; } else if (actualClass.equals(float[].class)) { String[] numbers = value.split("( )+"); String result = "new " + float[].class.getCanonicalName() + "{"; for (String currentNumber : numbers) { result += currentNumber + ", "; } result = result.substring(0, result.length() - 2); result += "}"; return result; } throw new RuntimeException("Unexpected action parameter type: " + type.name()); } /** * Given a Node element ({@link #TAG_NODE}) nodeElement of the BT in the XML file, * this method returns the position of a descendant Node element (also including * nodeElement) whose identifier ( {@link #ATTR_ID}) is nodeID. The * position is computed from currentPosition, which is the position of * nodeElement in the tree. *

* If such a node is not found, null is returned. * * @param nodeElement * the element from which the search starts. * @param nodeID * the identifier of the node being searched for. * @param currentPosition * the position of nodeElement in the tree. * @return the node with identifier nodeID, or null in case it is not found. */ private Position findNode(Element nodeElement, String nodeID, Position currentPosition) { if (nodeElement.getAttributeValue(ATTR_ID).equals(nodeID)) { return currentPosition; } Element childrenElement = nodeElement.getChild(TAG_CHILDREN); if (childrenElement != null) { List children = childrenElement.getChildren(); for (int i = 0; i < children.size(); i++) { Position currentChildPosition = new Position(currentPosition); currentChildPosition.addMove(i); Position found = findNode(children.get(i), nodeID, currentChildPosition); if (found != null) { return found; } } } return null; } /** * An InterrupterMatch just represents a match between an interrupter and a perform interruption * task in a BT. This class mainly stores two positions, the positions of the tasks that match. *

* When a match has to be performed, we must take into account that the match itself may be in * the tasks of a node's guard. This match may even be between tasks of a guard of a node's * guard, and so on. *

* We may therefore think that matches happen between tasks in trees, and that each guard is a * tree independent from other guards as well as the original tree. Therefore, the * InterrupterMatch also includes some information to know what is the tree (guard, or guard * within guard, and so on) where the match has to be applied. This is just a list of Position * objects. If the list if empty, the match has to be done in the original tree. If not, each * Position object represents a path from the root of the last tree to the next guard. For * example, if the list contains one Position object, the match is between two tasks in the tree * whose root is the guard of the node pointed by such position. If the list contains two * Position objects, then the same process is applied twice: the first Position object points to * a guard object (that of the node pointed by the position). The second Position object is used * to get the next guard, starting from that obtained from the first Position. The match is * between two tasks of the tree whose root is this last guard. * * @author Ricardo Juan Palma Durán * */ public static class InterrupterMatch { /** The position of the interrupter. */ private Position interrupterPosition; /** The position of the perform interruption. */ private Position performInterruptionPosition; /** The path to the subtree where the match takes place. */ private List pathToTree; /** * Creates an InterrupterMatch with two positions. * * @param interrupterPosition * the position of the interrupter. * @param performInterruptionPosition * the position of the perform interruption. * @param pathToTree * the path to the tree where the match takes place, as described in * {@link InterrupterMatch}. */ public InterrupterMatch(Position interrupterPosition, Position performInterruptionPosition, List pathToTree) { if (interrupterPosition == null) { throw new IllegalArgumentException(); } if (performInterruptionPosition == null) { throw new IllegalArgumentException(); } if (pathToTree == null) { throw new IllegalArgumentException(); } this.interrupterPosition = interrupterPosition; this.performInterruptionPosition = performInterruptionPosition; this.pathToTree = pathToTree; } /** * Returns the position of the interrupter. */ public Position getInterrupterPosition() { return this.interrupterPosition; } /** * Returns the position of the perform interruption. */ public Position getPerformInterruptionPosition() { return this.performInterruptionPosition; } /** * Returns the path to the tree where the match takes place. Returns an empty list if the * match is done between two tasks of the original behaviour tree. */ public List getPathToTree() { return this.pathToTree; } } /** * Class that stores the result of the method * {@link ModelBTGenerator#parseBTFile(FileInputStream, String, String, List, List)} . *

* When a BT XML file is parsed, two things are generated: *

    *
  • An "new" expression for the tree, not even ended in ";". This expression, however, is * incomplete, since interrupters are not associated with any perform interruption. *
  • A match between interrupters and perform interruption tasks in the tree. This match is * represented as a list containing {@link InterrupterMatch} object, one for each match. *
* * After parsing the BT XML file, the result (BTFileParseResult) should be further processed in * order to complete the declaration expression of the behaviour tree. One of the things that * must be done is, for instance, do the actual match between interrupters and perform * interruption tasks of the created tree. * * @author Ricardo Juan Palma Durán * */ private static class BTFileParseResult { /** The expression of the BT, with no ";" at the end. */ private String modelBTExpression; /** * The list of match between interrupters and perform interruptions. */ private List interruptersMatches; /** * Builds a {@link BTFileParseResult}. * * @param modelBTExpression * the expression of the BT, with no ";" at the end. * @param interruptersMatchings * the list of match between interrupters and perform interruptions that should * be done in the tree represented by modelBTExpression. */ public BTFileParseResult(String modelBTExpression, List interruptersMatchings) { if (modelBTExpression == null) { throw new IllegalArgumentException(); } if (interruptersMatchings == null) { throw new IllegalArgumentException(); } this.modelBTExpression = modelBTExpression; this.interruptersMatches = interruptersMatchings; } /** * Returns the expression of the BT. */ public String getModelBTExpression() { return this.modelBTExpression; } /** * Returns the matches that must be performed between interrupters and performs * interruptions in the tree. */ public List getInterruptersMatches() { return this.interruptersMatches; } } /** * Returns the ParsedAction in {@link #actionsDefinition} whose name is name, or * null if not found. */ private ParsedAction getAction(String name) { for (ParsedAction a : this.actionsDefinition) if (a.getName().equals(name)) return a; return null; } /** * Returns the ParsedMethod in {@link #conditionsDefinition} whose name is name, or * null if not found. */ private ParsedMethod getCondition(String name) { for (ParsedMethod m : this.conditionsDefinition) if (m.getName().equals(name)) return m; return null; } /** * Given a SafeOutputContextManager node of the XML file, this method returns a String * representing the declaration of a List containing the names of its output variables. */ private String getSafeOutputContextOutputListOfVariables(Element e) { /* First, check if the list of variables is syntactically correct. */ String listOfVariablesString = e.getChild(TAG_PARAMETERS).getChild(TAG_PARAMETER).getText(); Matcher matcher = pattern.matcher(listOfVariablesString); if (!matcher.matches()) { throw new RuntimeException("List of variables syntactically incorrect: " + listOfVariablesString); } /* Now construct a List with all output variables. */ List listOfVariables = new LinkedList(); int startQuotations = listOfVariablesString.indexOf('"'); while (true) { int endQuotations = listOfVariablesString.indexOf('"', startQuotations + 1); listOfVariables .add(listOfVariablesString.substring(startQuotations + 1, endQuotations)); startQuotations = listOfVariablesString.indexOf('"', endQuotations + 1); if (startQuotations == -1) { break; } } /* * Now construct an expression that creates the list of the output variable of the * SafeOuputContext. */ String result = Arrays.class.getCanonicalName() + ".asList("; String arrayExpression = "new " + String[].class.getCanonicalName() + "{"; for (String variable : listOfVariables) { arrayExpression += "\"" + variable + "\", "; } arrayExpression = arrayExpression.substring(0, arrayExpression.length() - 2) + "}"; result += arrayExpression + ")"; return result; } } ================================================ FILE: JBTCore/src/jbt/tools/btlibrarygenerator/util/Util.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.btlibrarygenerator.util; import java.io.File; import java.util.Map; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.text.edits.TextEdit; import gatech.mmpm.ActionParameterType; /** * General utilities used in the creation of BT libraries. * * @author Ricardo Juan Palma Durán * */ @SuppressWarnings("unchecked") public class Util { /** * The {@link CodeFormatter} that is used to format source code files. It is * statically initialized. */ static private CodeFormatter codeFormatter; /** * Given a MMPM parameter type, this method returns a Class object * representing such a type. MMPM has some built-in types that are not * present in Java, so a conversion must be performed. The conversion is * performed as follows: * *
	 * if (type == ActionParameterType.BOOLEAN) {
	 * 	return Boolean.class;
	 * } else if (type == ActionParameterType.ENTITY_ID) {
	 * 	return String.class;
	 * } else if (type == ActionParameterType.ENTITY_TYPE) {
	 * 	return String.class;
	 * } else if (type == ActionParameterType.FLOAT) {
	 * 	return Float.class;
	 * } else if (type == ActionParameterType.INTEGER) {
	 * 	return Integer.class;
	 * } else if (type == ActionParameterType.STRING) {
	 * 	return String.class;
	 * } else if (type == ActionParameterType.PLAYER) {
	 * 	return String.class;
	 * } else if (type == ActionParameterType.COORDINATE) {
	 * 	return float[].class;
	 * } else if (type == ActionParameterType.DIRECTION) {
	 * 	return Integer.class;
	 * } else if (type == ActionParameterType.OBJECT) {
	 * 	return Object.class;
	 * }
	 * 
* * If the input type is not recognized, an exception is thrown. * * @param type * the type to be converted. * @return a Class object representing the type type. */ public static Class fromMMPMParameterType(ActionParameterType type) { if (type == ActionParameterType.BOOLEAN) { return Boolean.class; } else if (type == ActionParameterType.ENTITY_ID) { return String.class; } else if (type == ActionParameterType.ENTITY_TYPE) { return String.class; } else if (type == ActionParameterType.FLOAT) { return Float.class; } else if (type == ActionParameterType.INTEGER) { return Integer.class; } else if (type == ActionParameterType.STRING) { return String.class; } else if (type == ActionParameterType.PLAYER) { return String.class; } else if (type == ActionParameterType.COORDINATE) { return float[].class; } else if (type == ActionParameterType.DIRECTION) { return Integer.class; } else if (type == ActionParameterType.OBJECT) { return Object.class; } throw new IllegalArgumentException("Unexpected action parameter type"); } /** * If a directory name does not contain the separator character at the very * end, it is added. Otherwise, the same input directory is returned. */ public static String addDirectorySeparator(String directory) { /* If directory does not end in separator, add it. */ if (!directory.endsWith(File.separator)) { return directory + File.separator; } return directory; } /** * Checks if a file name already exists in the file system. If so, returns a * new file name based on fileName, which represents a file * that does not exist in the file system (the name is the same as * fileName except for the fact that is has been appended a * number at its end, before the file extension). */ public static String overwrites(String fileName) { File file = new File(fileName); if (!file.exists()) { return fileName; } else { int counter = 1; while (true) { String alternativeFileName = removeExtension(fileName) + counter++; String extension = getExtension(fileName); if (extension != null) { alternativeFileName += "." + extension; } file = new File(alternativeFileName); if (!file.exists()) { return alternativeFileName; } } } } /** * Removes the extension from a file name. If the file name has no * extension, it is left unchanged. */ public static String removeExtension(String fileName) { String parts[] = fileName.split("\\."); if (parts.length == 0) { return fileName; } else { String result = new String(); for (int i = 0; i < parts.length - 1; i++) { result += parts[i] + "."; } return result.substring(0, result.length() - 1); } } /** * Returns the extension of a file name, or null if it has none. */ public static String getExtension(String fileName) { String parts[] = fileName.split("\\."); if (parts.length == 0) { return null; } else { return parts[parts.length - 1]; } } /** * If a directory name contains the separator character at the very end, it * is removed. Otherwise, the same input directory is returned. */ public static String removeDirectorySeparator(String directory) { /* If directory ends with separator, remove it. */ if (directory.endsWith(File.separator)) { return directory.substring(0, directory.length() - 1); } return directory; } /** * This method formats a source code file (its content is in * sourceCode) according to the Eclipse SDK defaults formatting * settings, and returns the formatted code. *

* Note that the input source code must be syntactically correct according * to the Java 1.7 version. Otherwise, an exception is thrown. * * @param sourceCode * the source code to format. * @return the formatted source code. */ public static String format(String sourceCode) { try { TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, sourceCode, 0, sourceCode.length(), 0, System.getProperty("line.separator")); IDocument document = new Document(sourceCode); edit.apply(document); return document.get(); } catch (Exception e) { throw new RuntimeException("The input source code is not sintactically correct:\n\n" + sourceCode); } } /** * Determines if a file exists. */ public static boolean fileExists(String fileName) { File f = new File(fileName); return f.exists(); } /* Initialization of the code formatter. */ static { /* Take default Eclipse formatting options. */ Map options = DefaultCodeFormatterConstants.getEclipseDefaultSettings(); /* Initialize the compiler settings to be able to format 1.7 code */ options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); /* Change the option to wrap each enum constant on a new line */ options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ENUM_CONSTANTS, DefaultCodeFormatterConstants.createAlignmentValue(true, DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE, DefaultCodeFormatterConstants.INDENT_ON_COLUMN)); options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_LINE_LENGTH, 78); /* Instanciate the default code formatter with the given options */ codeFormatter = ToolFactory.createCodeFormatter(options); } } ================================================ FILE: JBTCore/src/jbt/util/Pair.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.util; import java.io.Serializable; /** * Pair represents a pair of objects * * @author Wikipedia * @param * type of the first element of the pair. * @param * type of the second element of the pair. */ public class Pair implements Serializable { private static final long serialVersionUID = 1L; /** * First element of the pair. */ private T first; /** * Second element of the pair. */ private S second; /** * Constructs a Pair. * * @param f * first element of the pair. * @param s * second element of the pair. */ public Pair(T f, S s) { first = f; second = s; } /** * Returns the first element of the pair. * * @return the first element of the pair. */ public T getFirst() { return first; } /** * Returns the second element of the pair. * * @return the second element of the pair. */ public S getSecond() { return second; } /** * Sets the value of the first element of the pair. * * @param f * value for the first element of the pair. */ public void setFirst(T f) { first = f; } /** * Sets the value of the second element of the pair. * * @param s * value for the second element of the pair. */ public void setSecond(S s) { second = s; } /** * * @see java.lang.Object#toString() */ public String toString() { return "(" + first.toString() + ", " + second.toString() + ")"; } /** * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Pair)) { return false; } return ((Pair) o).getFirst().equals(this.first) && ((Pair) o).getSecond().equals(this.second); } /** * * @see java.lang.Object#hashCode() */ public int hashCode() { return this.first.hashCode() + this.second.hashCode(); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/.classpath ================================================ ================================================ FILE: JBTEditor/jbt.tools.bteditor/.gitignore ================================================ /bin ================================================ FILE: JBTEditor/jbt.tools.bteditor/.project ================================================ jbt.tools.bteditor org.eclipse.jdt.core.javabuilder org.eclipse.pde.ManifestBuilder org.eclipse.pde.SchemaBuilder org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature ================================================ FILE: JBTEditor/jbt.tools.bteditor/.settings/org.eclipse.jdt.core.prefs ================================================ #Thu Jun 10 14:14:01 CEST 2010 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.6 ================================================ FILE: JBTEditor/jbt.tools.bteditor/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: BT Editor Bundle-SymbolicName: jbt.tools.bteditor; singleton:=true Bundle-Version: 0.0.2 Require-Bundle: org.eclipse.core.runtime, org.eclipse.ui, org.eclipse.ui.forms;bundle-version="3.5.0" Bundle-Activator: jbt.tools.bteditor.Activator Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Bundle-ClassPath: ., libs/jdom.jar, libs/mmpm.jar ================================================ FILE: JBTEditor/jbt.tools.bteditor/bteditor.product ================================================ JBT Editor JBT Editor is an RCP application for defining behaviour trees in a standard XML format. (c) Copyright Ricardo Juan Palma Durán 2010. All rights reserved. Credits: Icons: iconspedia (http://www.iconspedia.com/) and icons at Wikimedia Commons (http://commons.wikimedia.org/wiki/Category:Icons). -Xmx1024M -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts ================================================ FILE: JBTEditor/jbt.tools.bteditor/build.properties ================================================ source.. = src/ output.. = bin/ bin.includes = plugin.xml,\ META-INF/,\ libs/jdom.jar,\ .,\ libs/mmpmold.jar,\ plugin_customization.ini,\ icons/,\ files/,\ libs/mmpm.jar source.. = src/ ================================================ FILE: JBTEditor/jbt.tools.bteditor/files/standardNodes.xml ================================================ Sequence Sequence I icons/sequence.png Selector Selector I icons/selector.png Parallel Parallel I icons/parallel.png RandomSelector Random Selector I icons/randomSelector.png RandomSequence Random Sequence I icons/randomSequence.png StaticPriorityList Static Priority List I icons/staticPriorityList.png DynamicPriorityList Dynamic Priority List I icons/dynamicPriorityList.png HierarchicalContextManager Hierarchical Context Manager 1 icons/hierarchicalContextManager.png SafeOutputContextManager Safe Output Context Manager 1 icons/safeOutputContextManager.png SafeContextManager Safe Context Manager 1 icons/safeContextManager.png Interrupter Interrupter 1 icons/interrupter.png Inverter Inverter 1 icons/inverter.png Limit Limit 1 icons/limit.png Repeat Repeat 1 icons/repeat.png UntilFail Until Fail 1 icons/untilFail.png Succeeder Succeeder 1 icons/succeeder.png Wait Wait 0 icons/wait.png SubtreeLookup Subtree Lookup 0 icons/subtreeLookup.png PerformInterruption Perform Interruption 0 icons/performInterruption.png VariableRenamer Variable Renamer 0 icons/variableRenamer.png Success Success 0 icons/success.png Failure Failure 0 icons/failure.png ================================================ FILE: JBTEditor/jbt.tools.bteditor/icons/jbt.xpm ================================================ /* XPM */ static char *jbt[] = { /* columns rows colors chars-per-pixel */ "250 250 256 2", " c #000000", ". c #080B06", "X c #10170B", "o c #0A3B02", "O c #0C2F04", "+ c #313130", "@ c #2F2900", "# c #753804", "$ c #4A3B00", "% c #065706", "& c #094A05", "* c #076805", "= c #077705", "- c #167905", "; c #0D6E0C", ": c #2A740E", "> c #255808", ", c #744807", "< c #666505", "1 c #535806", "2 c #515151", "3 c #6F6F6F", "4 c #626159", "5 c #875707", "6 c #904F0B", "7 c #BA5A01", "8 c #A8570A", "9 c #966505", "0 c #AF6E0B", "q c #C7670B", "w c #D97819", "e c #C97314", "r c #E17E1B", "t c #803E00", "y c #058807", "u c #198706", "i c #199606", "p c #0A9616", "a c #0A8B14", "s c #268907", "d c #269A08", "f c #38980A", "g c #348D11", "h c #16A113", "j c #2BA508", "k c #2DAA09", "l c #25A308", "z c #32AD09", "x c #39A60A", "c c #36B10A", "v c #3BB50B", "b c #3EB90C", "n c #3ABA07", "m c #3AA818", "M c #3C9727", "N c #358D27", "B c #13A925", "V c #1AB32A", "C c #3DB42A", "Z c #2DB427", "A c #459A12", "S c #6F920A", "D c #45A90A", "F c #43BC0C", "G c #48B70A", "H c #54B80D", "J c #4BA61C", "K c #54AC19", "L c #45BD1C", "P c #45B816", "I c #5AB71A", "U c #55AB0B", "Y c #62BA1C", "T c #73AE0C", "R c #429C23", "E c #47A826", "W c #45B32E", "Q c #4AB52B", "! c #4EB92A", "~ c #44B724", "^ c #53BE26", "/ c #51BD29", "( c #55B722", ") c #45AE2E", "_ c #68BA26", "` c #70BC33", "' c #38C81A", "] c #2DC121", "[ c #4BC40D", "{ c #47C10D", "} c #54CB0E", "| c #59CE0F", " . c #55C40E", ".. c #53D60D", "X. c #5CD20E", "o. c #55D908", "O. c #4AC314", "+. c #4CCE1D", "@. c #46C318", "#. c #4ED117", "$. c #51D312", "%. c #5ED410", "&. c #55CB14", "*. c #62D60F", "=. c #65D80E", "-. c #63CD1E", ";. c #64C719", ":. c #61D510", ">. c #65DA11", ",. c #6ADE12", "<. c #66D21E", "1. c #6AD51D", "2. c #6DDA1E", "3. c #6BD414", "4. c #71DD1C", "5. c #74CA0F", "6. c #6EE016", "7. c #6FE118", "8. c #73E21C", "9. c #78E21E", "0. c #49CA23", "q. c #47C728", "w. c #56C126", "e. c #5AC524", "r. c #5ECA23", "t. c #54C128", "y. c #61CD21", "u. c #6CC625", "i. c #73C92B", "p. c #64D221", "a. c #6AD621", "s. c #73DD24", "d. c #77D829", "f. c #79C737", "g. c #7CD634", "h. c #6DC233", "j. c #76E421", "k. c #7AE525", "l. c #7CE42B", "z. c #7DE92B", "x. c #77E329", "c. c #7EE431", "v. c #7EEC33", "b. c #7CF035", "n. c #77BD42", "m. c #7BC543", "M. c #B98A02", "N. c #8EB20A", "B. c #B1B207", "V. c #9AA009", "C. c #D18B09", "Z. c #EA990C", "A. c #F19D0D", "S. c #E58818", "D. c #EC9312", "F. c #D2B307", "G. c #FDAB01", "H. c #F8A605", "J. c #FEB301", "K. c #FFBC00", "L. c #F5B702", "P. c #EEAF07", "I. c #F9B629", "U. c #91CB0F", "Y. c #AFC914", "T. c #84D83A", "R. c #84CD3A", "E. c #81E92D", "W. c #81E62C", "Q. c #82E634", "!. c #84EB33", "~. c #89EC36", "^. c #86EB3A", "/. c #8BED3B", "(. c #88E43B", "). c #90EF3D", "_. c #93E735", "`. c #8BF13D", "'. c #84F139", "]. c #92F03E", "[. c #93E32C", "{. c #ACD72C", "}. c #D5D204", "|. c #FEC400", " X c #FFCB00", ".X c #F7C704", "XX c #FFD300", "oX c #FFDB00", "OX c #F6D501", "+X c #F3D00E", "@X c #E8E800", "#X c #FFE300", "$X c #FFEC00", "%X c #F3E902", "&X c #FFF400", "*X c #FFFE00", "=X c #F7F703", "-X c #FFFF17", ";X c #FED02D", ":X c #FFFF26", ">X c #FFFE39", ",X c #EBEB25", ".} c l y p V ' o.$.#.+.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ w.r.y.2.l.7XtX0X^.j.>.} v l i y h ] ....$.#.+.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ w.r.y.1.x.^.wXtX0X!.9.>.} b k i y p V ' o...$.+.+.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ w.r.y.<.s.c.7XtXtX7Xv.j.>.} F k i y y B ] ..o...$.#.+.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! / w.e.r.<.1.x.^.0XsX0X7Xz.8.>.} F z l i y p B ' o.o...$.#.+.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! / ^ e.r.-.1.s.c.4XwXyX0X/.z.8.>.$.F c l i y y B V { o.o.$.#.#.+.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! ^ e.r.y.<.2.x.^.0XtXtX7X~.k.7.>.} F v l i y y p V ' o.o...$.#.+.+.+.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ / ^ e.e.y.-.1.s.c.4XtXsXeX7X'.k.7.>.} { v k i i y p B V ' o.o...$.#.+.+.0.q.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ / ^ w.e.-.-.1.2.x.^.0XyXtXeX`.v.k.7.>.} { v k l i y y p V ] #.o.o...$.#.#.+.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ / ^ w.e.r.y.<.2.s.c.4X0XsXtX0X`.!.j.6.>.} { F k l i y y p B V ' o.o.o...$.#.+.+.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! ! ^ w.e.r.-.-.1.s.x.(.4XyXsXyX8X~.z.j.6.=.| [ v z l i y y p p V ] ' o.o...$.#.#.+.+.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! / ^ e.r.r.<.1.2.s.Q./.0XyXtXeX7X'.z.j.6.=.} [ F z k i i y y p B V ] o.o.o.....#.#.+.+.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q / / ^ e.e.r.-.<.2.s.x.^.7XyXtXtX0X`.~.z.j.6.%.} [ F c k d i y y p p B ] ' o.o.o...$.$.+.+.+.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q / / ^ e.e.y.-.-.1.2.d.c.4X0XtXtXtX8X`.!.k.7.6.>...[ F c k l i y y y p B V ] ..o.o...$.$.$.$.+.+.0.0.q.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! ^ w.e.e.y.y.<.<.2.s.x.^.4XwXtXsXtX8X`.!.z.8.6.=.} [ { c z l i i y y p B B ] ' ..o.o...%.$.$.+.+.+.+.0.q.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q / / ^ w.e.e.y.y.-.1.2.x.Q.4X0XtXtXtX0X].'.z.k.8.6.%.X.[ F n k k d i y y y p B V ] ' o.o.o.$.$.$.#.#.+.+.0.0.q.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! / ^ e.e.e.y.y.p.1.2.x.x.^.4X0XyXsXtX0X`.!.z.k.8.>.>.X.[ F v z k i i y y y p B V V ' o.o.o.....$.$.$.+.+.+.0.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / ^ w.e.e.r.y.-.1.1.a.d.Q.^.4XyXsXyXwX8X`.'.z.k.7.6.>.} } { v c j l i i y y y p B V ] ' o.o.......$.$.#.+.+.+.0.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW Q Q / / ^ w.e.e.r.-.y.1.1.2.x.l.^.4X0XyXsXyXeX].'.'.z.k.7.6.=.} [ { n z k l i i i y y p B B V ' #.o.o.o.....$.#.$.#.+.+.+.0.q.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / ^ w.e.e.e.y.-.p.1.1.s.x.Q.4X7X0XsXsXyXeX].'.v.z.k.6.6.%.X.[ { n n k l i i y y y p p B V ] ' ..o.o...%.$.$.$.#.#.+.+.+.0.q.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ~ / ^ ^ e.e.r.r.r.<.<.1.2.s.Q.(.4X0XyXhXyXyX0X`.'.!.k.k.6.6.%.X.[ { F z k j d i i y y y p p B V ' ' o.o.o.......#.#.+.+.+.+.0.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ W Q ! ^ ^ ^ e.e.e.r.;.<.<.1.2.s.x.c.^.4XwXyXyXtXeX8X`.'.E.k.8.6.6.%...} { F v z j l i i y y y p B B B ] ' ..o.o.o.......$.$.+.$.+.+.+.0.0.q.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW ! ! ! / ^ w.e.e.e.r.y.-.<.<.2.2.s.c.^.4X0XwXsXtXyX0X8X`.'.z.z.8.7.>.X.X.} { F n z j l i i y y y y p B B V ] ' o.o.o.......$.$.$.+.+.+.+.0.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW Q ! ! ! ^ ^ w.e.e.r.-.-.<.<.1.a.s.c.c.^.4X0XyXsXtXeX0X`.`.!.z.k.8.6.6.*.} } [ F v z k l i i i y y y p B V V V ' ..o.o.o.......$.$.$.$.+.+.+.+.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW Q Q / / / t.t.e.e.r.r.<.-.p.1.1.s.s.c.^.4X7XyXsXsXtXwX0X`.'.!.z.9.8.6.>.*.} } [ F v z k l l i y y y y y p B V V ' ' o.o.o.o.......$.$.+.+.+.+.+.0.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW Q Q / / / t.I w.e.r.r.r.r.<.1.1.2.s.x.Q.^.4X0XyXsXtXtXwX7X`.'.!.z.k.8.6.,.*.X.} { F F v j l l i i y y y y p p B V V ' ' o.o.o.o.....$.$.$.$.$.+.+.0.0.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! ! / / ^ e.e.e.e.r.-.-.-.<.1.2.s.x.c.^.^.7XwXtXsXsXtX0X7X`.'.E.z.k.8.6.>.*.| } [ { n c k l l l i u y y y p p B B V ' ' ' o.o.o.....$.$.$.$.+.$.+.+.+.0.+.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! ( ^ ^ ^ e.e.e.r.r.y.<.<.1.1.2.s.l.Q.^.7XuXyXtXsXtXyX0X7X`.~.E.z.9.8.6.>.X.| } [ F n v z j l i i i y y y p p p B V V ' ' ..o.o.o.o.....$.$.$.+.#.+.+.+.0.0.0.q.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q / / ^ w.w.e.e.r.r.-.-.<.1.1.2.4.x.c.^.4X4XeXyXsXsXwX0X0X`.'.'.z.z.9.8.6.,.o.X.} [ { F c z k l d i i y y y y p p B V V ] ' o.o.o.o.o.o.....$.$.$.#.$.+.+.+.+.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q / / ^ ^ w.w.e.e.r.r.y.-.<.<.1.2.s.s.l.Q.^.4X0XyXsXsXtXwX0X0X`.'.'.z.k.j.8.,.,.>...} } { F v z k j l i i i y y y y p B B V V ] ' o.o.o.o.o.....$.$.$.#.$.+.+.+.+.+.0.0.0.q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW Q Q Q / / ^ w.w.e.e.e.r.r.y.<.<.<.1.2.s.d.c.^.^.4X0XyXtXsXtXwXeX8X`.~.E.z.z.8.8.6.>.%.X.[ [ G F n z k j l i i y y y y y p p B B V ] ' ' ..o.o.o.....$.......$.$.+.+.+.+.0.0.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q / / / ^ ^ w.I e.r.;.y.y.<.<.<.1.2.a.x.c.Q.(.4X0XwXyXhXtXtXeX0X7X`.'.E.z.k.j.7.6.>.=.X.[ } F F v c z j l i i i y y y y p p B V V V ] ' ' o.o.o.o.......} } $.+.$.+.+.+.+.+.0.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! Q ! ! / ^ ^ e.e.e.e.r.r.y.y.<.<.1.1.2.s.x.c.v.^.7X0XyXyXtXtXtXeX8X`.`.!.E.z.k.8.6.6.>.%.X.} [ F F n c z j l l i i y y = y y p p B B V ] ] ..o.o.o.o.o.o.o.=.} $.#.$.+.+.+.+.+.0.0.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ W ! ! / ! ^ ^ ^ e.e.e.r.r.y.y.<.<.1.1.2.s.s.l.^.^.4X7XyXyXtXhXtXwXeX8X`.`.'.E.k.k.8.6.6.>.%.X.} [ [ F F z z k l i i i i y y y y p p B B V V ] ' ' o.o.o.o.o.U.U.X.$.$.$.#.$.+.$.+.+.+.+.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! Q ( ^ ^ ^ w.w.e.e.r.r.y.-.-.<.<.1.1.2.s.l.c.^.^.4X0X0XyXsXsXyXeX0X7X`.'.'.z.k.k.8.7.,.>.X.} } } { F n c z k l l i i i y y y y y p p B B V ] ] ' ' o.o.o.o.F.U.| $.$.$.$.$.+.+.+.+.+.+.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! ! ! / ^ ^ w.w.e.e.e.r.-.r.p.-.-.1.1.2.s.x.c.Q.(./.0X0XyXyXsXsXyXeX0X8X`.'.b.z.z.k.j.7.6.>.=.X.} } { F F v z k l l l i y i y y y y p p B B V V ] ' ' o.o.o.,.L.U.o...$.$.$.$.$.+.$.+.+.+.0.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q / / ^ ^ ^ t.w.e.e.e.-.e.-.-.-.1.1.1.2.a.s.x.c.v.4X7X0X0XyXsXtXtXwXeX8X`.`.'.E.z.z.k.8.6.,.>.o...} } [ F F G c k j l d i i i y y y y p p p B B V ] ] ' ' o.o.F.G.U.o.....$.$.$.#.#.#.+.+.+.+.0.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! ! Q / / ^ t.w.w.e.e.r.y.y.y.-.y.1.1.1.s.x.d.c.c.^.4X7X0XtXsXsXsXtXwX0X7X`.~.'.E.z.z.8.8.6.,.>.6...} [ { { T P.N.z j l i i i i y y y y p p p B B V V Z ' ' ..5.P.G.B.o.......$.$.$.#.#.#.#.+.+.+.+.0.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / ^ t.w.w.e.e.e.e.e.y.-.-.p.-.1.1.2.s.s.x.c.(.^.4X6X0XyXsXsXtXtXeX0X8X].`.'.z.z.z.j.8.6.,.>.*.X.} } [ { T G.H.N.j l l l i y i y y y y p p B B B V Z ' ' $.F.%XK.Y.o.......$...$.#.$.$.#.+.+.+.+.+.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! / / ^ ^ w.w.w.e.&.e.y.-.y.y.<.<.1.1.1.4.s.x.c.c.^.4X4X0XwXyXsXsXtXtXwX0X`.`.'.E.b.z.k.8.8.6.,.>.%.X.} [ [ [ T J.K.H.V.k i i i i y y y = y y p p B B V V ] ' B. X*X|.F.=.........$.$.$.$.#.#.#.#.+.+.+.+.+.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! Q ! / ^ ^ ^ I t.e.e.e.r.r.y.y.-.<.<.1.1.2.s.s.x.Q.(.^.4X0X0XyXyXsXsXtXwXeX7X7X`.'.E.E.z.k.8.6.6.6.>.:.X.} [ [ [ N.K.=X XG.B.x l i i y y y y y y p p h B B B Z B. X&X*X#XP.5.o...} o.....$.$.$.+.$.+.+.+.+.+.+.0.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q / / / ^ ^ w.e.e.e.e.;.r.y.y.-.<.<.1.1.2.4.s.x.c.c.^.^.4X0XwXyXsXsXsXwXwXeX7X`.`.'.!.z.z.k.k.8.6.>.>.:.| } } [ G N. X*X*XoXK.P.T A f i i y = y y p p h B E _ F.oX*X*X*X*XK.F.,.o.o.o...$.$.$.$.$.$.#.#.#.+.+.+.0.+.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! / / / ^ ^ w.w.e.e.e.r.r.y.y.y.p.<.<.1.1.2.s.x.c.c.^.^.4X0X0XtXtXsXsXyXwXeXeX7X`.'.'.!.z.k.k.8.7.6.,.=.X.| } } [ [ B.XX*X*X*X&X XK.F.V.S A A f s f g S S B.F. X$X*X*X&X#X*X$XJ.F.5.o.o.....$...$.$.#.#.#.+.+.#.+.0.+.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! Q / / / ^ w.w.e.e.e.r.r.r.y.y.<.<.<.1.1.2.2.s.x.c.c.^./.4X0X0XyXsXsXsXyXeXeX7X7X`.`.!.E.z.z.9.8.7.6.,.=.*.X.} } [ G F.oX*X*X*X*X*X&X#X XK.J.H.H.H.P.P.L.|.XX&X*X*X*X&XXX|.$X*X$XK.P.U.=.......$.$.$.| #.#.#.#.+.#.+.0.+.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ / / ^ ^ w.w.e.e.e.r.r.r.-.-.y.<.<.<.1.1.s.s.x.l.c.^.^.4X7X0XwXyXsXsXsXtXeX0XeX7X`.'.'.E.b.j.j.j.8.6.6.=.%.X.} } [ .P.&X*X=X&X*X*X*X*X*X*X$X#XoXoX#X$X&X*X*X*X*X*X$X X X X X$X*X&X|.J.B.U.=.X.o.$.$.$.$.#.#.#.#.#.+.+.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ / ^ ^ w.w.e.e.e.r.r.r.-.y.-.<.<.<.1.2.2.s.s.x.c.c.^.5X7X0X0XyXyXsXsXyXtX0X0X7X'.`.'.!.z.k.b.j.8.6.6.>.>.%.X...[ [ ..X*X*X&X X#X&X*X*X*X*X*X*X*X*X*X*X*X*X*X&X&XoX X X X X X X$X*X&XoX|.P.B.U.5.X...$.$.$.#.#.#.#.+.+.+.+.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ / ^ w.^ e.e.e.e.r.r.r.y.y.<.<.<.1.1.2.2.s.s.c.c.^.^.4X7XuXwXyXsXsXsXyXeXeX0X7X7X`.!.'.z.z.j.j.8.6.6.,.=.*.X.| } [ T #X*X*X&X|. XXX#X&X&X*X*X*X*X*X*X*X*X&X&X#XXX X XK.J. X X|.XX$X*X*X&XoX|.L.F.B.U.U.5.| ..' #.' +.+.+.0.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ ^ ^ w.e.e.e.r.e.r.r.-.y.-.<.<.<.1.1.s.s.d.x.g.v.v.4X4X7X0XwXyXhXsXyXyXtX0X7X7X7X'.!.E.b.z.k.j.8.6.6.>.>.X.| } } .Y.*X*X*X$X|. X X XXXoX$X&X&X&X&X&X&X#X#X X X X X|.G.G.|. X X X X#X&X*X*X*X$XXX|.K.P.F.B.Y.N.U.U.U.N.U.N.UXG.G.G.G.G.G.G.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ e.^ e.e.e.e.e.y.r.y.y.-.<.-.-.1.1.3.4.s.x.c.c.v.^.4X7X0XwXyXtXhXaXiXqXeX0X8X7X].`.!.!.E.k.j.8.8.6.6.>.=.*.X.} } U.$X*X*X*X#X|.K.|. X X X X XXXoXoXXX X|.|. X X|.J.G.A.A.G.K.|. X X|.oX$X&X*X*X*X*X&X#XoXXX X X|.|.|.|. X X X XXXXXoXXXK.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt.^ ^ e.e.e.e.e.y.y.y.-.p.<.1.1.1.2.2.s.x.x.c.c.^.4X4X7X0XyXyXsXtXaXiX{.,X{.4X7X`.!.!.E.E.b.j.j.7.6.6.,.=.*.X.[ 5.}.*X*X*X&XXX|.J.G.J.K. XXX X X X|.|.|..X;X;XlXlXlXlXlXlXlXI.;X.X|. X|.XXoX$X&X*X*X*X*X*X*X*X*X&X&X&X&X&X&X&X*X*X*X*XXXJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.e.e.e.y.e.y.y.y.y.-.-.<.1.1.2.a.s.s.x.c.c.^.^./.7X0XuXyXtXsXsXsXpX{.OX=XXxX>XxX>XxXxX>XxXzXcXvXVXCXKXKXCXvX;XK.|.|. X XXXoXoXoXoXoXoXoX$X*X*XoXG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.r.r.y.y.y.y.-.<.<.<.1.1.2.4.s.s.l.c.v.(.5X4X4X0XwXyXyXhXsXtXtXwXeX0X9X_.XxXzXzXzXzXcXzXzXzXzXzXzXzXxXxXxXzXvXVXFXPXFXZX+X X X X X X|. X X X|. X&X*X&X XG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.r.y.y.-.-.<.<.<.1.1.1.2.s.x.d.l.Q.v.^.4X4X7X0XyXyXsXsXsXtXwXwXeX7X].`._.}. X&X*X*X&X&X&X$X%X=X&X*X*X*X*X*X*X*X#X X X|.J.H.ZXFXCXvXzXxXzXzXzXzXzXzXzXzXzXzXzXzXzXzXzXzXzXzXcXzXzXxXxXcXVXKXIXCX;XJ.|.|.|.|.|. X X XoX*X*X$XK.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy.y.y.y.y.y.<.<.1.1.2.2.4.s.s.x.c.Q.^.4X4X4X0XwXyXyXsXsXsXtXwXeXeXeX8X`.`._.}. X=X*X*X*X*X*X*X*X*X*X*X*X*X*X&X#X X X XK.lXZXCXvXzXzXzXzXzXzXzXzXcXcXcXcXzXcXcXzXcXcXcXcXzXzXzXzXzXzXzXxX>XvXCXIXFXlXG.G.G.G.J.|. X X#X*X*X$XJ.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy.y.y.p.<.-.1.1.1.1.s.s.x.l.c.c.^.^.4X4X0X0XyXyXsXhXsXyXyXeXeX0X7X].`.`.'.T.P.XX&X*X*X*X*X&X*X*X*X*X&X$X#XXX X X XK.bXFXVXcXzXzXzXzXzXzXzXzXcXzX>X:X:X:X:X:X:X:X:X>XxXzXcXzXzXzXzXzXzXzXxXzXVXKXKXbXG.G.G.J. X XXX$X*X*X#XG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX-.y.1.1.-.1.1.2.s.s.d.x.c.c.^.^.4X4X0XeXyXtXsXsXsXyXyXeXeX8X8X`.`.!.'.b.{.P.#X*X*X*X*XoXoXXXXXXXXX X X|. X XK.ZXFXvXzXzXzXzXzXzXzXcXcX>X-X-X-X*X*X*X*X*X*X*X*X*X*X-X:XxXcXcXzXzXzXzXzXxXxXvXFXPXZXA.G.K. X XXX&X*X*X#XG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX<.1.1.1.1.2.4.s.x.x.c.c.(.^.4X4X7X0X0XyXtXsXsXtXyXtXeXeXeX].].`.`.E.z.b.}.L.&X*X*X*X#X X X X X X X X X|.K.bXFXvXzXzXzXzXzXzXcXzX>X-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-X-XxXcXcXzXzXzXzXzXzXvXFXIXbXG.K. X XXX&X*X*XoXJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX1.1.2.2.s.s.d.l.c.Q.^.^.4X4X0X0XwXyXsXsXsXyXtXtXeXeX7X`.`.`.~.'.v.z.W.A.#X*X*X*X&XXX XK.J.J.K.J.J.G.bXFXvX>XzXzXzXzXzXcX>X-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XxXcXzXzXzXzXzXzXvXFXKXbX|. X XoX&X*X*X#XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXa.2.s.s.s.x.l.c.c.^./.4X5X0XwXyXyXsXsXtXtXtXtX0X0X`.7X`.~.'.!.!.v.x.}. X&X*X*X*XXX X|.G.G.G.G.G.lXKXvXzXzXzXzXzXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-X>XcXzXzXzXzXzXzXvXPXFX;X|. XXX&X*X*X$XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXs.s.x.l.Q.v.^.4X4X7X0X0XwXyXyXsXsXsXtXtXwX0X0X8X`.`.`.!.!.z.z.c.U.L.&X*X*X*XoX X XJ.G.G.G.I.KXVX>XzXzXzXzXcXxX:X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X>XzXzXzXcXzXzXzXVXPXCX X XXX&X*X*X$XJ.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXl.c.(.(.^.4X4X0X0XwXwXsXsXsXsXtXtXtXeXeX7X7X`.`.'.!.'.!.z.z.W.L.&X*X*X*X#X XXXG.G.G.H.FXCXxXzXzXzXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X>XcXzXzXzXzXzXcXFXKX;X|.XX$X*X*X&X|.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXR ^ g.7X4X4X7X0XwXyXsXsXsXsXyXtXwXeXeXeX8X`.`.!.'.!.z.z.k.k.P.$X*X*X*X$X X XK.G.G.bXFXzXzXzXzXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XcXzXzXzXzXzXvXPXCX X X#X*X*X*XXXG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXN R R M E u.5XpXqXyXyXsXsXsXtXtXyXwXeX7X7X7X`.`.'.!.!.v.z.x.[.L.=X*X*X*X$X X X|.G.G.FXvXzXcXzXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X>XcXzXcXzXzXcXCXKX;X XXX&X*X*X$XK.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXR A N R M R R R ) f.5XsXsXsXsXsXtXtXwX0XeX7X7X`.`.'.'.E.E.z.z.U.L.&X*X*X*X$X X X|.G.bXKXcXzXzXzXzXxX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XxXcXzXzXzXcXVXIXCX|. X#X*X*X*X#XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXR R N R R R A A R S R R K ` 2XuXyXtXwXwX0X7X7X`.`.`.'.!.!.v.z.j.Y.L.&X*X*X*X#X X XK.G.FXVXxXzXzXzXzXxX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-XcXzXzXzXzXvXIXFXXX X X$X*X*X*XoXG.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXR R R R R R R R R J R R K A E J K I u.g.T.7XeX].`.`.'.!.!.E.z.z.z.F. X&X*X*X*XoX X XK.lXPXcXzXcXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XcXzXzXzXcXCXKXlX X XXX$X*X*X*X#XG.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXR M E R R J R R J J J J J J J A K J K J K K _ _ ` m.g.5Xg.uXb.z.z.W.P.oX&X*X*X*XXX X XJ.CXFX>XzXzXzXcXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XcXzXzXzXzXVXPXbXXX X XXX&X*X*X*X$XK.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM E M E E R E J J E R K J J K J K K K K K K _ ( ! _ ` ` n.n.n.n.f.h.B.P.&X*X*X*X&X X X XJ.IXvXxXzXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XcXzXzXcXvXPXZX|. X X X X#X*X*X*X&X XG.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXE E E E R E E J E E J J K K J K K K K K K _ K ( ` ` ` ` n.n.n.n.n.` ` F..X&X*X*X*X#X X X|.;XPXcXzXzXzXzXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-XzXzXzXzXvXIXZXG.K. X X X XoX&X*X*X&X#XJ.G.G.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) E ) E E E E J E J K J ( E K K K K K K S _ K _ _ _ ` ` ` n.n.m.m.n.` R.Z.$X*X*X*X&X X X XJ.bXFXxXzXzXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-X>XzXzXzXcXKXCXH.G.G.|. X X XXX&X*X*X*X&XXXG.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) Q E E E E K J E K K E K K K K K K I Y K Y Y ( _ _ ` ` ` n.m.m.m.m.n.n.B..X&X*X*X*X#X X X|.G.ZXCXxXcXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XcXzXzXcXFXFXI.G.K. X X XXX$X*X*X*X&X$X XUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXE Q E E Q Q Q E K Q ( ( K ~ ( I I I I I I Y Y Y _ u.` h.` n.m.m.1Xm.m.m.{.A.$X*X*X*X$X X X XG.G.FXVXxXzXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X:XzXzXxXcXFXFXI.K. X X XXX$X*X*X*X&X|.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXE Q Q E ( ( E K Q ( ( E I I I I I I I I I I Y _ _ u.h.h.h.m.m.m.m.1Xm.n.{.Z.XX&X*X*X&XXX X XK.G.H.FXvXzXcXzXzXxX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-XzXzXzXcXFXFX;X X X X X$X*X*X*X$XJ.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q Q Q ! ( ! ~ ( ( ( ( ^ I ( w.I I I I ;.Y Y _ i.h.h.m.g.g.m.2X1Xm.Y.P.|.=X*X*X*XoX X X|.G.H.A.FXvXzXzXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-XzXzXzXcXFXFX+X X X X$X*X*X*X*X|.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q Q / / ( / ( ( ^ ^ I e.I e.I e.;.I ;.;.;._ y.h.i.h.g.f.R.1X1X1XkXF.H.XX&X*X*X*X#X X X|.G.G.G.I.FXcXcXzXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XzXcXzXcXKXFX+X|. XoX&X*X*X*X$XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q ! ! / ( / ( ^ w.I w.w.I e.I I .I ;.;.Y u.u.u.i.f.g.g.m.1X1X2X{.P.H.oX*X*X*X*X#X X X|.G.G.G.G.H.FXvXzXcXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X-XzXzXzXcXPXZX X X X$X*X*X*X*XoXG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / / / ^ ^ ^ w.e.I e.I I e.;.I ;.;.-.;.;.;.u.u.d.i.g.m.g.m.2X{.P.J.+X&X*X*X*X*X#X X X|.G.G.G.G.G.I.FXcXzXzXzXzXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X@X@X=X*X&X=X*X*X=X*X*X*X*X*X*X*X*X*X*X*X*X*X*XxXzXzXvXPXVX|. XXX&X*X*X*X*XoXG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q ! / / ^ ^ w.w.w.e.e.e.r.;.r.e.e.;.y.;.;.u.p.a.i.i.g.g.g.g.{.P.G. X=X*X*X*X*X*X#X X X XJ.G.G.G.G.G.H.FXvXzXzXzXzX>X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X< V.=X*X=X=X=X=X*X=X=X*X*X*X*X*X*X*X*X*X*X*X*XzXzXzXCXIXlX X XXX&X*X*X*X*X$XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / ! ^ ^ w.w.w.e.e.e.e.r.;.;.r.-.-.;.-.;.1.a.i.p.g.g.g.g.m.2X{.F.I..XOX&X*X*X*X&XoX X X X XK.J.G.G.G.G.FXvXzXzXzXzXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X< V.=X=X=X=X=X=X=X*X=X*X=X*X*X*X*X*X*X*X*X*XzXzXcXKXFX;XK.|.XX&X*X*X*X*X&XK.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q Q / ! / ^ w.w.w.e.e.r.r.r.r.r.-.-.-.1.;.3.1.a.d.g.d.c.g.v.uX2XuX3XrXXzXvXFXFXG.G.J.|. XXX$X*X*X*X*XoXG.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! ! / / ^ ^ ^ ^ ^ y.y.^ r.r.r.y.y.-.<.1.1.1.1.s.s.d.x.g.b.c.5X4X5X5XrXuXyXrXuXrX5XuX5X{.F.OX$X*X*X*X$X X X XXXK.G.KXcXzXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X< . V.@X@X@X@X@X@X=X=X=X=X*X*X*X*X*X>XxXVXIXbXH.G.G.J..X XXX$X*X*X*X#XJ.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! ! / / / ^ ^ e.^ e.^ e.y.r.r.-.y.<.<.<.<.1.1.2.s.s.x.c.c.v.5X^.4X4X0XwXyXgXyXyXuX6X5X5Xc.T.F.K.#X*X*X*X$X X X X XG.ZXCXxXzXzXzXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X=X*X< X V.}.}.@X@X@X@X=X=X=X=X*X*X*X-XxXcXKXFXI.J.K.|.|. X X|. X$X*X*X&X|.H.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! ! ! / / ^ ^ w.^ y.e.e.y.e.-.r.y.<.<.<.1.1.1.2.s.s.x.c.c.c.v.4X4X7X0XuXyXyXyXyXrX0X7X5X5X5Xv.T.F.P.oX&X*X*X#X X X X|.I.IXcXzXzXzXzX-X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X=X*X< @ V.}.@X@X@X@X=X=X=X*X=X*X*X:XcXCXPXZXK. XXX X X XXXXXXX#X&X*X*XXXG.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! ! ! / / / w.^ e.^ y.^ y.e.y.r.-.-.p.<.<.1.1.2.2.s.s.x.c.(.b.5X5X4XuX0XyXyXfXyXyXyX0X0X4X4X^.(.(.(.Y.H.oX&X*X*XoX X X XJ.FXVXzXzXzXzXxX*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X=X*X< . N.}.}.}.@X@X@X=X=X*X*X-XzXvXIXKX;X|. X X XXXoX$X&X&X&X*X*X*X&XK.G.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! Q ! ! Q ( / / ^ t.^ w.w.e.e.r.r.y.y.-.r.<.<.-.1.1.3.2.s.s.s.c.c.c.^.4X4X5X0XyXyXyXsXsXyXtX0X0X7X7X`.^.c.c.l.F.L.$X*X*X*X X X X XlXKXcXzXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X*X< X V.}.}.@X@X@X@X=X=X=X:XvXFXPXbX|. X XXX$X&X*X*X*X*X*X*X=X#XXXK.H.G.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ W ! Q ! ! / / ^ ^ w.w.e.e.e.y.e.y.y.e.-.<.<.<.1.1.1.2.4.s.s.c.c.c.^.^.4X4X0XwXyXyXsXsXsXyXwX0X0X8X`.`.^.^.c.v.W.P.K.=X*X*X#X.X XXXG.FXVXxXcXzXzXzX*X*X*X*X*X*X*X*X*X*X*X*X*X*X=X=X*X< @ V.}.}.}.@X@X%X=X-XzXVXPXCX.X X X#X&X*X*X*X*X*X&X#X XP.F.V.N._ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ( ! / / ^ ^ w.^ e.^ e.e.y.r.y.y.y.y.<.<.<.1.1.2.s.s.s.d.Q.c.(.^.^.4X7X0XwXyXsXsXsXtXtXwXeX8X7X`.`.^.!.v.l.x.Y.H.$X*X*X*X X X XJ.bXKXcXzXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X*X=X=X*X< X V.}.}.}.@X@X@X,XVXIXKX;X|. X#X*X*X*X*X*X*X#X.XP.B.T ! C Z C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! Q ! Q / / ^ ^ ^ ^ e.e.e.e.e.y.y.y.y.y.<.<.<.1.1.2.2.s.s.s.x.c.c.v.4X4X4X0X0XwXyXsXsXsXtXyXeX0X0X7X`.`.'.!.!.z.x.[.H.oX*X*X*XoX X XK.H.FXCX>XzXzXzXzX:X*X*X*X*X*X*X*X*X*X*X*X*X=X=X=X< @ V.}.}.}.@X,XvXKXPXbX.X X#X*X*X*X*X*X*XoXP.B.N.( C C C ~ C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ! / / / ^ w.^ w.w.w.r.e.y.e.y.y.y.-.<.<.<.<.1.1.1.2.s.s.c.c.c.Q.^.4X4X7X0X0XyXtXsXsXsXyXtXeX0X7X7X`.`.'.'.!.E.k.k.P.XX*X*X*X$X|. XK.G.bXIXcXxXzXzXzXxX-X*X*X*X*X*X*X*X*X*X*X*X*X=X*X< @ V.}.}.+XbXGXHXCX+X.XXX&X*X*X*X*X*XoXP.N./ C b ~ ~ C C C C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! ! ! / ! / ^ ^ w.w.e.e.r.e.e.y.y.y.y.y.y.<.<.1.1.2.2.s.s.x.d.c.c.^.^.4X7X0X0XwXyXsXsXsXsXyXtX0X0XeX`.`.`.~.!.v.z.z.j.P.oX&X*X*X$X X XJ.G.H.ZXFXxXzXzXzXzXxX-X*X*X*X*X*X*X*X*X*X*X=X=X*X< @ V.XzXzXzXcXxX:X*X*X*X*X*X*X*X*X*X=X=X=X< @ nXZXSXDXlXF.L..X=X*X*X*X*X#XL.N.O.@.L @.( L L ~ L C ~ C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q Q / ! ^ / ^ ^ e.w.e.^ e.e.e.r.r.y.y.y.y.<.1.<.1.1.2.4.s.s.x.x.c.Q.^.^.4X7X7X0XwXyXyXsXsXtXtXtXeX0X0X`.`.`.~.'.!.E.E.z.U.H.&X*X*X*X$X X|.G.G.G.G.lXFXVX>XxXzXzXzXzX:X*X*X*X*X*X*X*X*X=X=X*X< + + NXDXnXP.P.}..X=X=X=X*X&X|.B.O.O.@.@.~ L L C ~ C ~ C C ) C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! Q ! ^ ^ / ^ t.^ ^ ^ e.r.e.e.y.r.y.r.y.<.y.p.1.<.1.2.2.s.s.s.x.c.c.Q.^.^.4X4X0X0XyXtXsXsXsXtXtXeXeX0X7X7X`.`.'.!.E.E.z.k.Y.G.&X*X*X*X#X XJ.G.G.G.G.H.bXKXCXvXzXxXzXcXzX>X-X*X*X*X*X*X*X=X=X*X< mX4 + nX0 F.F.P.OX%X=X=X=X XF.;.{ O.@.@.L @.@.~ ~ ~ ~ ~ C ~ C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q ! / ! ! / ^ ^ ^ e.^ y.^ e.r.e.y.y.y.y.-.y.<.<.1.1.2.1.2.s.s.d.x.c.c.^.^.4X4X8X0X0XyXtXsXsXsXtXtXwXeXeX0X7X`.`.~.'.E.z.z.k.F. X&X*X*X*XoX|.G.G.J.J.J.K.I.VXPXKXCXvXcXxXzXcXxX:X-X*X*X*X*X=X=X=X< DXGX2 @ 0 F.F.P.}.@X%X=X$XP.B.{ [ O.@.@.@.L L L @.~ L C L C ~ C W C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ Q Q ( ! ^ ^ ^ ^ t.w.^ e.e.y.y.^ y.y.y.y.-.p.<.1.;.1.1.2.2.s.s.x.l.c.c.^.^.^.4X7X0X0X0XyXtXsXsXtXtXyXwXeX0X7X7X`.`.'.!.'.E.z.[.H.#X*X*X*X&X X|.|.|.|.|. X X|.+XbXPXPXKXCXvX>XxXzXzX>X>X:X-X*X=X=X*X< BXPXAX2 $ M.F.F.}.@X@X=X.XF.| [ O.O.O.@.@.L L L L L L ~ C C C C ~ C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! ! / / ^ / ^ w.t.w.w.e.e.e.^ y.y.e.y.y.y.-.y.<.<.1.1.1.2.s.s.s.d.x.c.c.(.^.4X4X7XuX0XyXyXsXsXsXsXtXyXeXeX7X7X7X`.'.'.!.z.z.b.Y.L.&X*X*X*X#X X X X X X X X X X X+XlXKXPXIXKXFXVXcXxX>XzXxX>X>X:X,X:X< BXPXPXGX2 @ C.C.}.@X@X@XP.B.[ [ [ [ .O.O.@.L @.L L ~ L ~ L ~ C C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXQ ! / / / / ^ w.^ e.e.^ e.e.u.e.e.y.y.y.y.y.p.<.<.1.1.1.2.2.4.s.s.l.c.c.Q.^.^.4X7X0X0X0XyXtXsXsXtXaXaXwXeX0X8X7X`.`.'.~.v.!.z.W.P.XX*X*X*X&X X|. X X XXXXXXXXX X X X XlXCXPXPXPXIXKXFXCXvXcXxXzXzXxXvX1 BXPXPXPXAX+ $ F.F.}.}.OXC.T [ [ [ [ [ } O.O.@.L L ^ L L L C L C C ~ C W UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! ! / ^ / ^ ^ ^ ^ ^ e.e.e.e.e.e.y.y.y.y.y.y.<.p.<.1.1.1.2.2.s.s.s.c.c.c.^.(.^.4X4X0X0XyXyXsXsXsXtXtXwXwXeXeX7X7X`.`.'.!.E.E.x.}.L.$X*X*X*X$X#X#X$X&X&X&X&X&X&X&X&X$X#XoX+XlXCXPXPXPXIXIXKXCXvXvXvXcXvX< BXPXPXPXPXGX+ @ F.}.}.P.F.n [ [ [ [ [ [ O.O.@.@.@.L L L L q.C ~ L C C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! ! / / ^ w.w.w.e.e.e.e.e.r.r.r.y.y.y.y.-.y.<.<.1.1.1.2.s.s.x.d.c.c.c.Q.^.^.4X7X7X0XyXyXtXsXsXsXsXtXwXwX0X8X8X7X`.`.'.!.!.E.[.H.XX*X*X*X*X&X*X*X*X*X*X*X*X*X*X*X*X*X&X&X&X#X;X;XVXKXPXIXPXIXKXKXFXGXKX4 BXPXPXPXPXPXAX+ 1 }.}.F.M.F { [ [ [ { #.[ O.O.O.@.@.L L ~ ~ @.~ ~ C ~ C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! ! / / / ^ ^ ^ w.^ e.^ y.r.r.r.r.y.y.y.-.p.<.<.1.1.1.1.2.a.s.x.x.l.c.Q.^.^.4X4X7X0X0XyXyXsXsXsXsXtXtXwXeXeX0X8X`.`./.'.!.'.W.F.L.&X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X&XoXXX;X;XZXCXFXKXIXIXHXPX4 BXPXPXPXPXPXPXJXX $ }.F.B.n { { { [ [ [ [ O.O.O.@.@.@.@.@.~ @.~ L C C ~ ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ / / / / ^ w.^ w.w.e.e.e.^ r.r.e.y.y.y.y.p.<.p.3.1.1.1.2.4.s.s.x.l.c.c.v.(.4X4X4X7X0XyXyXtXsXsXsXsXtXtXeXeX8XeX`.`.`.~.'.!.z.Y.L.#X*X*X*X*X*X*X&X&X#X#X#X#X#X#X$X&X*X*X*X*X*X*X*X&X#X X X XJ.I.I.I.I.A.;X1 BXIXPXPXPXPXPXPXAXX . 1 F.M.D G G { [ [ [ [ [ [ [ @.@.L L L L L ~ C ~ ~ C C ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ ! ! / / ^ w.w.w.w.e.e.e.e.y.e.r.y.y.y.y.-.y.<.1.p.1.1.2.1.4.x.d.d.l.c.c.^.^.^.4X4X0X0XwXyXtXsXsXsXsXaXwXeXeX7X7X7X`.`.'.~.b.{.L.XX*X*X*X$XoX XK.K.L.L.P.P.P.G.G.J.J.K.XX$X*X*X&X*X*X*X$X X X|.G.G.A.G.P.|., BXPXPXPXPXPXPXPXPXSX. . $ M.D G G G G [ [ [ [ O.[ O.O.@.L @.L L ~ @.~ ~ ~ ~ C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! / / / ^ ^ w.^ w.w.e.e.e.y.e.y.y.y.y.y.-.y.<.<.1.1.1.1.2.s.s.s.x.x.c.c.^.^.^.4X4X7X0X0XyXyXyXsXsXyXsXwXwXeXeXeX7X7X`.`.^.^._.P.|.&X&X XJ.J.L.F.U.U.,.=.5.5.X.5. .U.N.F.G.G.|.&X*X*X*X*X*X#X X XK.G.A.L.L.XX, BXLXPXPXPXPXPXPXPXPXAX. . $ S x D G G { [ [ [ [ [ @.O.L @.L L L L L L L C q.C C UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! / / / ^ w.w.w.e.e.e.e.e.e.e.e.y.y.y.y.-.1.p.p.1.1.1.2.2.4.d.s.x.c.g.c.v.^.4X4X7X0X0X0XyXyXyXsXsXtXtXwXeXeXeXeX7X7X`./.^.!.}.G.|.K.G.P.Y.U.9.6.6.>.=.=.X.X.| } } [ [ .H T F.L.XX&X*X*X*X&X#X X|.G.G.K..XXX1 BXPXPXPXPXPXPXPXPXPXPXDX. . > v x D G G G { [ [ [ [ @.O.O.O.O.L L L L L L L C ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ / ^ ^ ^ e.t.e.^ e.e.e.-.y.y.y.y.y.y.<.y.<.<.1.1.1.2.4.2.s.d.x.c.c.c.Q.^.^.4X7X7X0XeXyXyXsXsXsXsXpXtXtXwX0X0X8X`.`.`.`.'.Y.L.G.P..=.%.o.X.} } } [ [ { F G T B.|.&X*X*X*X&XXX|.K.|..X.XXX1 BXLXPXPXPXPXPXPXPXPXPXPXSX > x D G G [ G [ [ [ [ [ O.O.@.@.L L L L L C L C L UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ ^ / ^ w.w.^ e.e.e.e.e.e.e.e.y.y.y.-.-.y.-.<.<.1.1.1.1.4.d.s.s.x.d.c.c.^.^.4X4X4X0XuXeXyXtXtXsXsXsXsXtXwXeX0X0XeX`.`.`.'._.P.L.Y.[.W.k.k.k.8.7.6.6.,.>.=.%.X.X.} } [ [ { { F F G G T P.$X*X*X*X#X X|. X.XL.oX< BXIXPXPXPXPXPXPXPXPXPXPXIXDX . > x v G G G G { [ [ [ O.O.O.O.@.L L @.L L ~ ~ ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX/ ^ ^ e.^ e.e.e.e.e.e.e.-.e.y.y.y.y.y.p.1.1.<.1.1.1.1.2.s.s.d.k.c.c.c.c.^.^.4X4X7X0X0XyXyXyXsXsXsXsXtXwXeXeX0X8XeX`.`.`.`.[.{.[.z.z.k.j.k.8.8.7.6.,.,.=.=.*.X.| X.} } [ [ { F F n v c T P.=X*X*X&XoX X X XOX&X< BXLXIXPXPXPXPXPXPXPXPXPXPXPXBX . > n D D G G F [ [ [ [ O.O.O.w.@.@.L / L ~ L ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ ^ ^ t.t.^ w.e.e.e.y.e.e.y.y.y.y.y.-.y.<.p.1.1.1.1.2.4.s.s.s.x.x.c.(.v.(.^.4X7X7X0X0XyXtXtXsXtXsXsXtXtXeXeXeX8X`.`.`.`.'.!.E.E.E.z.z.k.k.8.8.7.6.6.,.=.>.X.X.| } } [ [ [ F F F G v c k U }.&X*X*X#X X|.OX%X&X< BXLXPXPXPXPXPXPXPXPXPXPXPXPXPXBX . > x D G G G G G [ O.[ O.O.O.O.O.L L @.@.~ q.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ ^ ^ e.^ e.e.e.;.e.r.r.y.y.y.y.<.y.<.<.<.1.1.1.2.2.s.s.s.l.c.c.c.c.^.4X4X4X4X0X0XwXyXyXsXsXhXsXtXtXtXeXeX0X8X8X`.`.`.'.'.E.!.z.z.k.k.8.8.8.7.6.,.,.>.>.X.X.X.} } } } { { F b v n v z z T }.*X*X$X XXX=X=X*X< BXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXNX . : D D G G G F { [ O.O.{ O.P @.@.L L L L ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX^ e.e.e.e.e.e.e.e.r.y.y.y.y.-.<.-.p.1.<.1.1.1.2.2.s.s.s.l.d.l.c.(.^.^.4X4X7X6X0X0XwXyXtXsXhXtXsXsXwXwXeXeX8X8X`.8X`.`.'.E.'.z.z.z.k.k.k.8.8.7.6.6.,.>.:.X.X.| | } [ [ [ { F F b v v c z z N.oX*X&X#X$X*X=X*X1 BXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXIXNX . : D x G G G F F [ [ O.O.O.O.@.O.L L L ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw.^ e.e.e.e.e.e.y.e.-.y.y.y.y.-.<.<.1.1.1.1.1.2.2.s.s.s.x.c.c.c.c.(.b.^.4X7X0X0X0XyXyXsXsXsXsXtXtXwXwXeX8X0X8X`.`.`.'.~.!.v.E.z.z.k.k.j.8.8.6.6.,.>.>.>.%.X.X.} } } [ [ { { F F v v c z k U B.&X*X*X*X=X=X&X< BXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXLXmX X : D D D G F H { { O.O.H O.@.L @.@./ @.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.e.e.e.e.e.y.e.y.e.y.y.y.-.p.y.<.<.1.1.1.2.2.2.s.x.d.x.l.c.^.v.^.4X4X4X7X0X0XwXyXyXsXsXsXtXaXtXwXeXeXeXeX`.`.`.~.'.'.!.b.z.z.z.k.j.8.8.8.6.6.,.>.>.>.X.X.| } } [ } [ { F F b v v c c z l T OX*X*X*X=X#XK.$ BXLXPXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXMX O : D D G G G F [ O.O.O.O.@.O.@.L @.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.e.r.r.e.r.y.y.y.y.y.-.y.<.<.<.<.1.1.1.2.2.4.s.s.x.x.c.c.Q.(.^.4X4X7X7X0XeXwXyXtXsXsXsXsXyXyXeXeX7XeX7X7X`.`.`.'.~.!.E.z.z.k.j.j.8.8.7.7.7.,.,.,.>.X.X.} } } } [ [ { F F F v v v z z k D B.$X*X*XoXP.G.$ BXLXPXIXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXMX X : D D D G F F [ { [ O.O.@.@.@.L UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.e.e.e.;.e.y.r.y.-.y.-.<.<.1.1.1.1.1.2.2.d.s.s.s.x.c.c.^.v.^.4X^.4X7X0X0XeXyXyXyXsXsXsXyXyXyXtXeXeXeX7X].`.`.`.'.~.E.E.E.z.z.z.8.j.8.7.7.7.6.6.o.=.X.X.X.X.} } [ [ { { F F F v c c z z l N.XX*X#XJ.A.B.o NXLXPXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXLX3 O s D D D G G G { { G .O.@.O.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.y.r.e.y.y.y.y.y.-.p.<.<.<.;.1.1.1.2.4.s.x.l.x.d.c.c.v.(.^.4X4X7X6X0X0XyXyXyXsXhXsXsXsXtXyXeXeXeX].7X7X`.`.`.'.'.E.b.z.z.j.j.k.j.8.8.7.6.,.,.,.=.X.X.| } } } [ [ { F F v F v c c z k k U }.#XJ.G.V.f o NXLXLXPXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXHX3 O s D D G G G F O.[ O.O.O.@.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe.e.y.y.y.y.;.y.<.y.<.<.1.1.1.1.2.2.2.4.s.d.s.c.c.c.(.c.^.4X4X4X7X7X0XyXyXyXsXsXsXsXsXyXeXyXeXeXeXeX7X7X`.`.'.'.'.E.E.E.z.z.k.j.8.8.7.7.,.6.,.o.=.X.X.| } } } [ [ { { F F v v v c c k k D B.K.G.C.D i o BXLXLXIXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHX3 O g x D D G G F F O.H O.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.e.y.r.<.y.y.<.<.<.<.1.1.1.2.2.2.s.s.s.s.x.c.c.Q.(.^.4X4X4X7X0XuX0XwXyXyXsXsXsXtXtXtXtXeXeX0X7X8X7X`.`.`.'.~.!.!.z.z.z.j.k.j.k.8.7.7.6.,.,.o.,.o.X.| } } [ [ [ [ { F F b b c c c k k k V.G.H.T i i o BXLXLXPXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXGX2 @ A A D G G F P H O.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy.y.-.r.-.-.-.p.<.<.<.1.1.1.2.s.s.s.d.x.x.l.c.Q.v.4X^.4X4X7X0X0X0XtXyXyXsXsXsXhXtXtXtXwXeX0X0X7X7X7X`.'.~.'.E.'.z.z.b.j.k.8.8.8.7.7.,.,.,.,.=.=.X.X.X.} } [ [ { { F b b v v c c z k k T G.B.f i l o NXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXJX4 O A f D D G F G UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy.r.<.-.p.<.<.1.1.1.2.1.2.2.2.s.x.x.d.c.c.c.Q.^.^.4X4X7X6X0X0XwXyXyXsXsXsXsXsXtXtXwXwXeX0X0X7X7X`.~.`.!.!.'.E.E.z.z.j.j.j.8.8.7.7.,.,.,.,.=.%.o.| } } } } [ [ { F F n v v c c z z l T P.T i i h o BXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXAX2 o f x D G G UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy.<.p.<.<.<.<.1.1.3.2.2.2.d.s.s.x.c.c.c.Q.^.^.^.4X4X7X7X0X0XwXyXyXsXhXsXsXtXtXtXwXeXeX0X7X7X7X`.`.'.'.'.'.E.z.z.z.z.j.j.j.7.7.6.6.,.,.o.=.%.X.X.X.} } [ [ [ { F F F b v c c c k k S V.D l i l o NXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXSX2 O f D f UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX-.<.p.<.<.1.1.1.2.2.2.4.d.s.x.x.c.c.c.c.^.^.^.4X4X7X0X0XwXyXyXyXsXhXtXsXtXtXtXeXeXeXeXeX7X].`.~.'.!.!.!.E.z.z.j.z.8.z.7.7.7.7.6.,.>.>.*.X.X.| } } } } [ [ { { F b v v v c z z k U T j d d l o NXLXLXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXAX2 & : UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX<.<.1.1.1.1.1.2.4.s.s.x.d.l.g.c.v.^.^.4X4X4X4X7X0X0XwXyXyXsXsXhXtXtXtXtXtXeXeXeX7X].7X`.`.`.~.'.!.E.E.z.z.z.z.8.8.8.8.7.7.6.6.>.=.*.*.X.| } } } [ [ [ { F F b b v c c z z k k l d l i l o . NXLXLXLXLXLXPXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXSX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX1.1.1.2.2.2.4.s.s.d.k.x.c.c.c.^.^.^.^.4X4X7X0X0X0XtXyXyXsXsXsXsXtXtXyXyXeXeX7XeX7X7X`.`.`.~.'.!.'.z.z.z.z.8.k.8.8.7.7.7.,.>.,.>.=.X.X.| | } } [ [ [ { F F F v v v c c z k k j l d i l o NXLXLXLXLXPXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXSX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX;.-.2.4.a.s.s.d.x.l.c.c.c.^.^.^.4X4X7X8X0XuXtXyXyXsXsXsXsXtXtXtXtXeXeX0XeXeX7X`.`.`.'.~.!.!.E.E.z.z.k.k.k.j.8.8.7.7.,.,.>.>.=.X.X.$.| } } } [ [ [ F F b b v c c z z z k j l l d l o NXLXLXLXLXLXLXLXPXPXIXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXDX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXN E I a.j.d.x.d.x.x.c.c.^.^.^.4X4X4X0X0X0XwXwXyXyXsXsXsXsXsXyXyXtXeXeXeX8X8X].`.`.`.`.'.'.E.E.z.z.z.z.j.j.j.8.8.6.6.,.,.>.>.%.X.X.X.} } } } [ { { { F F b v v c c z z k j l l d l o NXLXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXDX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXN N N M K y.a.x.x.c.c.c.c.v.^.^.4X4X4X0X7X0XyXyXyXyXsXhXsXsXyXyXyXtXeXeX8X7X8X].`.`.`.!.'.'.E.b.E.z.z.k.j.j.8.8.8.6.6.,.,.>.=.=.%.X.X.| } } [ } [ { F F F b v v c c z z k k j d d l & NXLXLXLXLXLXLXLXLXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXBX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXN M M M N M M K u.i.c.c.Q.^.^.^.4X4X5X7X7XqX0XwXyXyXyXsXsXsXsXyXyXtXeXeXeXeXeX`.`.`.`.`.'.~.E.'.z.z.z.z.z.j.j.k.7.8.6.6.,.,.>.>.=.X.X.| | } } } [ [ { { F F b v v c c z z k k j l d l o NXHXHXHXLXLXLXLXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXBX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXN N N M M M R M M M E e.i.g.^.^.^.4X4X7X7X0XuXwXwXyXyXsXsXsXsXsXsXyXtXeXeXeXeX7X8X].`.`.`.'.!.'.E.E.z.z.z.k.j.j.8.8.6.6.6.,.,.>.>.=.X.X.X.} } } [ [ [ { F F b F n v c c c k k k j j i l o BXHXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXBX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM M M M M M R R R R R R R J _ h.g.T./.7X7X7X0XwXwXyXyXsXsXsXsXsXsXtXtXwXeXeX8X7X].].`.`.`.'.~.'.E.!.z.z.z.k.k.9.j.8.8.7.7.7.,.,.>.>.o.>.X.| X.} } [ [ { { { F F v v v v c c z k k j l d l o BXHXHXLXLXLXLXLXPXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXNX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM N M M M M M R R R R R J J E R A K _ d.g.4X0X0XwXwXyXsXsXsXsXsXsXtXtXtXwXwX0XeXeXeX7X7X`.`.'.'.'.!.E.!.z.z.z.j.k.j.8.8.7.7.7.,.,.>.=.>.%.X.X.X.} } } } { { { F F F v v v c z z z k j j d l & NXHXLXLXHXHXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXNXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM M M M M R M ) R E R E J R J J J E J K K Y u.i.g.5XwXyXsXsXsXsXsXsXtXtXwXwXwX0X8XeX`.7X`.`.`.'.'.!.!.E.z.z.z.k.k.k.j.8.7.7.7.6.,.,.>.>.%.%.o.X.} X.} [ } [ { { F F F v v v c c z k k j l d l o NXHXHXLXLXLXLXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXNXX . UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM M ) M M ) ) M R ) R J E J J J K J K K K K K K K K I h.f.T.5X0XyXdXtXaXsXtXwXwX0X0X7X7X7X7X`.].`.'.'.E.!.v.z.z.z.k.j.j.j.8.8.7.6.6.,.,.>.>.=.%.X.X.} } } } [ [ { { F F F v v v c z z z k j l d j & NXHXHXHXHXLXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXNXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM M ) M C E R R ) R E J E J E J E K E K K K I K I I Y _ I _ _ i.f.R.2X6X6XyXwXiXiXqX0XeX7X7X`.`.`.'.`.!.!.!.!.E.z.z.k.k.j.j.8.7.7.7.6.,.,.=.>.=.X.*.| X.} } [ [ [ { { [ F F F v v v c z z k k j d k o NXHXLXLXHXLXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXmX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC M M M ) R R E ) R E E E E E K E ( K K K ( I I I I I Y Y _ _ _ ` ` ` ` f.1X1X1X3XkX5X6X7X8X8X`.`.`.`.!.!.'.E.E.z.z.k.z.k.k.8.8.8.7.6.6.,.,.>.=.*.*.| | } } } } } { [ { F F F v v v c c c z k j j d j & NXHXHXHXLXHXHXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXmX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC M M C M R ) E E E E E E J ( ( J I K I ( I I I I I I Y Y _ _ _ ` ` ` ` R.R.` m.1X1X1X1X3X2X2X2X2XT.T.~.E.'.~.'.!.E.z.E.k.j.j.8.8.7.6.7.6.,.,.>.=.*.X.| | } } } } } [ [ F F F F F v v n v c c c z l d d o NXHXHXHXHXLXLXLXLXLXLXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXmX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) M ) ) ) ) R ) E E E E ( E E ( E ( ( ( ( I I ( I I Y Y ;._ u._ u.` ` ` R.` m.m.1X1X1X1XmX1X1X1Xm.R.m.R.f.g.i.f.i.i.d.s.d.d.8.j.8.8.7.6.6.6.,.,.,.=.=.=.*.X.%...} } } [ } [ F v v v z x d f d s u - - = = O NXHXHXJXLXHXLXLXLXLXLXLXPXLXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXMX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM M C ) E E Q ) ) E Q ( E ( Q ( ( K ( I ( I I e.I Y Y Y Y _ u.u.u.i.i.f.f.f.R.1X1X1X1X1X3X1X1X1Xm.1X1XR.` 1X` R.` ` ` ` _ _ _ r._ Y I Y Y Y Y H I H H U U D D D D x A A f f f f s s s s s - - - - * * * % * O NXHXHXHXLXHXLXLXLXLXLXLXLXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXmX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) C ) ) ) ) ) E ) ) E E ( E ( ( ( I ( ( ( I I I Y I ;.Y ;.;.u._ ` i.f.i.f.R.f.1XR.1X1X2X3X1X1X1X1Xm.1X` ` 1X` ` ` ` ` _ _ _ _ T _ I Y I K K U U U U U D D A A D f A f f f g s s : s - s s - - - - * - * * * * O NXHXHXHXHXHXHXLXHXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) ) ) ) ) ) E E ( Q ( Q ( ( Q ( ^ e.I I I ;.e.;.;.;.;.u.u.u.i.i.i.f.f.f.f.R.1X1X1X3X1X3X3X1X3X1Xn.1XR.` m.R.` ` R.` T ` _ _ Y _ _ Y I T K U I U U U A D D D A f A f f g s s s s s s s - : : - - - - * * * * O NXHXHXHXLXHXHXLXLXLXLXLXLXLXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXMX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) C ) W W Q E Q Q ! Q ( Q ( ( / I ^ I ^ I e.r.;.I ;.;.;.;.u.u.u.i.i.f.R.f.1XR.R.1X2X2XmX3X3X1X1X1X1XR.1X1XR.1X` R.` ` ` ` _ ` _ _ K Y Y Y Y K I H U U U U U A D D A f f f f f f f s s s : s - u - - - - * * * * O NXGXHXHXHXHXLXHXHXLXLXLXLXLXPXLXPXPXPXIXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXIX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W ) W W W Q Q ! ! ! ^ ^ ( / I ^ e.I e.e.^ ;.e.;.;.;.;.u.-.a.i.i.d.f.f.g.R.T.1X2X2X3X3X3X3X3X1X3X1X1X1XR.R.1XR.f.` R.` _ ` _ _ _ _ _ _ T Y Y I Y U I U U G D D D A A f f f f f s s s s s s : s - - - - - * * * * O NXGXHXHXHXHXHXLXLXHXLXLXLXLXLXPXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W W ) W W Q Q ! / ! ^ ^ ^ ^ ^ ^ I e.e.^ ;.;.;.u.;.<.;.-.1.i.i.i.d.f.g.g.T.R.2XT.2X2X3X3X3X3X3X3X3X2X2X1X1X2XR.R.f.f.R.` R._ ` U._ _ _ _ Y Y I Y U Y K H U U U U D A A D A f f f f f s f s s s s s - - - - - - = * O NXJXHXHXHXHXHXHXHXLXLXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) W W W W ! ! ! / / / ^ ^ ^ ^ e.e.r.e.e.;.;.-.;.-.<.;.;.1.u.d.i.d.i.g.T.T.T.2XT.2X2X3X3X3XgX3X3X3X3X1X2X2XR.R.R.R.f.f.` ` i.i.u._ u.u.Y _ Y Y Y U Y U H K H U D J D D D D f f f f f s f s s s - s - u - - - - * * - O NXGXGXHXHXHXHXLXLXHXLXHXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLX2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) ) W W ! ! ! ! / / ^ ^ e.^ e.e.e.e.e.y.y.y.y.-.-.-.;.3.d.2.u.d.d.d.T.g.T.T.T.2X5X2X3X3X3X3X3X3X3X3X3X2X2X1X1XR.R.f.f.R.R.i.i.` i.` u._ _ Y Y Y Y Y H Y H H H U U G D D D f f D f f s f s f s s s s s - - - - - - * = O NXGXGXHXJXHXHXHXHXLXLXLXLXLXLXPXLXPXLXPXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W W ) W ! ! ! q.n.q.t.h.t.r.t.t.r.r.r.p.r.y.%.p.p.1.1.1.2.d.d.d.i.d.T.T.T.T.T.T.5XkX3X6X3X3XgX3X3X3X3X3X3X2X2X2XT.R.g.T.f.f.f.g.i.i.u.u.u.u.Y Y Y ;.Y 5.H H Y H U G U U D D D x A x f f d s f g d s s s u u - y - - * = O NXGXHXGXHXHXHXHXLXHXHXLXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLX2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) ) W W W ! ! ! q.W t.t.q.t.t.r.r.r.p.r.p.p.p.<.p.a.1.1.1.4.2.d.d.W.d.T.T.Q.T.(.2X3X3X5X3XrX3XgXgX3X3XgX3X3X3X2XT.2XR.R.R.R.g.i.f.i.i.i.u.u.u.u.;.;.Y Y H Y Y H H H U G U D D D D x f x f f f s d s s s u s u u - - - = = O NXGXGXHXGXHXHXHXHXLXLXHXLXLXLXLXLXIXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXJX2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) W W W W W W ! W ! ! q.! t.t.t.t.r.r.r.r.r.r.p.p.1.a.a.2.2.2.4.d.d.d.W.R.T.Q.T.T.5X(.kX6XrX3XrXrXrXrXrXrX3X3X3X3X2X2X2XT.T.T.T.i.g.i.i.i.i.i.d.u.u.u.;.Y ;.Y Y H H H H H H G G D D D D f x f f f f d f s s d s u s - - - = - = O NXGXHXGXHXHXHXHXHXLXLXLXHXLXLXLXLXLXPXIXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLX2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W W W W W W ! t.! ! t.t.t.t.r.t.r.r.r.p.p.p.p.d.a.a.2.a.2.s.s.d.W.k.d.T.T.5X_.(.5X6X5X6XrXrXgXgXgXgXrXrXgX3XrX3XkX5XT.2XT.R.T.R.d.R.f.U.i.i.u.u.5.u.5.Y ;.;.;.H H ;.H H P U G D D D D D x x x f d f d s d s s u s u u u - = = O NXGXGXHXGXHXHXHXHXHXHXHXLXLXLXLXLXLXLXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXGX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) W W W W W ! W t.h.q.t.t.t.r.r.r.r.r.p.+.p.p.a.x.2.2.2.4.9.s.l.d.W.l.Q.!.(.(./.5X6X6X6XrXrXrXgXgXgXgXgXrXrXrX6X3X2XT.2X(.T.T.T.T.T.g.d.i.i.u.i.i.u.u.;.;.;.;. .Y . .Y H H G G U J D x D x x x f f d d d d s s s u u u u - = y O NXGXGXHXHXGXHXHXHXHXLXHXLXLXHXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHX+ . UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) ) W W W ! ! ! t.! t.! t.q.t.r.t.r.r.p.p.p.p.p.p.p.2.s.7.4.j.j.l.l.W.Q.Q.(./._./.6X6X6X9XrXrXgXgXfXgXgXgXrXrXkX6X6X6X5XkXT.T.T.T.T.R.d.R.d.W.i.i.5.u.1.5.u.;.;.5. .5.H . .H H H G G D P D x D x f x f d f d s d s s u u u u - u O NXAXGXGXGXHXGXHXHXHXHXHXHXLXLXLXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXGX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) ) ) W ! W ! t.! ! t.t.t.t.h.t.r.r.r.r.p.p.p.p.p.a.x.2.8.4.j.x.x.z.W.Q.!._.(./._.6X6X6XrXrXpXgXgXfXfXgXgXgXgXrXrXrX6X5XkX5XT._.T.T.W.T.i.d.d.i.d.4.i.i.3.u.<.;.;.;.;.5. . .H H H H G G D D c x x x x j f f d d d d s s i u u y - y O NXGXGXHXHXGXGXHXHXLXHXLXHXLXLXLXLXLXLXPXLXPXLXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXSXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W W W W W t.! W t.t.t.t.t.t.t.r.r.r.p.p.p.p.p.2.2.p.2.4.4.4.j.j.W.W.Q.!.(././.6X4X6XqXqXiXgXgXhXgXhXfXfXpXrXgXrX6XkX6X5X6X5X).T.T.T.T.T.W.T.g.i.W.i.i.1.i.1.;.;.;.5.| ;. . .5. .H H G G G G D D x x x x j f f f d s d i s u u u - u O NXGXGXGXGXGXHXHXGXHXHXHXLXHXLXHXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXJXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) ) W W W W ! ! ! ! t.t.t.t.t.t.0.h.r.r.0.p.p.p.p.a.a.a.7.7.4.9.j.j.k.W.W.!.(./.).).).8X9XqXiXpXpXfXfXhXfXgXhXgXfXgXrX9X6X6X6X6X5X_.2X(.T.T.W.T.d.d.W.d.i.4.d.5.i.3.1.;.;.;.| | H . .H .H H G D G m x x x x j f j f d d s s i i u u - u o NXAXGXGXGXHXHXGXHXHXHXHXLXHXLXHXLXLXPXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXDXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) W ) W W W ! t.! W h.t.t.t.t.t.r.r.q.r.r.p.p.p.p.p.p.7.a.a.x.4.j.x.x.l.W.E.!.!./.).).9X8XqXqXiXpXfXfXdXhXhXfXfXgXpXgXgX6XrX9X6X6X6X5X_.(.(.(.T.T.T.d.d.d.d.4.d.a.5.1.;.3.3.3.;.| 5. . . .H H H G G G D v v x x j x d d d d d d d s i u u y O NXAXGXGXGXGXGXHXHXHXHXHXHXLXLXHXLXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXAXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) W W W W ! W W ! ! q.t.t.t.t.t.r.t.r.r.r.p.p.p.p.p.p.a.a.a.7.x.s.j.x.z.W.E.!.(.~.).).9X9X6XiXiXaXfXfXdXjXjXdXdXfXfXfXpXiXiXrX6X6X6X_._.(._.(.Q.Q.W.W.d.W.W.d.4.d.i.4.1.2.;.3.;.5.| 5. .| .H .P H G G P x D c x x x j x f d d s d i u u u i O NXGXGXGXGXGXHXGXGXHXHXHXHXLXHXLXHXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXDXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX) W W W W W t.! ! ! t.! t.t.t.t.r.r.r.r.r.p.p.p.p.p.x.p.7.7.2.7.j.j.l.W.Q.!.~.~./.).).8X9XqXwXiXaXdXdXdXjXjXdXdXfXfXfXpXiXiXiX9X6X].6X)./.(.(._.Q.T.W.T.d.d.d.i.2.2.3.2.2.3.3.:.=.;.| | . .| H H F G G G G G x x x x j j d j d d i s i i u y o mXGXGXGXGXGXGXGXHXHXHXHXHXHXLXHXLXLXLXLXLXLXPXLXPXLXIXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXBX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W W W q.W q.W t.! t.t.t.t.t.r.r.r.r.r.p.p.p.p.p.p.p.a.2.7.2.7.x.j.k.W.l.E.!.~.(./.).8X6X9XqXiXpXaXfXdXhXjXhXdXdXfXdXaXaXiXiXiX9X9X].).9X).)._.(.Q.Q.W.W.W.W.W.d.4.4.4.2.3.2.3.3.3.X.;.| | . . . .[ F G G D v c c x z x x d j d d d d i u u i o NXAXGXGXGXGXGXGXHXGXGXHXHXLXHXLXLXLXLXLXLXLXLXLXPXPXLXPXPXPXPXPXPXPXLXLXPXLXLXPXLXIXIXPXLXPXIXLXPXLXIXPXIXLXPXLXIXPXLXPXIXPXBX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W W W W W n.W ! t.! t.q.q.q.h.t.r.r.r.r.p.+.p.p.p.2.2.2.a.x.2.j.x.x.l.W.W.!.(././.).8X9X9XqXqXpXaXfXdXhXjXhXhXdXdXfXfXaXaXiXiXqXqX9X9X6X].).(./.~._.W.Q.W.d.W.4.4.d.d.4.4.2.3.3.3.=.*.*.| | | . .[ [ H G F v G G D c c x j x j d l d s i i u i o mXGXGXGXGXGXGXGXGXHXHXHXHXHXHXHXHXLXLXLXLXLXLXPXLXPXPXPXPXPXPXPXPXPXDXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W W W n.W W q.W W t.t.t.h.t.r.t.t.r.r.r.p.p.p.p.a.2.a.2.7.7.7.j.x.k.W.W.W.!.~./.).).8X9X9XqXiXiXfXfXdXhXjXhXdXdXdXfXaXaXiXiXiXqX9X9X9X].].)./.~.(.~.W.W.W.W.W.s.W.s.4.4.4.8.3.3.,.3.=.=.;.| | | . .[ G O.F F v x c x m z x j j j d l d d i u i o mXAXGXGXGXGXGXGXHXGXGXHXHXHXHXHXLXHXHXLXLXLXLXLXLXLXLXPXIXPXPXPXPXPXMX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W W W W W W q.W h.q.t.t.t.t.r.t.r.p.p.r.r.p.p.p.a.p.2.2.2.2.j.j.j.x.W.W.!.Q.!.).).).).8X9XqXiXiXaXfXdXdXhXjXhXhXdXdXfXaXiXiXiXqXqX9X9X9X].].).)./.~.~.~.W.W.W.W.W.k.9.W.4.8.4.4.2.,.3.=.=.:.:.| | . . . .O.G F G F v v c z x z j j j d l i i i i O NXAXGXAXGXGXGXGXGXHXHXHXGXHXLXHXHXLXLXLXLXLXLXPXLXPXLXPXPXPXPXPXPXPXGX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W ! W ! ! ! ! q.t.t.t.r.q.r.q.r.q.r.r.q.b.r.p.p.7.a.7.2.2.7.x.j.j.x.W.W.!.!.~./.).8X8X9X9XqXqXaXaXsXdXdXjXjXdXdXdXdXaXaXaXiXqX9XiX9X9X].].).].`.~.~.~.~.E.E.E.E.W.W.9.8.9.8.8.6.6.,.,.=.=.=.*.X.| | } [ O.[ { { F F v v c x z x k j j d l i d i i & NXAXAXGXGXGXGXGXHXGXGXHXGXHXHXHXHXLXHXLXLXLXLXPXLXLXPXLXPXPXPXPXPXPXPX3 . UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W ! W W ! ! ! / t.t.t.t.q.h.t.r.h.p.p.p.p.r.p.a.a.a.a.a.7.7.7.x.x.x.b.E.E.(.~./.).).).9X6XqXiXpXaXfXdXhXjXjXhXhXdXaXaXaXaXiXiXiXiX9X9X].9X].).].].~.~.~.E.E.E.E.E.E.9.k.k.8.8.8.8.6.6.,.,.>.=.*.X.| } } } .[ { H F F F v G c z z z k j j l d d i i o mXAXAXGXAXGXGXGXGXGXHXGXHXHXHXHXHXHXLXHXLXLXLXLXLXPXLXPXLXPXPXPXPXPXPXSX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW ! W ! t.! ! t./ t.q.t.t.r.r.r.p.r.p.r.p.p.p.p.a.a.p.2.7.2.7.x.j.x.x.z.Q.!.(.~./.).6X8X9XqXqXiXpXaXdXdXhXjXjXjXdXdXdXdXaXaXiXiXiX9X9X9X9X9X].].].~.~.~.~.E.E.E.E.E.9.9.k.9.9.8.8.6.6.6.6.>.>.=.*.X.X.| } } [ [ [ F F F v v v c c z z k k l l l i i l o mXAXGXAXGXAXGXGXGXHXGXHXHXHXHXHXHXLXHXLXLXLXLXLXLXLXPXPXPXPXPXPXPXPXPXPX3 . UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W W ! ! ! ! ! t./ t.h.t.r.t.t.r.r.r.r.r.p.p.p.a.p.a.7.2.x.7.x.x.j.z.z.z.E.!.~././.).].9X9XqXqXiXaXaXdXdXjXjXjXdXdXdXdXaXaXiXiXqX9XiX9X9X9X].].).~.].~.~.~.E.~.E.9.E.E.k.k.k.8.8.8.6.6.6.,.>.>.>.X.X.X.} } } } [ [ [ F F F v v c c z z k j j l d d i l o NXAXAXGXGXAXGXGXGXGXGXGXHXHXHXHXHXHXHXHXLXHXLXLXLXLXLXLXLXPXIXPXPXPXPXPXAX+ . . . . . X . . . . . UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW ! ! W ! ! t.! t.q.t.h.t.t.t.r.r.q.p.p.p.p.p.p.2.2.7.2.7.4.4.j.k.l.W.E.!.!._././.)./.8X9XqXqXpXpXaXfXdXdXjXjXdXdXdXdXaXaXaXaXiXiX9X9X9X9X].].].).).~.~.~.~.E.E.E.E.9.9.k.9.9.9.9.8.6.6.,.,.>.>.%.%.%.X.| } } } [ { { { F F b v v c z z z k j l l l i l o mXAXAXAXGXAXGXGXGXGXHXGXHXHXHXHXHXHXLXHXLXHXLXLXLXLXLXLXLXPXPXLXPXPXPXPXPX4 . O O O O O O O O O O O o O O O O O o O O + O O X . . . . . UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW W ! ! t.! t.! t.t.r.q.r.r.r.r.p.p.p.p.p.p.p.p.2.2.p.2.x.9.j.j.x.z.l.W.Q.!./././.4X8X9X9XqXqXpXaXaXdXdXjXjXhXjXdXdXaXaXaXiXiXiXiXiX9X9X9X9X].).).).~.~.E.E.E.E.E.E.E.E.k.k.9.8.8.8.6.6.6.,.,.>.>.=.%.X.} } } } [ [ { { F F b v v c c z z k j j l i i l o NXAXAXGXAXGXAXGXGXGXGXHXGXGXHXHXHXHXHXLXLXLXHXSXLXLXLXPXLXPXPXPXPXPXPXPXPXBXX & > : : : : : : : : : : : : : : : : : : : : : : UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! ! ! W ! t.t.t.t.t.q.r.q.r.r.r.p.p.p.p.p.p.a.p.2.7.x.7.j.s.x.k.W.W.!.!.!.~.).).8X9X9XqXqXpXpXaXdXdXdXjXjXjXdXdXdXaXaXiXiXiXiX9XiX9X9X9X].].]._.).).~.~.~.E.E.E.E.E.9.9.k.9.9.8.6.6.6.,.,.,.=.>.%.%.o.| } } } [ [ [ F F b b v v c c z k k k j l l d i o mXAXAXAXGXAXGXGXGXGXGXHXHXHXGXHXHXHXHXHXLXHXmX2 DXPXLXPXPXLXLXPXPXPXPXPXPXPX4 > : : > : : : : : : : : N : N : : : : : : : : UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXW t.! t.t.t.t.q.h.t.r.r.r.h.r.r.p.+.p.p.p.x.p.a.x.7.a.2.7.j.j.z.l.l.E.Q.!.(./.).8X).8X9XqXiXpXaXaXfXdXjXjXjXjXdXdXdXaXaXaXiXiXiXiX9X9X9X].].].).).~.~.~.~.~.E.E.E.9.k.E.k.k.9.8.8.8.6.6.,.,.>.>.=.%.%.X.| } } } [ [ { F F F b b v c c z z k j j l l i l o mXAXAXAXAXGXAXGXGXGXGXGXGXGXHXGXHXHXHXLXHXmX. 3 PXLXLXLXLXPXPXLXPXPXPXPXPXDXX @ : : : : : : g g g N : g : N N : N N : N N : UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! t.t.t.t.t.t.t.r.r.r.r.r.r.p.r.p.p.p.a.a.p.2.2.2.4.4.j.x.x.z.z.W.W.!.~././.).).9X9XqXqXiXaXaXaXdXdXjXjXjXdXdXdXaXdXaXaXiXiXiXiX9X].9X9X].].].~.].~.~.~.E.E.E.E.E.W.6.W.k.8.8.8.8.6.6.6.6.>.>.=.%.%.X.| } } } [ [ { { F F b v v v c c z z j j l l d i & mXAXAXGXAXGXGXAXGXGXGXHXGXGXHXGXHXHXLXLXMXX + DXPXLXLXPXLXPXPXPXPXPXPXPXPX2 . > : : g : : g g g g g g g N g N g g N g N N UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! t.W t.t.t.t.t.r.t.r.t.r.r.r.p.p.p.p.a.a.2.2.2.s.4.4.j.j.k.k.W.W.Q.!.~././.).6X8X6X9XqXiXiXaXaXdXdXdXjXjXjXdXdXdXaXaXaXiXiXqXiX9XiX9X9X].].].].].~.~.~.~.~.E.E.E.E.E.6.W.k.9.8.8.8.6.6.,.>.>.>.>.:.%.} | } } } [ [ [ { F F b b v c c z z k k j l d i l & mXAXAXAXAXAXAXGXAXGXGXGXGXHXHXHXHXLXJXMXX 3 PXLXLXPXPXLXPXLXPXPXPXPXPXNXX . > : : : g g g g f f g m g g m g N m M N g N UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX! ! t.t.t.t.t.h.t.r.t.r.r.p.p.p.p.p.p.x.a.2.2.2.7.8.4.j.k.k.l.W.E.!.!.~./.).).).].9XqXiXiXaXaXdXdXjXjXjXjXdXdXdXaXaXaXaXiXiXiXiX9X9X9X].].].].].~.].E.~.E.E.~.E.E.E.E.E.6.9.9.8.8.6.6.6.6.,.>.>.=.:.X.X.} } } } [ [ [ { F F b b v v c c z z k j j l d l o mXAXAXAXAXAXAXGXGXGXGXGXGXGXGXHXHXHXMX + DXPXLXLXLXLXLXPXPXPXPXPXPXLX+ . : : A g g f f f R m m m m M m m M M m m M UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt.! t.t.t.t.t.q.r.r.r.r.p.0.p.p.p.p.p.p.p.2.2.2.4.s.j.d.k.l.W.W.Q.!.(././.).).9X9X9XqXiXiXaXaXdXdXdXjXjXjXdXdXdXdXaXaXiXiXiXqX9X9X].9X9X].].~.].~.].~.~.E.E.E.E.E.E.6.E.E.9.8.8.8.8.6.6.6.,.>.>.=.:.X.X.| } } } } [ { { F F b b v c v z z k j j j l d l o mXSXAXAXAXAXAXAXAXGXGXGXGXGXGXHXHX3 X . MXPXLXLXPXPXLXPXPXPXPXPXPXPXNX. > g : g A f J m x J m J m m m m m m Z M M UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt.t.t.t.q.t.t.r.r.r.r.p.p.p.p.p.p.p.a.a.a.2.a.7.8.j.k.s.k.W.W.W.!.!.~././.].].9X9X9XiXiXiXaXaXdXdXjXjXdXjXdXdXdXaXaXaXaXiXiXiX9XiX9X9X].].].].~.~.~.~.~.~.E.E.E.E.W.E.6.E.k.8.8.8.6.6.6.6.,.>.>.=.:.%.X.| | } } } [ [ { { F b b b v c c z k k k j l d l o mXAXAXAXAXAXGXGXAXGXGXGXGXGXJXJXMX + DXPXLXLXLXPXLXLXPXPXPXPXPXPX+ X s g g f f m m J m P P m P P C ~ m m C m UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt.t.q.t.h.r.r.t.q.r.r.p.r.p.p.p.p.a.2.a.a.2.7.8.s.4.x.W.k.W.E.!.!._././.).].].9XiXiXiXiXaXaXdXdXdXjXjXjXdXdXdXaXdXaXaXiXiXiXqXiX].9X9X].].].].~.].~.E.~.E.E.E.E.E.6.E.E.6.9.9.8.8.6.6.6.,.,.,.>.=.%.X.X.} } } } [ [ [ { F F F b v v c c z z k k j l l l o mXAXAXAXAXAXAXAXAXGXAXGXGXJXGX3 . 3 IXLXLXPXLXPXPXPXPXPXPXPXPXmXX > g f g m x D P P P P L P P P L m C m UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXh.q.t.r.r.r.q.g.p.q.p.r.p.p.p.p.2.2.2.7.2.8.2.j.j.x.k.W.Q.W.!.!././.4X8X_.iX9X9XiXiXaXaXaXdXjXjXjXjXdXdXdXdXaXaXaXiXiXiXqXqX9X9X9X].].].].].~.].~.[.E.[.E.E.E.E.W.E.W.6.9.8.8.8.8.6.6.,.,.,.>.=.>.X.X.} } } } [ [ [ { F F b b v v v c z k k k j l d l o mXSXSXAXAXAXAXAXAXGXGXGXGXGX3 + DXPXLXLXPXLXLXPXPXPXPXPXPXHX+ O g g f A D m D P P P P P ~ m C P ~ P UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.t.t.r.q.r.r.r.p.h.p.p.p.p.2.2.p.2.a.2.8.2.j.j.j.l.z.W.Q.!.!.(./.).).).9X9X9X9XiXiXaXaXdXdXdXjXjXjXdXdXdXdXdXaXaXiXiXiXqXqX9X9X9X].].].~.].~.~.[.E.E.E.E.E.E.E.6.E.6.W.9.8.8.8.6.6.,.,.,.>.>.%.%.X.X.} } } } [ [ [ { { F F b n c v v z z k k k l d k o mXSXSXAXAXAXAXAXAXGXGXGXGX3 . MXPXLXLXLXPXPXPXIXPXPXPXPXPX3 . . > g f f x G m P P P L L L L L L ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXn.q.h.r.r.p.r.p.p.p.p.x.p.a.a.p.7.7.7.7.x.j.x.k.x.z.!.Q.!.~.~./.).).9X9X6XqXqXiXpXaXaXdXdXhXjXjXhXdXdXdXdXaXaXiXiXiXiXiX9X9X9X].].].].].~.].~.~.~.~.E.E.E.E.E.E.6.E.6.W.6.E.6.6.6.6.,.,.>.>.=.%.X.X.X.| } } [ [ [ [ { F F b b v c c c z z k j l d l o mXAXAXAXAXAXAXAXGXAXGXGX4 . . + SXPXLXLXPXPXLXPXPXPXPXPXPXLXX X f f f D D P P F P L P @.L L L ~ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.q.r.r.r.r.r.p.0.p.p.p.a.a.2.2.a.j.7.s.j.x.k.z.Q.z.Q.~.~.).).).).8X9X9XqXiXpXaXaXdXdXjXjXjXjXdXdXdXaXaXaXaXaXiXiXiX9X9X9X9X9X].].].~.].~.~.~.~.E.[.E.E.E.E.E.6.6.E.E.6.6.6.6.6.6.,.,.>.>.>.=.X.X.} } } } [ [ [ { { F F b b v v c z z k k j l l k o mXSXSXSXSXAXAXAXAXGXAX4 . X . MXPXLXLXLXLXPXPXLXPXPXPXPXPX3 . : g f x D m P G P O.P @.L L L UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.r.r.r.r.r.p.p.p.p.2.p.a.x.a.j.7.2.x.j.x.x.l.z.W.!.!.~././.).).9X8X9XqXqXiXsXaXfXdXjXdXjXjXhXhXdXdXdXaXaXaXiXiXiXiXiX9X9X9X9X].].].].~.].~.~.~.E.E.E.E.E.E.E.W.6.W.6.6.6.6.6.6.6.,.>.>.>.%.%.X.X.X.X.} } } [ [ { { F F b v v v c c z z k k l d j & mXAXSXAXSXAXAXAXGXAX2 . o & X + DXPXLXPXLXPXPXPXPXPXPXPXPXGX+ O g f A x D D P @.P L @.@.P UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr.r.r.p.p.p.p.p.x.p.2.p.2.2.2.7.j.j.x.x.x.W.W.!.!.~./././.4X8X9X9XqXqXiXpXaXaXaXdXdXjXjXdXhXdXdXdXdXaXaXaXiXiX9XiX9X9X9X9X].].]._.].~.~._.~.E.~.E.E.E.E.9.E.6.E.6.W.6.E.6.6.6.6.,.6.>.>.=.%.X.X.X.} } } [ [ [ [ F F F b b v v c c z z k j j d j o mXSXAXSXAXAXAXAXAX2 . o % % & . . MXPXLXLXPXLXLXPXPXPXPXPXPXPX2 . . : f f x m G P G O.O.@.O.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXp.r.p.p.p.p.a.p.2.2.7.a.7.7.8.j.s.x.l.z.l.!.!.!.(.~.).).].8X8X9XqXqXiXpXaXfXdXdXjXjXjXjXdXdXdXaXaXaXaXiXiXiXiX9XiX].iX].].].].].].~.~.~.~.~.~.E.E.E.E.E.6.E.6.E.6.W.W.6.6.6.6.6.>.>.>.=.%.X.X.| } } } [ } [ { F { F b b v v c c c z k k j d j o mXAXAXSXAXSXGXAX2 . O & + ; % X . . 2 SXPXPXLXPXPXLXLXPXPXPXPXPXSX. O A f A D c P G H O.L UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXp.p.p.p.a.a.2.2.2.a.a.x.7.8.j.k.k.l.W.Q.E.Q.~././.).).8X9X6XqXqXiXiXaXaXfXdXdXjXjXjXjXdXdXdXdXaXaXaXaXiXiX9XiX9XiX].9X].].].].~.].~.~.E.~.E.E.E.E.E.E.E.6.E.6.W.6.6.6.6.6.6.,.,.,.>.=.:.%.X.| } } } } [ [ [ { F F F b v v c c z z k k j d k o mXSXSXAXSXAXAX+ o % % ; % % & . mXPXLXLXLXLXPXPXPXPXPXPXPXPX2 : f f f D P F P L UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXa.p.p.2.a.a.2.a.2.x.a.7.x.x.k.W.W.W.E.!.~.~././.).).8X9XqXqXqXpXiXaXfXdXdXhXjXjXjXjXdXdXdXaXaXaXaXiXiXiX9X9X9X].].].].].].].~.~.~.~.~.~.E.E.E.E.6.6.E.E.6.E.6.6.6.6.6.6.,.,.>.>.>.%.:.X.X.} | } } [ } [ { { F F F b b v v c c z z k k l k & mXAXAXSXAXSX2 o % % % % % ; % X + SXPXLXPXLXPXPXLXLXPXPXPXPXAX. > f A f D D F G UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXa.p.p.1.a.7.7.7.7.x.x.x.k.l.W.Q.!.!.~./.).).).8X8X9XqXqXiXpXiXaXdXdXdXhXjXjXdXdXdXdXaXdXaXaXaXiXiXiXiX9X].iX].].].].]._.].~.~.~.~.E.[.E.E.E.E.E.W.6.W.6.W.6.W.6.6.6.6.,.,.>.>.=.>.X.X.X.| } } } [ [ [ { F F F b b v v c z z z k j d k o mXSXSXAXSX+ & & % % ; ; ; ; ; & . . MXLXLXLXLXPXLXPXPXPXPXPXPXPX2 . : f f D m D UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXa.2.2.s.7.2.4.4.j.x.k.W.W.W.Q.!.~.~./.).).9X8X9X9XqXqXiXpXaXaXdXdXhXhXjXhXhXdXdXdXaXaXaXaXiXiXiXiXiX].iX].9X].].].].].~.].E.~.E.~.E.E.E.E.E.9.9.z.k.k.6.6.6.6.6.6.6.,.,.,.,.=.:.X.X.} } } } } [ [ { [ F F F F v v v c c z k k k d z o mXSXGXSX+ o & % % % + ; ; ; ; a X . 2 AXPXLXIXPXLXPXPXPXPXPXPXPXBX. > f f D f UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2.s.2.2.8.9.j.j.x.l.z.W.W.!.!.~./.)./.4X).9X9XqXqXqXpXpXaXdXdXdXhXjXjXhXhXdXdXaXdXaXaXaXiXiX9XiX9XiXiX].].9X].].`.].].~.~.~.~.E.E.E.E.E.E.z.z.9.8.8.W.6.W.6.6.6.6.,.,.,.=.=.:.X.X.X.| } } [ } [ [ F F F F F b v v c c z z k k l k o mXAXDX+ . o & & % % ; ; ; ; a ; a + . . NXPXLXLXLXPXLXPXPXLXPXPXPXIX+ f g UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7.s.4.j.x.j.k.W.W.W.!.!.(.~././.).).9X9X9XqXiXiXiXaXaXdXdXhXjXjXhXhXdXdXaXdXaXaXaXiXiXiXiX9XiX].].9X].].].~.~.].~._.~.~.~.~.E.E.9.E.9.z.9.9.9.k.6.6.6.6.6.6.,.,.,.>.=.=.%.X.X.X.X.} } } [ [ [ [ F F F v b v v c c z z k j d k & NXDX+ o & % % % * ; ; ; ; a ; ; ; X 2 AXPXLXPXLXPXPXPXPXPXPXPXPXBX O UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX9.s.k.l.l.z.W.!.Q.~./././.).8X].9X6X9XqXiXiXaXfXsXdXdXjXjXjXhXhXdXdXdXdXaXaXaXiXiXiXiX9X9X9X9X].].].].].].~.~.~.~.E.E.E.E.E.E.E.z.z.9.z.9.8.E.W.6.6.6.6.,.,.,.>.,.o.X.X.X.} } } } [ } [ { { F F F F b v v c c c z x k j z o MX+ & & & & % % * ; ; ; a ; a a a % mXPXLXLXLXPXLXLXIXPXPXPXPXPX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXj.x.k.W.W.!.!.!./././.).).).9X6X9XqXiXqXsXaXaXdXdXjXjXjXjXhXhXdXdXaXaXaXaXaXaXiXiXiX].iX9XiX].].].].].~.~.~.~.~.~.E.E.E.E.9.9.E.9.9.9.k.8.6.6.6.6.6.6.6.,.=.>.>.=.%.%.X.X.| } } [ } [ { { { F F v v v v v c z z z j j k o . & & & & % % % * ; ; = a a a a a ; o . . . 2 DXLXLXPXLXPXPXPXPXPXPXPXPXBX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXz.W.!.W.!.~.~././.).8X8X9X9XqXqXiXpXaXaXdXdXdXdXjXjXhXhXdXdXdXdXaXaXaXiXiXiXiXiX9X].9X].].].].].].].~.~.~.~.~.E.E.E.E.E.E.E.9.E.9.8.k.6.W.6.W.6.6.,.,.=.,.>.=.%.X.X.} X.} } } } [ [ { { F F F F v v c c z c k j j k & & & & & % % * * * ; ; = a a a a a a % . . . NXPXLXLXLXLXPXLXPXPXPXPXPXJX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX!.!.!./././.).).).8X8X9XqXqXiXpXaXaXaXdXdXjXjXjXhXhXdXaXdXaXaXaXiXiXiXiXiXiX9X9X9X].].].].].].].~.~.~.~.~.~.E.E.E.9.E.9.z.9.9.k.8.6.6.6.6.6.6.,.,.,.,.=.>.=.%.X.X.} } } [ [ [ [ { { { F F F b v v c c z z j f z & . & & & % % % % * * = a a ; a a a a a B ; X . 4 JXPXPXLXPXLXPXPXPXPXPXPXPXMX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX~./.).).4X9X9X9XqXqXiXpXaXaXdXdXdXdXjXjXjXjXdXdXdXaXdXaXaXaXaXiXiX].].iX9X9X9X].].].~.].~.~.~.~.~.E.~.E.E.E.E.z.E.9.k.k.9.8.E.6.6.6.6.6.6.,.,.=.=.o.%.X.X.X.X.} } } } [ [ { { F F F v v v v c c z z k j k o . & & & % % % % % * = = ; a a a a a a p a a & . NXPXLXLXLXPXLXPXLXPXPXPXPXPX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX).4X8X8X9XqXqXqXqXpXyXyXdXdXhXhXhXhXhXhXdXdXdXdXaXaXaXaXiXiXiX].9X9X].9X].].].].].~.~.].~.~.~.E.~.E.E.W.W.9.W.9.k.k.8.k.9.9.8.6.6.6.,.,.>.>.>.>.:.X.X.| } } } } } [ [ [ { F F F b n v v v c z z k j k o . & % & % % % * * * * = = = y y a y a p a a p ; X 2 AXPXLXLXPXLXPXLXPXPXPXPXPXmX. UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX9X9XqXqXiXpXsXfXhXdXdXhXjXjXhXhXhXdXdXaXaXaXaXiXiXiXiXiXiXiX9X9X].].9X].].`.~.~.~.`.[.~.E.E.E.E.E.W.W.k.9.8.k.8.8.6.6.8.8.6.6.6.,.>.>.>.=.:.%.X.| X.} } O.[ [ [ { { F F b b v v v c c z z x d k & . % & % % % * % * * * * = = y y y y p p a p p p B % . NXLXLXLXLXPXLXPXPXPXPXPXPXHXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXwXqXpXpXaXsXhXdXdXhXjXhXhXhXdXdXaXdXaXaXaXaXiXiXiXiX].9X9X9X].].9X].].].~.].~.~.E.E.~.E.E.E.E.k.E.k.k.k.8.8.k.9.6.6.6.6.6.,.,.,.>.>.X.%.X.X.} } } } } } [ [ { F F F F F v b v c c z z k j v o . & % % % % % * * * * * = = y y a a p y p p p p a a ; O 4 GXPXLXPXLXPXPXPXPXPXPXPXPX4 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXpXfXhXjXdXhXjXjXjXhXhXdXdXdXaXaXaXaXaXqXiX9XiXiX9X9X9X9X].].].].].].~.~.~.~.~.E.E.E.E.E.E.k.E.k.k.k.k.8.9.9.6.6.6.,.,.,.,.>.>.=.:.%.X.| X.} } .} [ [ { { { F F F b v v v c c z x j z & X & % % % % * * * * * * = = = = = y y y y p a p p a a a & DXPXLXLXLXLXLXPXIXPXPXPXPXMX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXdXhXjXjXjXdXdXdXdXaXaXaXaXaXiXaXiX].iX].9X].].].].].].].~.~.~.~.~.E.~.~.E.E.W.W.9.E.k.9.9.9.8.8.6.6.9.6.6.,.,.,.>.>.>.=.:.%.X.X.} } } } } [ [ { { F F F F b v v c c c z k j z o X & % % % > % * * * = = = = = = y y y y a a p p p p a p B ; X 4 JXLXLXPXPXPXLXLXPXPXPXPX3 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXjXjXdXdXdXaXdXdXaXaXpXiXiXiXiXiX].].iXiX].].].].~.~.].].~.~.~.E.E.E.E.z.W.E.9.E.9.k.9.k.8.9.6.6.6.6.,.,.,.,.>.=.=.:.X.X.} $.| } } [ } [ [ { { F F b b v v v c c z x j v o O % * > * > * * * - = = = = y = = y y y y a a a p p p a a UXUX BXLXPXLXLXLXPXPXPXPXPXPX+ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXdXdXdXaXaXaXiXiXiXiXiX].iXiX9X].].].].].].~.].~.~.~.W.~.~.E.E.E.z.k.k.9.k.9.k.8.8.6.8.6.6.6.,.,.>.>.>.>.%.:.X.X.| | } } } .[ [ [ { { F F b b b v v c c z k j z o O % > * % > * * * * = = = = y = = = = y y y a a p p UXUXUXUXUXUXUX 4 GXLXLXLXLXPXLXPXPXLXMX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXaXiXiXiXiX9XiX].].].9X].].~.].~.]._.~.~.~.~.W.W.E.E.z.E.E.k.k.k.9.9.8.j.9.9.6.8.6.6.,.6.>.>.>.=.:.X.X.| | } } [ [ } [ [ { { F F F b b v c c c x z j c & O > - * > * * - * - - = = y y = y y y y = y UXUXUXUXUXUXUXUXUXUXUXUX X DXPXLXPXLXPXPXPXJX3 . UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXiX9X9X9X9X].].].].`.`.~.~.~.~.!.!.~.E.E.W.c.l.z.z.k.j.j.j.9.9.8.6.6.6.6.6.,.,.,.>.=.%.=.X.X.| } } } } } } { { { { F F F b v v c n c c k x c & O - > - * > * - - - = - y - y y y y UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX . 4 SXJXHXHXGXAXBXX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX9X].].].].).~.~.~.!.!.~.W.E.E.c.v.k.k.k.j.j.8.9.9.9.8.8.7.6.6.,.,.>.>.>.=.%.X.X.| | | } } [ [ [ [ { { F F F b v v n c c x k k z o O - - - - - - - - * - - = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX X 4 3 3 4 X UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX~.~.^.!.^.uXE.d.d.g.k.k.x.z.k.9.s.9.8.8.8.7.6.6.,.,.>.=.=.=.%.%.| | X.} } } } [ [ [ { { { P b b b n v v x z x j c & o u s - - > * % % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXV.S N.N.T U.5.i.5.5.W.,.5.,.5.3.=.5.=.=.=.=.=.*.X.| | } .} .} .G H K G G Q G D D A f A A A : A c O . UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX# 6 , , , 6 # , < 9 < < 9 S S S S 9 S S S T S S T S S S S S S S S < < 4 < < < < , , , , # # # # # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX, 6 6 6 6 6 6 5 5 5 5 9 5 5 9 5 5 < 5 5 5 , , , , , , , , # 6 # , , , 6 # # , , # , # # # # # # # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX6 8 8 6 6 6 5 5 9 5 9 9 9 9 < 9 9 9 9 < 5 5 5 5 5 , 5 , , , 4 5 , , , 6 # t 4 , 6 # # t # # # # # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 6 4 8 9 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 5 9 5 5 5 5 5 6 5 6 6 6 6 6 6 6 6 , 6 6 6 t # # # t 6 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 8 8 8 8 9 4 0 0 8 0 9 9 0 9 9 9 M.9 9 9 9 9 9 9 9 9 9 9 6 9 6 8 6 6 6 6 6 6 6 6 8 6 6 t t t t t 8 t UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 8 0 0 0 0 0 0 0 0 0 0 0 0 M.M.M.9 0 0 M.9 9 0 9 9 9 0 9 0 8 9 8 4 0 6 6 0 6 6 8 4 6 8 6 t 8 t t t 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7 8 0 0 e 0 e 0 0 0 M.0 0 M.M.M.M.0 M.0 M.0 M.0 0 0 0 0 0 0 0 9 8 0 8 8 0 3 q 8 8 8 8 6 6 8 8 t 7 8 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7 0 7 e e e 0 e 0 C.C.0 M.C.0 M.M.M.M.M.M.M.0 M.M.0 M.0 0 0 0 0 0 0 0 0 8 0 8 8 e 8 8 8 8 6 8 8 6 8 t 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q 0 q e e e e C.C.e C.C.C.C.C.C.C.C.C.C.M.M.M.C.M.0 0 0 0 e 0 e 0 e 0 0 q q 3 e e 8 8 e 8 8 8 8 8 8 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q e e w C.w C.S.C.C.C.C.C.C.F.C.F.C.F.C.C.C.C.C.C.C.C.e e C.e e e M.e e w q w 8 e 8 e 8 8 7 7 7 8 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q w w r S.S.S.C.C.C.S.Z.Z.Z.F.Z.Z.F.C.Z.C.C.C.C.C.C.C.C.C.C.e M.S.e e e w e w e w 8 e 8 7 7 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq w w r S.S.S.D.D.Z.Z.Z.Z.Z.Z.H.Z.P.H.H.P.P.Z.Z.Z.C.Z.C.D.S.C.S.S.S.C.nXe w w w w w w e w 7 7 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7 q q w w S.S.S.D.D.D.Z.A.P.H.P.H.H.L.L.G.P.H.P.P.A.Z.Z.D.D.D.D.S.S.S.S.S.S.w w r r w w w q q 7 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7 q q w r S.S.S.D.D.Z.A.H.A.H.G.J.J.J.J.L.H.P.H.H.A.P.A.A.A.D.D.D.D.I.S.S.r r r r r w w w e q q 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX7 q q r r S.S.S.D.D.A.A.A.A.I.H.H.J.J.J.J.J.H.H.H.H.A.A.A.D.D.D.D.S.r r S.S.r r r r w w q q q 7 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q q w w S.S.D.D.D.D.A.A.H.H.H.J.J.J.J.J.G.J.H.H.A.P.A.A.A.A.D.D.D.S.S.S.S.r r r r w w w e q q q 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq e w r S.S.D.D.D.A.A.A.H.P.H.K.J.L.J.J.J.H.P.H.A.H.A.A.A.D.D.D.D.r r r S.r r r r w q w 8 q 7 7 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq w r S.S.S.D.D.D.A.H.A.A.H.H.K.J.J.J.J.J.H.H.H.A.A.A.A.A.D.D.D.r r S.S.r r r w w w w w q q 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw S.S.S.D.D.D.A.A.A.H.H.J.J.J.J.J.J.H.J.H.H.H.H.A.A.A.D.D.D.S.r r r r r r w w w w q q q 7 7 7 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXr S.S.S.D.D.A.A.A.A.A.J.H.J.J.J.J.J.H.H.H.I.A.A.D.A.D.D.D.D.r nXr r r r w w w w w q q q q 7 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS.D.D.D.D.A.A.H.A.H.J.J.K.J.J.H.H.J.H.A.H.A.A.D.A.D.D.S.D.S.S.S.r r r r r w w q q q 7 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXD.D.D.A.A.H.H.J.H.J.J.J.J.J.L.H.H.H.A.A.A.A.D.D.D.D.S.S.S.S.S.r r r w w e w q UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXA.H.H.H.P.J.J.K.J.J.P.G.H.H.H.P.A.A.A.A.D.D.D.D.S.S.S.S.r r r w w UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXH.H.K.J.J.K.H.J.I.H.A.A.A.A.A.D.D.D.D.D.S.S.S.r S.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXJ.H.H.H.A.A.A.D.I.D.D.UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX" }; ================================================ FILE: JBTEditor/jbt.tools.bteditor/plugin.xml ================================================ ================================================ FILE: JBTEditor/jbt.tools.bteditor/plugin_customization.ini ================================================ org.eclipse.ui/SHOW_TRADITIONAL_STYLE_TABS=false org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP = false ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/Activator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle */ public class Activator extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "jbt.tools.bteditor"; // The shared instance private static Activator plugin; /** * The constructor */ public Activator() { } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { super.start(context); plugin = this; } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context); } /** * Returns the shared instance * * @return the shared instance */ public static Activator getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given * plug-in relative path * * @param path the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/Application.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import jbt.tools.bteditor.util.StandardDialogs; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; /** * This class controls all aspects of the application's execution */ public class Application implements IApplication { public static final String PLUGIN_ID = "jbt.tools.bteditor"; /* * (non-Javadoc) * * @seeorg.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app. * IApplicationContext) */ public Object start(IApplicationContext context) throws Exception { Display display = PlatformUI.createDisplay(); /* * Before doing anything at all, standard nodes must be loaded. If they * cannot be loaded, the application will not start. */ try { NodesLoader.loadStandardNodes(); ApplicationIcons.loadIcons(); } catch (Exception e) { StandardDialogs .exceptionDialog( "Could not start application", "There were errors while loading the set of standard nodes", e); return IApplication.EXIT_OK; } try { int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor()); if (returnCode == PlatformUI.RETURN_RESTART) return IApplication.EXIT_RESTART; else return IApplication.EXIT_OK; } finally { /* Dispose loaded icons. */ ApplicationIcons.disposeIcons(); display.dispose(); } } /* * (non-Javadoc) * * @see org.eclipse.equinox.app.IApplication#stop() */ public void stop() { final IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench == null) return; final Display display = workbench.getDisplay(); display.syncExec(new Runnable() { public void run() { if (!display.isDisposed()) workbench.close(); } }); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/ApplicationActionBarAdvisor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import jbt.tools.bteditor.actions.DialogExportAsCppAction; import jbt.tools.bteditor.actions.DialogLoadMMPMDomainAction; import jbt.tools.bteditor.actions.DialogOpenBTAction; import jbt.tools.bteditor.actions.NewBTAction; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.ICoolBarManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.ContributionItemFactory; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer; public class ApplicationActionBarAdvisor extends ActionBarAdvisor { private IWorkbenchWindow window; private IWorkbenchAction openBTAction; private Action newBTAction; private IWorkbenchAction saveBTAction; private IWorkbenchAction saveBTAsAction; private IWorkbenchAction loadMMPMDomainAction; private IWorkbenchAction aboutAction; private IWorkbenchAction exportAsCppAction; private IContributionItem viewsList; public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) { super(configurer); } protected void makeActions(IWorkbenchWindow window) { this.window = window; this.saveBTAction = ActionFactory.SAVE.create(this.window); this.saveBTAsAction = ActionFactory.SAVE_AS.create(this.window); this.exportAsCppAction = new DialogExportAsCppAction(this.window); this.openBTAction = new DialogOpenBTAction(this.window); this.newBTAction = new NewBTAction(); this.loadMMPMDomainAction = new DialogLoadMMPMDomainAction(this.window); this.viewsList = ContributionItemFactory.VIEWS_SHORTLIST.create(this.window); this.aboutAction = ActionFactory.ABOUT.create(this.window); /* For key bindings and for copy and paste actions... */ this.register(this.saveBTAction); this.register(ActionFactory.COPY.create(this.window)); this.register(ActionFactory.PASTE.create(this.window)); } protected void fillMenuBar(IMenuManager menuBar) { MenuManager fileMenu = new MenuManager("File"); MenuManager viewMenu = new MenuManager("View"); MenuManager helpMenu = new MenuManager("Help"); MenuManager showViewMenu = new MenuManager("Show view"); fileMenu.add(this.newBTAction); fileMenu.add(this.openBTAction); fileMenu.add(this.saveBTAction); fileMenu.add(this.saveBTAsAction); fileMenu.add(this.exportAsCppAction); fileMenu.add(this.loadMMPMDomainAction); showViewMenu.add(this.viewsList); viewMenu.add(showViewMenu); helpMenu.add(this.aboutAction); menuBar.add(fileMenu); menuBar.add(viewMenu); menuBar.add(helpMenu); } protected void fillCoolBar(ICoolBarManager coolBar) { IToolBarManager firstCoolBar = new ToolBarManager(coolBar.getStyle()); coolBar.add(firstCoolBar); firstCoolBar.add(this.newBTAction); firstCoolBar.add(this.saveBTAction); firstCoolBar.add(this.saveBTAsAction); firstCoolBar.add(this.exportAsCppAction); firstCoolBar.add(this.openBTAction); firstCoolBar.add(this.loadMMPMDomainAction); coolBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/ApplicationIcons.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Vector; import jbt.tools.bteditor.util.IconsPaths; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; /** * Class used to manage common icons in the application. These include icons * for standard nodes (those read form the * {@link NodesLoader#STANDARD_NODES_FILE} file), as well for "actions", * "conditions", "categories" and "guards" (whose path can be accessed via * {@link IconsPaths}). *

* They can be retrieved by their path in the plugin, and they are * automatically disposed by the application. * * @author Ricardo Juan Palma Durán * */ public class ApplicationIcons { /** List of the icons. Can be retrieved by their path in the plugin. */ private static Map applicationIcons = new Hashtable(); /** * Returns an icon by its path in the plugin, or null if it cannot be * found. *

* The returned image does not have to be disposed. It is automatically * managed by the application. *

* This function should be called after loading the icons ( * {@link #loadIcons()}). */ public static Image getIcon(String iconLocation) { return applicationIcons.get(iconLocation); } /** * Loads all the common standard icons. * * @throws IOException * in case there is an error while loading the icons. */ public static void loadIcons() throws IOException { URL url = FileLocator.find(Activator.getDefault().getBundle(), new Path( NodesLoader.STANDARD_NODES_FILE), Collections.EMPTY_MAP); URL fileUrl = null; fileUrl = FileLocator.toFileURL(url); FileInputStream file = new FileInputStream(fileUrl.getPath()); /* First get icons from the standard nodes file. */ parseStandardNodesFile(file); /* Then non standard icons. */ loadNonStandardIcons(); } private static List parseStandardNodesFile(FileInputStream file) { List exceptions = new Vector(); SAXBuilder builder = new SAXBuilder(); try { Document doc = builder.build(file); Element root = doc.getRootElement(); parseElement(root); } catch (Exception e) { exceptions.add(e); } return exceptions; } private static void parseElement(Element e) { String nodeType = e.getName(); if (nodeType.equals("Category")) { List children = e.getChildren(); for (Element child : children) { parseElement(child); } } else if (nodeType.equals("Node")) { String path = e.getChildText("Icon"); loadIcon(path); } } private static void loadNonStandardIcons() { loadIcon(IconsPaths.ACTION); loadIcon(IconsPaths.CONDITION); loadIcon(IconsPaths.ROOT); loadIcon(IconsPaths.CATEGORY); loadIcon(IconsPaths.GUARD); loadIcon(IconsPaths.BT); } /** * Loads an icon into {@link #applicationIcons} from an icon path. */ private static void loadIcon(String iconPath) { ImageDescriptor imageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin( Activator.PLUGIN_ID, iconPath); Image icon = imageDescriptor == null ? null : imageDescriptor.createImage(); applicationIcons.put(iconPath, icon); } /** * Disposes all the icons stored by the ApplicationIcons. */ public static void disposeIcons() { Collection icons = applicationIcons.values(); for (Image icon : icons) { if (icon != null) { icon.dispose(); } } } private ApplicationIcons() { }; } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/ApplicationWorkbenchAdvisor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchAdvisor; import org.eclipse.ui.application.WorkbenchWindowAdvisor; public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor { private static final String PERSPECTIVE_ID = "jbt.tools.bteditor.perspective"; public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { return new ApplicationWorkbenchWindowAdvisor(configurer); } public String getInitialWindowPerspectiveId() { return PERSPECTIVE_ID; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/ApplicationWorkbenchWindowAdvisor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchWindowAdvisor; public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { super(configurer); } public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) { return new ApplicationActionBarAdvisor(configurer); } public void preWindowOpen() { IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); configurer.setShowCoolBar(true); configurer.setShowMenuBar(true); configurer.setShowStatusLine(true); } public void postWindowOpen() { /* Maximize the window. */ getWindowConfigurer().getWindow().getShell().setMaximized(true); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/BTCPPManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Calendar; import jbt.tools.bteditor.NodesLoader.NodeCategories; import jbt.tools.bteditor.model.BT; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.model.BTNode.Parameter; import jbt.tools.bteditor.model.BTNode.VarParameter; import jbt.tools.bteditor.model.ConceptualBTNode; import jbt.tools.bteditor.model.ConceptualBTNode.NodeInternalType; import jbt.tools.bteditor.model.ConceptualBTNode.ParameterType; import org.jdom.Document; import org.jdom.Element; /** * Class used to export and load behaviour trees into/from XML files. * * @author Ricardo Juan Palma Durán * */ public class BTCPPManager { /** * Exports a {@link BT} into an CPP file. Also creates a header file */ public static void export(BT tree, String fileName) throws IOException { //Uncomment blocks to export different files //String headerName = fileName.replace(".cpp", ".h"); //FileOutputStream headerFile = new FileOutputStream(headerName); //exportHeader(tree, headerFile); // //FileOutputStream file = new FileOutputStream(fileName); //export(tree, file); //file.close(); String inlineName = fileName.replace(".cpp", ".inl"); FileOutputStream inlineFile = new FileOutputStream(inlineName); exportInline(tree, inlineFile); inlineFile.close(); } /** * Exports a {@link BT} into a CPP file */ public static void exportInline(BT tree, FileOutputStream file) throws IOException { // Connect print stream to the output stream PrintStream p = new PrintStream( file ); createInlineFile(tree,p); p.close(); file.close(); } /** * Exports a {@link BT} into a CPP file */ public static void export(BT tree, FileOutputStream file) throws IOException { // Connect print stream to the output stream PrintStream p = new PrintStream( file ); createCPPFile(tree, p); p.close(); file.close(); } /** * Exports a {@link BT} into a .H file */ public static void exportHeader(BT tree, FileOutputStream file) throws IOException { PrintStream p = new PrintStream( file ); createHeaderFile(tree, p); p.close(); file.close(); } /** * Creates and .CPP file from a {@link BT}. */ private static void createCPPFile(BT tree, PrintStream p) { String BTName = tree.getRoot().getName(); p.println("/**"); p.println(" @file "+BTName+".cpp"); p.println(" Contiene la implementaci�n de un componente de BT"); p.println(); p.println(" @author Fichero generado autom�ticamente con JBT Editor"); p.println("*/"); p.println(); p.println("#include \""+BTName+".h\""); p.println("#include \"LatentAction.h\""); p.println(); p.println("#include "); p.println(); p.println(); p.println("namespace OIM {"); p.println(""); p.println("\tIMP_FACTORIA(C"+BTName+");"); p.println("\tIMPL_SUBCLASS_TYPE(C"+BTName+", CBTComponent);"); p.println("\talive::Node *C"+BTName+"::createBehaviourTree() {"); p.println("\t\treturn"); p.println("\t\t\talive::TreeBuilder()"); //TODO write file info Element rootElement = new Element("Tree"); processNode(tree.getRoot(), p, "\t\t\t"); p.println(); p.println("\t}"); p.println(); p.println("} //namespace OIM"); } /** * Creates and .CPP file from a {@link BT}. */ private static void createInlineFile(BT tree, PrintStream p) { String BTName = tree.getRoot().getName(); p.println("\t\treturn"); p.println("\t\t\talive::TreeBuilder()"); //TODO write file info Element rootElement = new Element("Tree"); processNode(tree.getRoot(), p, "\t\t\t"); } /** * Creates a .H from a {@link BT}. */ private static void createHeaderFile(BT tree, PrintStream p) { Calendar cal = Calendar.getInstance(); int currentYear = cal.get(Calendar.YEAR); String BTName = tree.getRoot().getName(); p.println("/**"); p.println("@file "+BTName+".h"); p.println(" Contiene la declaraci�n de un componente de BT"); p.println(); p.println(" @author Fichero generado autom�ticamente con JBT Editor"); p.println("*/"); p.println("#ifndef OIM_"+BTName+"_H"); p.println("#define OIM_"+BTName+"_H"); p.println(); p.println("#include \"BTComponent.h\""); p.println(); p.println("#include "); p.println(); p.println("namespace OIM {"); p.println("/**"); p.println("\tComponente de IA de una entidad que funciona utilizando"); p.println("\tun �rbol de comportamiento concreto de prueba."); p.println("\t

"); p.println("\tEl componente simplemente hereda de CBTComponent y"); p.println("\tredefine el m�todo factor�a para la creaci�n del"); p.println("\t�rbol de comportamiento a utilizar."); p.println("\t@author Marco Antonio G�mez Mart�n"); p.println("\t@date "+currentYear); p.println("*/"); p.println("\tclass C"+BTName+" : public CBTComponent {"); p.println("\t\tDEC_FACTORIA(C"+BTName+");"); p.println("\t\tDECL_TYPE();"); p.println("\tprotected:"); p.println("\t\t/**"); p.println("\t\t M�todo factor�a que crea el �rbol de comportamiento"); p.println("\t\t utilizado por la entidad que contiene el componente."); p.println("\t\t

"); p.println("\t\t En este caso, crea un BT para probar las acciones."); p.println("\t\t @return �rbol de comportamiento a ejecutar por la entidad"); p.println("\t\t a la que pertenece el componente."); p.println("\t\t */"); p.println("\t\tvirtual alive::Node *createBehaviourTree();"); p.println("\t};"); p.println(); p.println("} // namespace OIM"); p.println(); p.println("#endif // OIM_BTComponent_H"); } /** * Creates the method calls of a {@link BTNode}. */ private static void processNode(BTNode node, PrintStream p, String margin) { String nodeType = node.getConceptualNode().getType(); NodeCategories category = NodesLoader.getCategoryOf(nodeType); String endToken = margin+".end()\n"; switch(category) { case COMPOSITE: p.println(margin+".composite()"); break; case DECORATOR: p.println(margin+".decorator<"+nodeType+">()"); break; case LEAF: p.println(margin+".execute<"+nodeType+">()"); break; } /* Process guard. */ //if (node.getGuard() != null) { // Element guardElement = new Element("Guard"); // guardElement.setContent(processNode(node.getGuard(), p)); // result.addContent(guardElement); //} /* Process parameters. */ if (node.getConceptualNode().getParameters().size() != 0) { processParameters(node, p, margin+"\t"); } /* Process children. */ if (node.getNumChildren() != 0) { Element children = new Element("Children"); for (BTNode child : node.getChildren()) { processNode(child, p, margin+"\t"); } } if (!(node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString()))) { p.print(endToken); } else { p.print(margin+";"); } } /** * Creates the method calls associated to all the parameters of a {@link BTNode}. */ private static void processParameters(BTNode node, PrintStream p, String margin) { for (int i=0; i * There are two types of nodes: *

    *
  • Standard nodes: these are internal to the application, that is, the * application knows of them in advance. These are nodes such as Parallel, * Sequence, or Wait. *
  • Non-standard nodes: these nodes are specified by external clients in MMPM * domain files. These are low level nodes in a BT, that is, actions and * conditions. Actions are obtained from the action set of the MMPM domain file, * and conditions are obtained from the sensor set of the MMPM domain file. *
* * Standard nodes are defined in a XML file located at * {@link #STANDARD_NODES_FILE}. That file specifies the XML format of the * standard nodes. Standard nodes are loaded into the application at star-up * time. In fact, if they are not properly loaded, the application does not * start. *

* Non-standard nodes are loaded from MMPM domain files. Only actions and * sensors are read from the MMPM file. Actions are transformed into nodes of * type {@link NodeInternalType#ACTION}, and sensors are transformed into nodes * of type {@link NodeInternalType#CONDITION}. These are the nodes upon which * external users build domain dependent behaviour trees. *

* Standard and non-standard nodes become {@link ConceptualBTNode}s when they * are loaded so that they can be easily used in the application. *

* NodesLoader stores both standard and non-standard nodes into * {@link ConceptualNodesTree}s in order to organize them in categories. *

* {@link #loadStandardNodes()} loads all the standard nodes specified in the * file {@link #STANDARD_NODES_FILE} and creates a ConceptualNodesTree for them. * Also, the list of standard nodes contains a special node that is not defined * in the file, the root node, which is statically created and included in the * list. *

* {@link #loadNonStandardNodes(String)} loads all the actions and sensors * (conditions) from a MMPM domain file, and creates a ConceptualNodesTree whose * root category is the short name of the file. The root category contains two * sub categories, one for actions (which includes all the actions loaded from * the file), and another one for conditions (which contains all the boolean * sensors loaded form the file). *

* The NodesLoader class can also be used for retrieving nodes by type. * * @author Ricardo Juan Palma Durán * * Modified by Fernando Matarrubia * * The main purpose of the modification is to add the functionality * of register all the standard nodes paired with their category in a hashtable * When the c++ file is written, that info would be needed in order to * separate the different node types by their category */ public class NodesLoader { /** ConceptualNodesTree for standard nodes. */ private static ConceptualNodesTree standardNodesTree = new ConceptualNodesTree(); /** Standard nodes. They are indexed by type. */ private static Hashtable standardNodes = new Hashtable(); /** All nodes sorted by their category */ private static Hashtable nodesByCategory = new Hashtable(); /** * ConceptualNodesTrees for non standard nodes. They are indexed by the name * of the file from which they are loaded. */ private static Hashtable nonStandardTrees = new Hashtable(); /** * Non-standard nodes. They are indexed both by type and name (the type is * the first element in the Pair, and the name is the second one). */ private static Hashtable, ConceptualBTNode> nonStandardNodes = new Hashtable, ConceptualBTNode>(); /** The file that contains the definition of all standard nodes. */ public static final String STANDARD_NODES_FILE = "files/standardNodes.xml"; /** Public enum that contains the different node categories */ public enum NodeCategories { COMPOSITE, DECORATOR, LEAF, DUMMY }; /** * Loads all the standard nodes defined in the file * {@link #STANDARD_NODES_FILE}. After doing so, standard nodes can be * accessed through {@link #getStandardNodes()} and * {@link #getNode(String, String)}. *

* Throws an exception if there is any error loading the standard nodes. */ public static void loadStandardNodes() throws IOException { loadRoot(); URL url = FileLocator.find(Activator.getDefault().getBundle(), new Path(STANDARD_NODES_FILE), Collections.EMPTY_MAP); URL fileUrl = null; fileUrl = FileLocator.toFileURL(url); FileInputStream file = new FileInputStream(fileUrl.getPath()); parseStandardNodesFile(file); } /** * Loads the actions and sensors from a MMPM domain file. Creates a new * ConceptualNodesTree whose root category is the short name of the file. * The root category contains two sub categories, one for actions (which * includes all the actions loaded from the file), and another one for * conditions (which contains all the boolean sensors loaded form the file). * The tree can be accessed through {@link #getNonStandardNodesTree(String)} * , and its nodes can be accessed through {@link #getNode(String, String)}. */ public static ConceptualNodesTree loadNonStandardNodes(String fileName) throws IOException { FileInputStream fileStream = new FileInputStream(fileName); SAXBuilder builder = new SAXBuilder(); ConceptualNodesTree tree = new ConceptualNodesTree(); File file = new File(fileName); try { Document doc = builder.build(fileStream); ParsedDomain parsedDomain = new ParsedDomain(); parsedDomain.init(doc.getRootElement(), null); List actions = parsedDomain.getActionSet().getAction(); List conditions = parsedDomain.getSensorSet().getMethods(); for (ParsedAction action : actions) { ConceptualBTNode xmlAction = loadNonStandardAction(action); nonStandardNodes.put( new Pair(xmlAction.getType(), action.getName()), xmlAction); tree.insertNode( file.getName() + ConceptualNodesTree.CATEGORY_SEPARATOR + "Actions", new ConceptualBTNodeItem(xmlAction)); } for (ParsedMethod condition : conditions) { if (condition.getReturnedType() == ActionParameterType.BOOLEAN) { ConceptualBTNode xmlCondition = loadNonStandardCondition(condition); nonStandardNodes.put( new Pair(xmlCondition.getType(), condition.getName()), xmlCondition); tree.insertNode(file.getName() + ConceptualNodesTree.CATEGORY_SEPARATOR + "Conditions", new ConceptualBTNodeItem(xmlCondition)); } } nonStandardTrees.put(fileName, tree); return tree; } catch (Exception e) { throw new IOException(e.getMessage(), e); } } /** * Returns the standard nodes tree or null if it has not been loaded yet * (see {@link #loadStandardNodes()}). */ public static ConceptualNodesTree getStandardNodesTree() { return standardNodesTree; } /** * Returns the non-standard nodes tree that was loaded from the file whose * name is fileName. Returns null if that tree cannot be found. */ public static ConceptualNodesTree getNonStandardNodesTree(String fileName) { return nonStandardTrees.get(fileName); } /** * Returns all the non-standard nodes trees currently loaded by the * NodesLoader. */ public static List getNonStandardNodesTrees() { return Arrays.asList(nonStandardTrees.values().toArray( new ConceptualNodesTree[nonStandardTrees.size()])); } /** * Removes the non-standard nodes tree that was loaded from the file * fileName from the list held by the NodesLoader. */ public static void removeNonStandardNodesTree(String fileName) { nonStandardTrees.remove(fileName); } /** * Returns all the file names of the non-standard nodes trees held by the * NodesLoader. */ public static String[] getNonStandardTreeNames() { return nonStandardTrees.keySet().toArray(new String[nonStandardTrees.size()]); } /** * Returns a List with all the standard nodes. */ public static List getStandardNodes() { return new Vector(standardNodes.values()); } /** * Returns a List with all the non-standard nodes. */ public static List getNonStandardNodes() { return new Vector(nonStandardNodes.values()); } /** * Retrieves a node from all the nodes held by the NodesLoader. For standard * nodes this retrieval process is done through the type ( type * ) only, in which case name is ignored. *

* For non-standard nodes, (actions and conditions), the retrieval process * is carried out by using both the type of the node and its name. This is * necessary since all actions share the same type ( * {@link NodeInternalType#ACTION}), and so do conditions ( * {@link NodeInternalType#CONDITION}), so the only way of telling them * apart is by using their name. */ public static ConceptualBTNode getNode(String type, String name) { if (type.equals(NodeInternalType.ACTION.toString()) || type.equals(NodeInternalType.CONDITION.toString())) { return nonStandardNodes.get(new Pair(type, name)); } else { return standardNodes.get(type); } } private static ConceptualBTNode loadNonStandardCondition(ParsedMethod condition) { ConceptualBTNode xmlNode = new ConceptualBTNode(); xmlNode.setReadableType(condition.getName()); xmlNode.setType(NodeInternalType.CONDITION.toString()); xmlNode.setIcon(IconsPaths.CONDITION); xmlNode.setNumChildren(0); xmlNode.setHasName(true); xmlNode.setName(condition.getName()); List parameters = condition.getParameters(); for (ParsedActionParameter parameter : parameters) { Parameter xmlNodeParameter = new Parameter(); xmlNodeParameter.setName(parameter.getName()); xmlNodeParameter.setType(fromMMPMParameterType(parameter.getType())); /* MMPM parameters are always contextable. */ xmlNodeParameter.setContextable(true); xmlNode.addParameter(xmlNodeParameter); } return xmlNode; } private static ConceptualBTNode loadNonStandardAction(ParsedAction action) { ConceptualBTNode xmlNode = new ConceptualBTNode(); xmlNode.setReadableType(action.getName()); xmlNode.setType(NodeInternalType.ACTION.toString()); xmlNode.setIcon(IconsPaths.ACTION); xmlNode.setNumChildren(0); xmlNode.setHasName(true); xmlNode.setName(action.getName()); List parameters = action.getParameters(); for (ParsedActionParameter parameter : parameters) { Parameter xmlNodeParameter = new Parameter(); xmlNodeParameter.setName(parameter.getName()); xmlNodeParameter.setType(fromMMPMParameterType(parameter.getType())); /* MMPM parameters are always contextable. */ xmlNodeParameter.setContextable(true); xmlNode.addParameter(xmlNodeParameter); } return xmlNode; } private static ParameterType fromMMPMParameterType(ActionParameterType type) { if (type == ActionParameterType.BOOLEAN) { return ParameterType.BOOLEAN; } else if (type == ActionParameterType.ENTITY_ID) { return ParameterType.STRING; } else if (type == ActionParameterType.ENTITY_TYPE) { return ParameterType.STRING; } else if (type == ActionParameterType.FLOAT) { return ParameterType.REAL; } else if (type == ActionParameterType.INTEGER) { return ParameterType.INTEGER; } else if (type == ActionParameterType.STRING) { return ParameterType.STRING; } else if (type == ActionParameterType.PLAYER) { return ParameterType.STRING; } else if (type == ActionParameterType.COORDINATE) { return ParameterType.COORDINATE; } else if (type == ActionParameterType.DIRECTION) { return ParameterType.DIRECTION; } else if (type == ActionParameterType.OBJECT) { return ParameterType.OBJECT; } throw new IllegalArgumentException("Unexpected action parameter type"); } private static List parseStandardNodesFile(FileInputStream file) { List exceptions = new Vector(); SAXBuilder builder = new SAXBuilder(); try { Document doc = builder.build(file); Element root = doc.getRootElement(); parseElement(null, root); } catch (Exception e) { exceptions.add(e); } return exceptions; } private static void parseElement(String currentPath, Element e) { String nodeType = e.getName(); if (nodeType.equals("Category")) { String categoryName = e.getAttributeValue("name"); List children = e.getChildren(); for (Element child : children) { String nextPath = currentPath == null ? categoryName : currentPath + ConceptualNodesTree.CATEGORY_SEPARATOR + categoryName; parseElement(nextPath, child); } } else if (nodeType.equals("Node")) { ConceptualBTNode xmlNode = new ConceptualBTNode(); xmlNode.setType(e.getChild("Type").getValue()); String numChildren = e.getChild("Children").getValue(); xmlNode.setNumChildren(numChildren.equals("I") ? -1 : Integer.parseInt(numChildren)); xmlNode.setIcon(e.getChild("Icon").getValue()); xmlNode.setReadableType(e.getChild("ReadableType").getValue()); xmlNode.setHasName(false); parseParameters(xmlNode, e); ConceptualBTNodeItem nodeToInsert = new ConceptualBTNodeItem(xmlNode); standardNodesTree.insertNode(currentPath, nodeToInsert); standardNodes.put(xmlNode.getType(), xmlNode); nodesByCategory.put(xmlNode.getType(), currentPath); } } public static NodeCategories getCategoryOf(String type) { if (nodesByCategory.containsKey(type)) { String category = nodesByCategory.get(type); String parentType = category.split(ConceptualNodesTree.CATEGORY_SEPARATOR)[1]; if (parentType.contentEquals("Composite")) { return NodeCategories.COMPOSITE; } else if (parentType.contentEquals("Decorator")) { return NodeCategories.DECORATOR; } else if (parentType.contentEquals("Leaf")) { return NodeCategories.LEAF; } } return NodeCategories.DUMMY; } private static void parseParameters(ConceptualBTNode node, Element e) { Element paramsElement = e.getChild("Parameters"); if (paramsElement != null) { List parameters = paramsElement.getChildren(); for (Element parameter : parameters) { parseParameter(node, parameter); } } } private static void parseParameter(ConceptualBTNode node, Element e) { Parameter parameterToAdd = new Parameter(); parameterToAdd.setName(e.getAttributeValue("name")); ParameterType type = ParameterType.valueOf(e.getAttributeValue("type")); parameterToAdd.setType(type); if (type == ParameterType.NODE_ID) { String nodeTypes = e.getAttributeValue("nodetypes"); nodeTypes = nodeTypes.trim(); if (!nodeTypes.equals("")) { String[] individualNodeTypes = nodeTypes.split("( )+"); for (String s : individualNodeTypes) { parameterToAdd.addNodeClass(s); } } String contextable = e.getAttributeValue("contextable"); if (contextable.equals("true")) { parameterToAdd.setContextable(true); } else if (contextable.equals("false")) { parameterToAdd.setContextable(false); } else { throw new RuntimeException("Invalid value for \"contextable\" attribute"); } } node.addParameter(parameterToAdd); } private static void loadRoot() { ConceptualBTNode root = new ConceptualBTNode(); root.setIcon(IconsPaths.ROOT); root.setNumChildren(1); root.setType(NodeInternalType.ROOT.toString()); root.setReadableType(NodeInternalType.ROOT.toString()); root.setHasName(true); standardNodes.put(NodeInternalType.ROOT.toString(), root); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/Perspective.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor; import jbt.tools.bteditor.views.NodeInfo; import jbt.tools.bteditor.views.NodesNavigator; import jbt.tools.bteditor.views.NodesSearcher; import org.eclipse.ui.IFolderLayout; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPerspectiveFactory; public class Perspective implements IPerspectiveFactory { private static final String RIGHT_FOLDER_ID = "RightFolder"; private static final String BOTTOM_RIGHT_FOLDER_ID = "BottomRightFolder"; public void createInitialLayout(IPageLayout layout) { layout.setEditorAreaVisible(true); layout.addView(NodesNavigator.ID, IPageLayout.LEFT, 0.3f, layout.getEditorArea()); IFolderLayout rightFolderLayout = layout.createFolder(RIGHT_FOLDER_ID, IPageLayout.RIGHT, 0.6f, layout.getEditorArea()); IFolderLayout bottomRightFolderLayout=layout.createFolder(BOTTOM_RIGHT_FOLDER_ID, IPageLayout.BOTTOM, 0.5f, RIGHT_FOLDER_ID); rightFolderLayout.addView(NodeInfo.ID); bottomRightFolderLayout.addView(NodesSearcher.ID); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/CheckErrorsAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import jbt.tools.bteditor.editor.BTEditor; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; public class CheckErrorsAction extends EditorActionDelegate{ public CheckErrorsAction(){ super(); } public void run(IAction action) { BTEditor editor=(BTEditor)this.getEditor(); editor.checkTree(); } public void selectionChanged(IAction action, ISelection selection) { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/ClearErrorsAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import jbt.tools.bteditor.editor.BTEditor; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; public class ClearErrorsAction extends EditorActionDelegate { public ClearErrorsAction(){ super(); } public void run(IAction action) { BTEditor editor = (BTEditor) this.getEditor(); editor.clearErrors(); } public void selectionChanged(IAction action, ISelection selection) { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/CollapseTreeAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import jbt.tools.bteditor.editor.BTEditor; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; public class CollapseTreeAction extends EditorActionDelegate{ public void run(IAction action) { ((BTEditor)this.getEditor()).expandTree(false); } public void selectionChanged(IAction action, ISelection selection) { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/DialogExportAsCppAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.util.List; import jbt.tools.bteditor.Application; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.editor.BTEditorInput; import jbt.tools.bteditor.model.BT; import jbt.tools.bteditor.util.Extensions; import jbt.tools.bteditor.util.IconsPaths; import jbt.tools.bteditor.util.StandardDialogs; import jbt.tools.bteditor.util.Utilities; import org.eclipse.jface.action.Action; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * Action that loads several behaviour trees into the application. It first * opens a dialog where the user selects the files that contain the trees. Then, * these trees are opened in separate editors. * * @author Ricardo Juan Palma Durán * */ public class DialogExportAsCppAction extends Action implements IWorkbenchAction { private IWorkbenchWindow window; /** * Constructor. * * @param window the main window. */ public DialogExportAsCppAction(IWorkbenchWindow window) { this.window = window; this.setText("Export BT to an inline file"); this.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, IconsPaths.INL)); } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { BTEditor myEditor = Utilities.getActiveBTEditor(); if (myEditor==null || !myEditor.checkTree()) { StandardDialogs.errorDialog("Tree not saved", "Errors were detected while validating the tree"); } else { /* * Open dialog for asking the user to enter some file names. */ FileDialog dialog = new FileDialog(this.window.getShell(), SWT.SAVE); String[] individualFilters = Extensions.getFiltersFromExtensions(Extensions.getInlFileExtensions()); String[] unifiedFilter = new String[] { Extensions.getUnifiedFilterFromExtensions(Extensions.getInlFileExtensions()) }; String[] filtersToUse = Extensions.joinArrays(individualFilters, unifiedFilter); dialog.setFilterExtensions(filtersToUse); dialog.setText("Export BT to an inline file"); String fileName = dialog.open(); if (fileName != null) { List editors = Utilities.getBTEditors(); for (BTEditor editor : editors) { BTEditorInput editorInput = (BTEditorInput) editor.getEditorInput(); if (editorInput.isFromFile() && editorInput.getTreeName().equals(fileName)) { throw new RuntimeException( "There is a behaviour tree already open with the same name (" + fileName + "). Close it first."); } } String targetFileName = Extensions.joinFileNameAndExtension(fileName, Extensions.getInlFileExtensions()[dialog.getFilterIndex()]); BT tree = Utilities.getActiveBTEditor().getBT(); new ExportToCppAction(tree, targetFileName).run(); } } } public void dispose() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/DialogLoadMMPMDomainAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.io.File; import java.util.Vector; import jbt.tools.bteditor.Application; import jbt.tools.bteditor.util.Extensions; import jbt.tools.bteditor.util.IconsPaths; import org.eclipse.jface.action.Action; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * Action that loads several MMPM domain files into the application. It first * opens a dialog where the user selects the domain files. Then, these domains * are loaded into the application. * * @author Ricardo Juan Palma Durán * */ public class DialogLoadMMPMDomainAction extends Action implements IWorkbenchAction { private IWorkbenchWindow window; /** * Constructor. * * @param window * the main window. */ public DialogLoadMMPMDomainAction(IWorkbenchWindow window) { this.window = window; this.setText("Load MMPM Domain"); this.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( Application.PLUGIN_ID, IconsPaths.LOAD_MMPM_DOMAIN)); } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { /* * Open dialog for asking the user to enter some file names. */ FileDialog dialog = new FileDialog(this.window.getShell(), SWT.MULTI); String[] individualFilters = Extensions .getFiltersFromExtensions(Extensions.getMMPMDomainFileExtensions()); String[] unifiedFilter = new String[] { Extensions .getUnifiedFilterFromExtensions(Extensions .getMMPMDomainFileExtensions()) }; String[] filtersToUse = Extensions.joinArrays(individualFilters, unifiedFilter); dialog.setFilterExtensions(filtersToUse); dialog.setText("Open BT"); if (dialog.open() != null) { /* Get the name of the files (NOT absolute path). */ String[] singleNames = dialog.getFileNames(); /* * This vector will store the absolute path of every single selected * file. */ Vector absolutePath = new Vector(); for (int i = 0, n = singleNames.length; i < n; i++) { StringBuffer buffer = new StringBuffer(dialog.getFilterPath()); if (buffer.charAt(buffer.length() - 1) != File.separatorChar) buffer.append(File.separatorChar); buffer.append(singleNames[i]); absolutePath.add(buffer.toString()); } new LoadMMPMDomainAction(absolutePath).run(); } } /** * * @see org.eclipse.ui.actions.ActionFactory.IWorkbenchAction#dispose() */ public void dispose() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/DialogOpenBTAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.io.File; import java.util.Vector; import jbt.tools.bteditor.Application; import jbt.tools.bteditor.util.Extensions; import jbt.tools.bteditor.util.IconsPaths; import org.eclipse.jface.action.Action; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * Action that loads several behaviour trees into the application. It first * opens a dialog where the user selects the files that contain the trees. Then, * these trees are opened in separate editors. * * @author Ricardo Juan Palma Durán * */ public class DialogOpenBTAction extends Action implements IWorkbenchAction { private IWorkbenchWindow window; /** * Constructor. * * @param window the main window. */ public DialogOpenBTAction(IWorkbenchWindow window) { this.window = window; this.setText("Open BT"); this.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( Application.PLUGIN_ID, IconsPaths.OPEN_BT)); } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { /* * Open dialog for asking the user to enter some file names. */ FileDialog dialog = new FileDialog(this.window.getShell(), SWT.MULTI); String[] individualFilters = Extensions .getFiltersFromExtensions(Extensions.getBTFileExtensions()); String[] unifiedFilter = new String[] { Extensions .getUnifiedFilterFromExtensions(Extensions .getBTFileExtensions()) }; String[] filtersToUse = Extensions.joinArrays(individualFilters, unifiedFilter); dialog.setFilterExtensions(filtersToUse); dialog.setText("Open BT"); /* * If the user has selected at least one file name, we must open it. * Note that the user may select several files. */ if (dialog.open() != null) { /* Get the name of the files (NOT absolute path). */ String[] singleNames = dialog.getFileNames(); /* * This vector will store the absolute path of every single selected * file. */ Vector absolutePath = new Vector(); for (int i = 0, n = singleNames.length; i < n; i++) { StringBuffer buffer = new StringBuffer(dialog.getFilterPath()); if (buffer.charAt(buffer.length() - 1) != File.separatorChar) buffer.append(File.separatorChar); buffer.append(singleNames[i]); absolutePath.add(buffer.toString()); } new OpenBTAction(absolutePath).run(); } } public void dispose() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/EditorActionDelegate.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import org.eclipse.jface.action.IAction; import org.eclipse.ui.IEditorActionDelegate; import org.eclipse.ui.IEditorPart; public abstract class EditorActionDelegate implements IEditorActionDelegate { private IEditorPart editor; private IAction accion; public void setActiveEditor(IAction action, IEditorPart targetEditor){ this.editor = targetEditor; this.accion = action; } public IEditorPart getEditor(){ return this.editor; } public IAction getAction(){ return this.accion; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/ExpandTreeAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import jbt.tools.bteditor.editor.BTEditor; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; public class ExpandTreeAction extends EditorActionDelegate { public void run(IAction action) { ((BTEditor)this.getEditor()).expandTree(true); } public void selectionChanged(IAction action, ISelection selection) { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/ExportToCppAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.io.IOException; import jbt.tools.bteditor.BTCPPManager; import jbt.tools.bteditor.model.BT; import org.eclipse.jface.action.Action; /** * Action that saves a behaviour tree in a CPP file. If there is currently an * open behaviour tree coming from a file and with the same name, the saving * process fails, and an exception is thrown. * * @author Ricardo Juan Palma Durán & Fernando Matarrubia * */ public class ExportToCppAction extends Action { /** The tree to save. */ private BT btToSave; /** The name of the file where the tree must be saved. */ private String fileName; /** * Constructor. * * @param tree * the tree to save. * @param fileName * the name of the file where the tree is going to be saved. */ public ExportToCppAction(BT tree, String fileName) { this.btToSave = tree; this.fileName = fileName; } /** * * @see org.eclipse.jface.action.Action#run() */ public void run(){ try { BTCPPManager.export(this.btToSave, this.fileName); } catch (IOException e) { throw new RuntimeException(e.getMessage()); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/LoadMMPMDomainAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.io.IOException; import java.util.Vector; import jbt.tools.bteditor.NodesLoader; import jbt.tools.bteditor.model.ConceptualNodesTree; import jbt.tools.bteditor.util.StandardDialogs; import jbt.tools.bteditor.util.Utilities; import jbt.tools.bteditor.views.NodesNavigator; import org.eclipse.jface.action.Action; import org.eclipse.ui.IViewPart; /** * Action that loads, into the {@link NodesLoader}, the actions and conditions * (sensors) present in a MMPM domain file. * * @author Ricardo Juan Palma Durán * */ public class LoadMMPMDomainAction extends Action { /** Names of the files to open. */ private Vector fileNames; /** * Constructor. * * @param fileNames * the names of the files to load. */ public LoadMMPMDomainAction(Vector fileNames) { this.fileNames = fileNames; } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { Vector exceptions = new Vector(); for (String currentFile : this.fileNames) { try { ConceptualNodesTree newTree = NodesLoader .loadNonStandardNodes(currentFile); IViewPart view = Utilities.getView(NodesNavigator.class); if (view != null) { NodesNavigator treeView = (NodesNavigator) view; treeView.addTree(newTree); } } catch (IOException e) { exceptions.add(e); } } if (exceptions.size() != 0) { StandardDialogs.exceptionDialog("Error loading MMPM domain file", "There were errors when opening MMPM domain files", exceptions); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/NewBTAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import jbt.tools.bteditor.Application; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.editor.BTEditorInput; import jbt.tools.bteditor.util.IconsPaths; import jbt.tools.bteditor.util.StandardDialogs; import org.eclipse.jface.action.Action; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * Action that creates a new BT and opens an editor for it. * * @author Ricardo Juan Palma Durán * */ public class NewBTAction extends Action { /** Integer used for generating names for the trees that are created. */ private static int currentTreeNumber = 1; /** * Constructor. */ public NewBTAction() { this.setText("New BT"); this.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( Application.PLUGIN_ID, IconsPaths.NEW_BT)); } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { IWorkbenchPage activePage = PlatformUI.getWorkbench() .getWorkbenchWindows()[0].getActivePage(); BTEditorInput editorInput = new BTEditorInput("new " + currentTreeNumber, false, false); currentTreeNumber++; try { activePage.openEditor(editorInput, BTEditor.ID); } catch (PartInitException e) { StandardDialogs.exceptionDialog("Error when creating new tree", "There was an unexpected error when creating the tree", e); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/OpenBTAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.util.Vector; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.editor.BTEditorInput; import jbt.tools.bteditor.util.StandardDialogs; import org.eclipse.jface.action.Action; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; /** * Action for loading behaviour trees into the application, and opens an editor * for every tree that is properly opened. The trees are read from XML files. * This action can open several trees simultaneously. * * @author Ricardo Juan Palma Durán * */ public class OpenBTAction extends Action { /** Names of the files that contain the trees to open. */ private Vector fileNames; /** * Constructor. * * @param fileNames * names of the files that contain the trees to open. */ public OpenBTAction(Vector fileNames) { this.fileNames = fileNames; } public void run() { IWorkbenchPage activePage = PlatformUI.getWorkbench() .getWorkbenchWindows()[0].getActivePage(); Vector exceptions=new Vector(); for (String fileName : this.fileNames) { BTEditorInput editorInput = new BTEditorInput(fileName, true, false); try { activePage.openEditor(editorInput, BTEditor.ID); } catch (PartInitException e) { exceptions.add(e); } } if(exceptions.size()!=0){ StandardDialogs.exceptionDialog("Error opening tree", "There was an error when opening the tree", exceptions); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/SaveBTAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.io.IOException; import jbt.tools.bteditor.BTXMLManager; import jbt.tools.bteditor.model.BT; import org.eclipse.jface.action.Action; /** * Action that saves a behaviour tree in a XML file. If there is currently an * open behaviour tree coming from a file and with the same name, the saving * process fails, and an exception is thrown. * * @author Ricardo Juan Palma Durán * */ public class SaveBTAction extends Action { /** The tree to save. */ private BT btToSave; /** The name of the file where the tree must be saved. */ private String fileName; /** * Constructor. * * @param tree * the tree to save. * @param fileName * the name of the file where the tree is going to be saved. */ public SaveBTAction(BT tree, String fileName) { this.btToSave = tree; this.fileName = fileName; } /** * * @see org.eclipse.jface.action.Action#run() */ public void run(){ try { BTXMLManager.export(this.btToSave, this.fileName); } catch (IOException e) { throw new RuntimeException(e.getMessage()); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/actions/SaveBTAsAction.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.actions; import java.util.List; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.editor.BTEditorInput; import jbt.tools.bteditor.model.BT; import jbt.tools.bteditor.util.Extensions; import jbt.tools.bteditor.util.Utilities; import org.eclipse.jface.action.Action; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.PlatformUI; /** * Action that saves a behaviour tree in a XML file. It first asks the user to * enter a file name in a dialog, and then saves the tree. If there is any * problem in the saving process, an expcetion is thrown. If there is an open * tree coming from a file with the same name that the user specifies, the * saving process also fails, and an exception is thrown. */ public class SaveBTAsAction extends Action { /** The tree to save. */ private BT tree; /** The file where the tree will be stored. */ private String selectedFile; /** The file name that is initially displayed in the file dialog. */ private String initialFileName; /** * Constructor. * * @param tree * tree to save. * @param initialFileNAme * the file name that is initially displayed in the file dialog. */ public SaveBTAsAction(BT tree, String initialFileName) { this.tree = tree; this.initialFileName = initialFileName; } /** * * @see org.eclipse.jface.action.Action#run() */ public void run() { FileDialog dialog = new FileDialog( PlatformUI.getWorkbench().getWorkbenchWindows()[0].getShell(), SWT.SAVE); dialog.setOverwrite(true); dialog.setFilterExtensions(Extensions.getFiltersFromExtensions(Extensions .getBTFileExtensions())); dialog.setText("Save BT as"); dialog.setFileName(this.initialFileName); String fileName = dialog.open(); if (fileName != null) { List editors = Utilities.getBTEditors(); for (BTEditor editor : editors) { BTEditorInput editorInput = (BTEditorInput) editor.getEditorInput(); if (editorInput.isFromFile() && editorInput.getTreeName().equals(fileName)) { throw new RuntimeException( "There is a behaviour tree already open with the same name (" + fileName + "). Close it first."); } } String targetFileName = Extensions.joinFileNameAndExtension(fileName, Extensions.getBTFileExtensions()[dialog.getFilterIndex()]); new SaveBTAction(this.tree, targetFileName).run(); this.selectedFile = targetFileName; } } /** * Returns the name of the file where the tree has been stored. Returns null * if the tree could not be saved or if {@link #run()} has not been called * yet. */ public String getSelectedFile() { return this.selectedFile; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/editor/BTEditor.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.editor; import java.awt.Checkbox; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Vector; import jbt.tools.bteditor.ApplicationIcons; import jbt.tools.bteditor.BTXMLManager; import jbt.tools.bteditor.NodesLoader; import jbt.tools.bteditor.actions.SaveBTAction; import jbt.tools.bteditor.actions.SaveBTAsAction; import jbt.tools.bteditor.event.ITreeModifierListener; import jbt.tools.bteditor.event.TreeModifiedEvent; import jbt.tools.bteditor.model.BT; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.model.BTNode.Identifier; import jbt.tools.bteditor.model.BTNode.Parameter; import jbt.tools.bteditor.model.ConceptualBTNode; import jbt.tools.bteditor.model.ConceptualBTNode.NodeInternalType; import jbt.tools.bteditor.model.ConceptualBTNode.ParameterType; import jbt.tools.bteditor.util.IconsPaths; import jbt.tools.bteditor.util.OverlayImageIcon; import jbt.tools.bteditor.util.Pair; import jbt.tools.bteditor.util.StandardDialogs; import jbt.tools.bteditor.util.Utilities; import jbt.tools.bteditor.viewers.BTNodeIndentifierTransfer; import jbt.tools.bteditor.viewers.ConceptualBTNodeTransfer; import jbt.tools.bteditor.views.NodeInfo; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IPartService; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.EditorPart; /** * Editor that is used for editing behavior trees ({@link BT}). This editor just * shows the tree in a tree-like way, letting the user modify a behaviour tree. *

* This editor can be used to insert nodes into the tree, delete them, and edit * their parameters. Nodes are inserted via a drag and drop mechanism. *

* For insertion purposes, this class is compatible with the * {@link ConceptualBTNodeTransfer} class, so a new empty node is inserted as a * child of the target of a drop operation when the underlying data is * compatible with {@link ConceptualBTNodeTransfer}. *

* Also, nodes of the tree can be moved around by using the drag and drop * mechanism. This class is compatible with the * {@link BTNodeIndentifierTransfer} class, so a dragged node is inserted as a * sibling of the target of the drop operation. *

* A context menu lets the user delete nodes (nodes can be deleted also by using * the delete and back space keys) and edit nodes' guards. Copying and pasting * nodes from the BTEditor into another editor (or the same one) is supported * through the context menu and global actions (Ctrl+C and Ctrl+V). However, it * should be noted that dragging from a BTEditor and dropping onto another * editor is not still supported. *

* For those nodes that have parameters, their value can be set by double * clicking on them. *

* This editor implements the {@link ITreeModifierListener} in order to change * the dirty status of the editor when the underlying tree is modified. *

* An important feature of the BTEditor is that it can be used to also edit the * guard of a node of another tree. BTEditors are opened with * {@link BTEditorInput} objects. BTEditorInput can specify where the tree that * the BTEditor edits comes from, and one of the possible options is to tell the * BTEditorInput that the tree comes from a node's guard. In such case, the * BTEditor has pretty much the same functionality as a normal BTEditor, with * small differences. *

* For instance, the way it responds to the save action is different. * When the BTEditor that contains a guard is saved, the guard is not saved into * a file, but stored into the node whose guard is being edited. Also, the root * node of a guard does not have a name, unlike that of normal tree. *

* A BTEditor's behaviour tree can therefore have guards being edited at any * time. In such case, if the editor is closed, all the editors editing guards * of the tree will be dissociated from the tree, and as a result, they will not * be considered guards any more (just a normal behaviour tree). * * @author Ricardo Juan Palma Durán * */ public class BTEditor extends EditorPart implements ITreeModifierListener { public static final String ID = "jbt.tools.bteditor.editor.BTEditor"; /** The viewer that is being used to display the tree. */ private TreeViewer viewer; /** The tree that the BTEditor is managing. */ private BT tree; /** Flag that indicates whether the tree is dirty or not. */ private boolean dirty; /** * Red color for cells containing erroneous items. */ private Color ERROR_COLOR = Utilities.getDisplay().getSystemColor(SWT.COLOR_RED); /** * List of all the editors that are currently editing the guards of this * tree's nodes. BTEditors are indexed by the BTNode whose guard is being * edited. */ private Map openGuardEditors; /** * In case this BTEditor is editing a guard, this variable stores the * behaviour tree that contains the node whose guard is being edited ( * {@link #guardNode}). */ private BT guardTree; /** * In case this BTEditor is editing a guard, this variable stores the BTNode * whose guard is being edited. */ private BTNode guardNode; /** * * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor) */ public void doSave(IProgressMonitor monitor) { /* First, check if the tree has no structural errors. */ if (!checkTree()) { StandardDialogs.errorDialog("Tree not saved", "Errors were detected while validating the tree"); monitor.setCanceled(true); return; } /* * The save the tree. */ try { if (this.isFromFile()) { /* If the tree comes from a file, save the tree into a file. */ new SaveBTAction(this.tree, ((BTEditorInput) getEditorInput()).getTreeName()).run(); this.dirty = false; firePropertyChange(EditorPart.PROP_DIRTY); } else if (this.isFromGuard()) { /* * If the tree comes from a guard, then set the tree as a guard * of the "this.guardNode". Note that we set a clone of the * guard, not the original one. By doing so, the guard of the * original node will not be modified even if this editor's tree * is modified. */ BTNode guard = this.tree.getRoot().getChildren().get(0); if (guard != null) { this.guardNode.setGuard(guard.clone()); this.guardTree.fireTreeChanged(this); BTEditorInput editorInput = (BTEditorInput) this.getEditorInput(); Utilities.getBTEditor(Long.parseLong(editorInput.getTreeName().split( File.pathSeparator)[0])).viewer.refresh(); } this.dirty = false; firePropertyChange(EditorPart.PROP_DIRTY); } else { /* Otherwise, do a save as. */ doSaveAs(); } } catch (Exception e) { StandardDialogs.exceptionDialog("Tree not saved", "Errors were detected while saving the tree", e); monitor.setCanceled(true); } } /** * * @see org.eclipse.ui.part.EditorPart#doSaveAs() */ public void doSaveAs() { /* First, check if the tree has no structural errors. */ if (!checkTree()) { StandardDialogs.errorDialog("Tree not saved", "Errors were detected while validating the tree"); return; } SaveBTAsAction action = new SaveBTAsAction(this.tree, this.getEditorInput().getName()); try { action.run(); if (action.getSelectedFile() != null) { BTEditorInput editorInput = (BTEditorInput) getEditorInput(); editorInput.setTreeName(action.getSelectedFile()); this.dirty = false; setIsFromFile(true); /* * If the tree comes from a guard, it must be dissociated from * its original tree. From then on, this BTEditor will be * managed as a normal BTEditor. */ if (isFromGuard()) { dissociateFromParentTree(); } setPartName(editorInput.getName()); firePropertyChange(EditorPart.PROP_DIRTY); firePropertyChange(PROP_TITLE); } } catch (Exception e) { StandardDialogs.exceptionDialog("Error saving the tree", "There was an error when saving the tree", e); } } /** * * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, * org.eclipse.ui.IEditorInput) */ public void init(IEditorSite site, IEditorInput input) throws PartInitException { if (!(input instanceof BTEditorInput)) { throw new PartInitException("Illegal IEditorInput. Must be " + BTEditorInput.class.getCanonicalName()); } setSite(site); setInputWithNotify(input); BTEditorInput editorInput = (BTEditorInput) input; setPartName(input.getName()); this.openGuardEditors = new Hashtable(); /* Set the IPartListener that will handle close events. */ IPartService partService = (IPartService) this.getSite().getService(IPartService.class); partService.addPartListener(new BTEditorPartListener()); if (editorInput.isFromFile()) { /* If the tree comes from a file, load the file. */ try { this.tree = BTXMLManager.load(editorInput.getTreeName()); this.tree.addTreeModifiedListener(this); this.dirty = false; } catch (IOException e) { throw new PartInitException("There were errors while loading the tree: " + e.getMessage(), e); } } else if (editorInput.isFromGuard()) { /* * If the tree comes from a guard, we have to construct a new tree * whose root is the guard. */ this.tree = new BT(); this.tree.addTreeModifiedListener(this); BTEditor activeEditor = Utilities.getActiveBTEditor(); this.guardTree = activeEditor.getBT(); String[] pieces = editorInput.getTreeName().split(File.pathSeparator); this.guardNode = this.guardTree.findNode(new Identifier(pieces[1])); /* * Important: the root node (type ROOT) of the guard's tree is not a * normal ROOT, since it has no name. Therefore, we clone the * original ROOT type and remove its ability to provide a name. */ ConceptualBTNode conceptualNoNameRoot = NodesLoader.getNode( NodeInternalType.ROOT.toString(), null).clone(); conceptualNoNameRoot.setHasName(false); BTNode noNameRoot = this.tree.createNode(conceptualNoNameRoot); BTNode guard = this.guardNode.getGuard(); if (guard != null) { /* If the node had a guard, then the editor is not dirty. */ BTNode clonedGuard = guard.clone(); clonedGuard.setParent(noNameRoot); noNameRoot.addChild(clonedGuard); this.dirty = false; } else { /* Otherwise, the editor is dirty. */ this.dirty = true; } this.tree.setRoot(noNameRoot); this.setTitleImage(ApplicationIcons.getIcon(IconsPaths.GUARD)); } else { /* Otherwise, create a new empty BT. */ this.tree = new BT(); this.tree.addTreeModifiedListener(this); tree.setRoot(tree.createNode(NodesLoader.getNode(NodeInternalType.ROOT.toString(), null))); this.dirty = true; } } /** * * @see org.eclipse.ui.part.EditorPart#isDirty() */ public boolean isDirty() { return this.dirty; } /** * Returns true; */ public boolean isSaveAsAllowed() { return true; } /** * * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { initializeViewer(parent, SWT.NONE); IActionBars bars = this.getEditorSite().getActionBars(); if (bars.getGlobalActionHandler(ActionFactory.COPY.getId()) == null) { bars.setGlobalActionHandler(ActionFactory.COPY.getId(), new BTEditorCopyNode()); bars.updateActionBars(); } if (bars.getGlobalActionHandler(ActionFactory.PASTE.getId()) == null) { bars.setGlobalActionHandler(ActionFactory.PASTE.getId(), new BTEditorPasteNode()); bars.updateActionBars(); } /* Expands all the nodes of the tree. */ this.expandTree(true); /* * Selection listener that updates the NodeInfo view in order to show * the currently selected node. */ this.viewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { NodeInfo nodeInfoView = (NodeInfo) Utilities.getView(NodeInfo.class); if (nodeInfoView != null) { List selectedElements = getSelectedElements(); if (selectedElements.size() == 1) { nodeInfoView.setNode(selectedElements.get(0)); } } } }); } /** * * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ public void setFocus() { /* * Update the NodeInfo view so that it displays the information of the * currently selected node. */ NodeInfo nodeInfoView = (NodeInfo) Utilities.getView(NodeInfo.class); if (nodeInfoView != null) { List selectedElements = getSelectedElements(); if (selectedElements.size() == 1) { nodeInfoView.setNode(selectedElements.get(0)); } else { nodeInfoView.setNode(null); } } } private void initializeViewer(Composite parent, int style) { /* Initializes the viewer. */ this.viewer = new TreeViewer(parent, style); this.viewer.setContentProvider(new BTContentProvider()); BTLabelProvider btLabelProvider = new BTLabelProvider(); DecoratingLabelProvider decoratingLabelProvider = new DecoratingLabelProvider( btLabelProvider, btLabelProvider); this.viewer.setLabelProvider(decoratingLabelProvider); this.viewer.setInput(this.tree); final Tree treeWidget = (Tree) this.viewer.getControl(); /* Key listener for deleting nodes. The root node cannot be deleted. */ treeWidget.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.DEL || e.keyCode == SWT.BS) { List selectedNodes = getSelectedElements(); if (selectedNodes.size() != 0) { new DeleteNode(selectedNodes).run(); } } } }); /* Adding drag support. */ this.viewer.addDragSupport(DND.DROP_MOVE, new Transfer[] { BTNodeIndentifierTransfer.getInstance() }, new BTEditorDragSourceListener()); /* Adding drop support. */ this.viewer .addDropSupport(DND.DROP_MOVE, new Transfer[] { ConceptualBTNodeTransfer.getInstance(), BTNodeIndentifierTransfer.getInstance() }, new BTEditorDropTargetListener()); /* Menu listener that creates the context menu. */ treeWidget.addMenuDetectListener(new MenuDetectListener() { public void menuDetected(MenuDetectEvent e) { List selectedNodes = getSelectedElements(); if (selectedNodes.size() != 0) { MenuManager menuManager = new MenuManager(); menuManager.add(new DeleteNode(selectedNodes)); menuManager.add(new ExpandNodes(selectedNodes)); menuManager.add(new CollapseNodes(selectedNodes)); if (selectedNodes.size() == 1) { BTNode selectedNode = selectedNodes.get(0); if (!selectedNode.getConceptualNode().getType() .equals(NodeInternalType.ROOT.toString())) { menuManager.add(new EditGuard(selectedNode)); } menuManager.add(new CopyNode()); PasteNode pasteAction = new PasteNode(); menuManager.add(pasteAction); } treeWidget.setMenu(menuManager.createContextMenu(treeWidget)); } } }); /* * Listener that shows a panel for editing the parameters of the node. */ this.viewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); if (!selection.isEmpty()) { BTNode selectedNode = (BTNode) selection.getFirstElement(); if (selectedNode.getConceptualNode().getParameters().size() != 0 || (selectedNode.getConceptualNode().getType() == NodeInternalType.ROOT .toString() && !isFromGuard())) { new NodeParametersDialog(Utilities.getShell(), selectedNode).open(); } } } }); } /** * Expands or collapses all the nodes of the tree. If expand is * true, the tree is expanded. If false, it is collapsed. */ public void expandTree(boolean expand) { if (expand) { this.viewer.expandAll(); } else { this.viewer.collapseAll(); } this.clearErrors(); } /** * Returns a list of the selected elements, or an empty list if none is * selected. */ public List getSelectedElements() { return ((IStructuredSelection) this.viewer.getSelection()).toList(); } /** * * @see jbt.tools.bteditor.event.ITreeModifierListener#treeModified(jbt.tools.bteditor.event.TreeModifiedEvent) */ public void treeModified(TreeModifiedEvent event) { this.dirty = true; firePropertyChange(EditorPart.PROP_DIRTY); } /** * Checks the validity of the tree. It highlights the nodes that are * incorrect. * * @return true if the tree is correct, and false otherwise. */ public boolean checkTree() { this.clearErrors(); List incorrectNodes = this.tree.checkTree(); if (incorrectNodes.size() != 0) { for (BTNode n : incorrectNodes) { this.setErrorColor(n); } return false; } return true; } /** * Returns the BT that is being edited by this BTEditor. * * @return the BT that is being edited by this BTEditor. */ public BT getBT() { return this.tree; } /** * Selects a node of the BT. If the node does not exist, nothing happens. * * @param node * the node to select. */ public void selectNode(BTNode node) { this.viewer.setSelection(new StructuredSelection(node), true); } /** * Selects a node of the BT. If the node does not exist, nothing happens. * * @param nodeID * the identifier of the node to select. */ public void selectNode(Identifier nodeID) { BTNode node = this.tree.findNode(nodeID); if (node != null) { this.viewer.setSelection(new StructuredSelection(node), true); } } /** * Returns true if the BT that this BTEditor is editing comes from a file, * and false otherwise. * * @return true if the BT that this BTEditor is editing comes from a file, * and false otherwise. */ private boolean isFromFile() { return ((BTEditorInput) getEditorInput()).isFromFile(); } /** * Sets if the BT that this BTEditor is editing comes from a file. * * @param isFromFile * true if the BT comes from a file, and false otherwise. */ private void setIsFromFile(boolean isFromFile) { ((BTEditorInput) getEditorInput()).setIsFromFile(isFromFile); } /** * Returns true if the BT that this BTEditor is editing comes from a guard, * and false otherwise. * * @return true if the BT that this BTEditor is editing comes from a guard, * and false otherwise. */ private boolean isFromGuard() { return ((BTEditorInput) getEditorInput()).isFromGuard(); } /** * Sets if the BT that this BTEditor is editing comes from a guard. * * @param isFromGuard * true if the BT comes from a guard, and false otherwise. */ private void setIsFromGuard(boolean isFromGuard) { ((BTEditorInput) getEditorInput()).setIsFromGuard(isFromGuard); } /** * Highlights with an error color the node node. */ private void setErrorColor(BTNode node) { TreeItem item = findNode(this.viewer.getTree().getTopItem(), node); if (item != null) { item.setBackground(ERROR_COLOR); } } /** * Returns the TreeItem associated to node. The search starts * from the node by item, so all the nodes whose root is * item are searched for. Returns null if no such element is * found. */ private TreeItem findNode(TreeItem item, BTNode node) { if (node == item.getData()) { return item; } else { for (TreeItem child : item.getItems()) { TreeItem found = findNode(child, node); if (found != null) { return found; } } return null; } } /** * Clears the errors that may be being showed in the tree of the editor. */ public void clearErrors() { this.tree.clearErrors(); TreeItem rootItem = viewer.getTree().getTopItem(); /* * This check is done because when the TreeViewer is created in the * BTEditor, somehow the rootItem is null despite the fact that there is * an actual root element in the tree. I do not know why this happened. * Actually, after so many changes, I do not know if this still happens * :S */ if (rootItem != null) { internalClearErrorColors(rootItem); } } /** * Clears the error color from all the nodes of the tree whose root element * is item. */ private void internalClearErrorColors(TreeItem item) { item.setBackground(null); for (TreeItem child : item.getItems()) { internalClearErrorColors(child); } } /** * Label provider of the underlying TreeViewer. * * @author Ricardo Juan Palma Durán * */ private static class BTLabelProvider implements ILabelProvider, ILabelDecorator { private List disposableImages = new LinkedList(); public void addListener(ILabelProviderListener listener) { } public void dispose() { for (Image i : this.disposableImages) { i.dispose(); } } public boolean isLabelProperty(Object element, String property) { return false; } public void removeListener(ILabelProviderListener listener) { } public Image getImage(Object element) { BTNode node = (BTNode) element; return ApplicationIcons.getIcon(node.getConceptualNode().getIcon()); } public String getText(Object element) { BTNode node = (BTNode) element; if (node.getConceptualNode().getType() .equals(NodeInternalType.SUBTREE_LOOKUP.toString())) { for (Parameter p : node.getParameters()) { if (p.getName().equals("subtreeName")) { return node.getConceptualNode().getReadableType() + " - " + p.getValue(); } } } return node.getConceptualNode().getReadableType(); } /** * If the BTNode has a guard, it is decorated. Otherwise, it is left * unchanged. * * @see org.eclipse.jface.viewers.ILabelDecorator#decorateImage(org.eclipse.swt.graphics.Image, * java.lang.Object) */ public Image decorateImage(Image image, Object element) { /* Overlay decorator image over base image. */ BTNode node = (BTNode) element; if (node.getGuard() != null) { Image result; OverlayImageIcon overlayIcon = new OverlayImageIcon(image, ApplicationIcons.getIcon(IconsPaths.GUARD)); result = overlayIcon.getImage(); this.disposableImages.add(result); return result; } else { return null; } } public String decorateText(String text, Object element) { return null; } } /** * Content provider for the underlying TreeViewer. Its input is a {@link BT} * . * * @author Ricardo Juan Palma Durán * */ private static class BTContentProvider implements ITreeContentProvider { public Object[] getChildren(Object parentElement) { BTNode node = (BTNode) parentElement; return node.getChildren().toArray(); } public Object getParent(Object element) { BTNode node = (BTNode) element; return node.getParent(); } public boolean hasChildren(Object element) { BTNode node = (BTNode) element; return node.getNumChildren() > 0; } public Object[] getElements(Object inputElement) { BTNode root = ((BT) inputElement).getRoot(); if (root != null) { return new Object[] { root }; } else { return new Object[] {}; } } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } /** * Copies the currently selected BTNode of the currently active BTEditor * (not necessarily this one) into the * {@link BTEditorCopyAndPasteManager} for future paste operations. If the * root node is selected, what is copied is its child (if it has one). If no * node is selected, nothing is copied. If several nodes are currently * selected, nothing is copied. If there is no active BTEditor, nothing is * copied either. * * @author Ricardo Juan Palma Durán * */ private class BTEditorCopyNode extends Action implements IAction { private BTNode selectedNode; public BTEditorCopyNode() { this.setText("Copy"); } public void run() { BTEditor activeEditor = Utilities.getActiveBTEditor(); if (activeEditor != null) { List selectedElements = activeEditor.getSelectedElements(); if (selectedElements.size() == 1) { this.selectedNode = selectedElements.get(0); if (this.selectedNode.getConceptualNode().getType() .equals(NodeInternalType.ROOT.toString())) { if (this.selectedNode.getChildren().size() != 0) { this.selectedNode = this.selectedNode.getChildren().get(0); } else { this.selectedNode = null; } } } if (this.selectedNode != null) { BTEditorCopyAndPasteManager.getInstance().copy(this.selectedNode); } } } } /** * Paste into the currently selected node of the currently active BTEditor * (not necessarily this one) whatever BTNode is currently copied in * the {@link BTEditorCopyAndPasteManager}. The node is pasted as a child of * the currently selected node. If no node is currently copied in the * BTEditorCopyAndPasteManager, nothing is done. If the currently selected * node cannot hold any more children, nothing is done. Also, if there is no * active BTEditor, nothing is done. * * @author Ricardo Juan Palma Durán * */ private class BTEditorPasteNode extends Action implements IAction { private BTNode selectedNode; public BTEditorPasteNode() { this.setText("Paste"); } public void run() { if (!BTEditorCopyAndPasteManager.getInstance().hasCopy()) { return; } BTEditor activeEditor = Utilities.getActiveBTEditor(); if (activeEditor != null) { List selectedElements = activeEditor.getSelectedElements(); if (selectedElements.size() == 1) { this.selectedNode = selectedElements.get(0); if (this.selectedNode.getConceptualNode().getNumChildren() != -1 && this.selectedNode.getConceptualNode().getNumChildren() <= this.selectedNode .getNumChildren()) { this.selectedNode = null; } } if (this.selectedNode != null) { BTNode pastedNode = BTEditorCopyAndPasteManager.getInstance().paste(); activeEditor.tree.recomputeIDs(pastedNode); if (pastedNode.getBT() != activeEditor.tree) { activeEditor.tree.reassignUnderlyingBT(pastedNode); } this.selectedNode.addChild(pastedNode); pastedNode.setParent(this.selectedNode); activeEditor.tree.updateNodeCounter(); activeEditor.viewer.refresh(this.selectedNode); activeEditor.treeChanged(this); } } } } /** * Copies the currently selected BTNode into the * {@link BTEditorCopyAndPasteManager} for future paste operations. If no * node is selected, nothing is copied. If the root node is selected, what * is copied is its child (if it has one). If several nodes are currently * selected, nothing is copied either. * * @author Ricardo Juan Palma Durán * */ private class CopyNode extends Action implements IAction { private BTNode selectedNode; public CopyNode() { this.setText("Copy"); } public void run() { List selectedElements = getSelectedElements(); if (selectedElements.size() == 1) { this.selectedNode = selectedElements.get(0); if (this.selectedNode.getConceptualNode().getType() .equals(NodeInternalType.ROOT.toString())) { if (this.selectedNode.getChildren().size() != 0) { this.selectedNode = this.selectedNode.getChildren().get(0); } else { this.selectedNode = null; } } } if (this.selectedNode != null) { BTEditorCopyAndPasteManager.getInstance().copy(this.selectedNode); } } } /** * Paste into the currently selected node whatever BTNode is currently * copied in the {@link BTEditorCopyAndPasteManager}. The node is pasted as * a child of the currently selected node. If no node is currently copied in * the BTEditorCopyAndPasteManager, nothing is done. Also, if the currently * selected node cannot hold any more children, nothing is done. * * @author Ricardo Juan Palma Durán * */ private class PasteNode extends Action implements IAction { private BTNode selectedNode; public PasteNode() { this.setText("Paste"); } public void run() { if (!BTEditorCopyAndPasteManager.getInstance().hasCopy()) { return; } List selectedElements = getSelectedElements(); if (selectedElements.size() == 1) { this.selectedNode = selectedElements.get(0); if (this.selectedNode.getConceptualNode().getNumChildren() != -1 && this.selectedNode.getConceptualNode().getNumChildren() <= this.selectedNode .getNumChildren()) { this.selectedNode = null; } } if (this.selectedNode != null) { BTNode pastedNode = BTEditorCopyAndPasteManager.getInstance().paste(); if (pastedNode.getBT() != tree) { tree.reassignUnderlyingBT(pastedNode); } tree.recomputeIDs(pastedNode); this.selectedNode.addChild(pastedNode); pastedNode.setParent(this.selectedNode); tree.updateNodeCounter(); viewer.refresh(this.selectedNode); treeChanged(this); } } } /** * Action for deleting a list of nodes from the tree. The root node is not * deleted. * * @author Ricardo Juan Palma Durán * */ private class DeleteNode extends Action { private List selectedNodes; public DeleteNode(List selectedNodes) { this.setText("Delete node"); this.selectedNodes = selectedNodes; } public void run() { boolean nodeDeleted = false; for (BTNode node : this.selectedNodes) { if (!node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString())) { if (openGuardEditors.containsKey(node)) { boolean delete = StandardDialogs .confirmationDialog( "Node with open guard", "Node " + node.getID().toString() + " has an open guard. If this node is removed, the guard will be dissociated from the tree. Are you sure you want to delete the node?"); if (delete) { BTNode parent = node.getParent(); parent.removeChild(node); viewer.refresh(parent); nodeDeleted = true; BTEditor guardEditor = openGuardEditors.get(node); BTEditorInput editorInput = (BTEditorInput) guardEditor .getEditorInput(); editorInput.setTreeName(editorInput.getName()); guardEditor.dissociateFromParentTree(); guardEditor.dirty = true; guardEditor.firePropertyChange(PROP_TITLE); guardEditor.firePropertyChange(PROP_DIRTY); openGuardEditors.remove(node); } } else { BTNode parent = node.getParent(); parent.removeChild(node); viewer.refresh(parent); nodeDeleted = true; } } } if (nodeDeleted) { treeChanged(viewer); } } } /** * Action that expands nodes of the tree. * * @author Ricardo Juan Palma Durán * */ private class ExpandNodes extends Action { private List nodesToExpand; public ExpandNodes(List nodesToExpand) { this.setText("Expand node"); this.nodesToExpand = nodesToExpand; } public void run() { for (BTNode node : this.nodesToExpand) { viewer.expandToLevel(node, TreeViewer.ALL_LEVELS); } } } private class EditGuard extends Action { private BTNode node; public EditGuard(BTNode node) { this.setText("Edit Guard"); this.node = node; } public void run() { new GuardEditionDialog(Utilities.getShell(), this.node).open(); } } /** * Action that expands nodes of the tree. * * @author Ricardo Juan Palma Durán * */ private class CollapseNodes extends Action { private List nodesToCollapse; public CollapseNodes(List nodesToCollapse) { this.setText("Collapse node"); this.nodesToCollapse = nodesToCollapse; } public void run() { for (BTNode node : this.nodesToCollapse) { viewer.collapseToLevel(node, TreeViewer.ALL_LEVELS); } } } /** * Dialog that lets the user: *

    *
  • Add a guard to a node. *
  • Modify the guard's parameters. *
  • Remove the guard from a node. *
* * @author Ricardo Juan Palma Durán * */ private class GuardEditionDialog extends Dialog { private BTNode node; private TreeViewer guardsViewer; private Button editGuardButton; private Button addSimpleGuardButton; private Button removeGuardButton; private Button addComplexGuardButton; public GuardEditionDialog(Shell parentShell, BTNode node) { super(parentShell); this.node = node; this.setBlockOnOpen(false); this.setShellStyle(SWT.RESIZE | SWT.CLOSE | SWT.APPLICATION_MODAL | SWT.MAX); } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse * .swt.widgets.Composite) */ protected Control createContents(Composite parent) { Control contents = super.createContents(parent); return contents; } /** * * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent);// new composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); composite.setLayout(new GridLayout(2, false)); /* * We force the default size of the list displaying the guard to be * two rows, and to have at least a minimum width. */ Composite guardsViewerComposite = new Composite(composite, SWT.NONE) { public Point computeSize(int wHint, int hHint, boolean changed) { return new Point(wHint < 200 ? 200 : wHint, guardsViewer.getTree() .getItemHeight() * 2); } }; guardsViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); guardsViewerComposite.setLayout(new FillLayout()); this.guardsViewer = new TreeViewer(guardsViewerComposite); this.guardsViewer.setContentProvider(new BTContentProvider()); this.guardsViewer.setLabelProvider(new BTLabelProvider()); /* Double click listener for editing the guards parameters. */ this.guardsViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { if (node.getGuard() != null) { if (node.getGuard().getChildren().size() != 0) { try { openGuardEditor(node); close(); } catch (PartInitException ex) { StandardDialogs.exceptionDialog("Error opening the guard", "There was an unexpected error when opening the guard", ex); } } else if (node.getGuard().getConceptualNode().getParameters().size() != 0) { new NodeParametersDialog(getShell(), node.getGuard()).open(); } } } }); if (this.node.getGuard() != null) { BT input = new BT(this.node.getGuard()); this.guardsViewer.setInput(input); } Composite buttonsComposite = new Composite(composite, SWT.NONE); buttonsComposite.setLayout(new GridLayout(1, true)); buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); this.addSimpleGuardButton = createAddSimpleGuardButton(buttonsComposite); this.addComplexGuardButton = createAddComplexGuardButton(buttonsComposite); this.editGuardButton = createEditGuardButton(buttonsComposite); this.removeGuardButton = createRemoveGuardButton(buttonsComposite); return composite; } private Button createAddSimpleGuardButton(Composite parent) { Button addSimpleGuardButton = new Button(parent, SWT.PUSH); addSimpleGuardButton.setText("Add simple guard"); addSimpleGuardButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); final GuardEditionDialog guardEditionDialog = this; addSimpleGuardButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { new GuardInsertionDialog(Utilities.getShell(), node, guardEditionDialog).open(); } }); return addSimpleGuardButton; } private Button createAddComplexGuardButton(Composite parent) { Button addGuardButton = new Button(parent, SWT.PUSH); addGuardButton.setText("Add complex guard"); addGuardButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); addGuardButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { try { openGuardEditor(node); close(); } catch (PartInitException ex) { StandardDialogs.exceptionDialog("Error opening the guard", "There was an unexpected error when opening the guard", ex); } } }); return addGuardButton; } private Button createEditGuardButton(Composite parent) { Button editGuardButton = new Button(parent, SWT.PUSH); editGuardButton.setText("Edit guard"); editGuardButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); editGuardButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (node.getGuard() != null) { if (node.getGuard().getChildren().size() != 0) { try { openGuardEditor(node); close(); } catch (PartInitException ex) { StandardDialogs.exceptionDialog("Error when creating new tree", "There was an unexpected error when creating the tree", ex); } } else if (node.getGuard().getConceptualNode().getParameters().size() != 0) { new NodeParametersDialog(getShell(), node.getGuard()).open(); } } } }); return editGuardButton; } private Button createRemoveGuardButton(Composite parent) { final Button removeGuardButton = new Button(parent, SWT.PUSH); removeGuardButton.setText("Remove guard"); removeGuardButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); removeGuardButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (node.getGuard() != null) { node.setGuard(null); guardsViewer.setInput(null); /* Dissociate the editor that is editing the guard. */ if (openGuardEditors.containsKey(node)) { BTEditor guardEditor = openGuardEditors.get(node); BTEditorInput editorInput = (BTEditorInput) guardEditor .getEditorInput(); editorInput.setTreeName(editorInput.getName()); guardEditor.dissociateFromParentTree(); guardEditor.dirty = true; guardEditor.firePropertyChange(PROP_TITLE); guardEditor.firePropertyChange(PROP_DIRTY); } treeChanged(removeGuardButton); } } }); return removeGuardButton; } /* * (non-Javadoc) * * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt. * widgets .Shell) */ protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText("Guard Editor"); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org. * eclipse .swt.widgets.Composite) */ protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) */ protected void buttonPressed(int buttonId) { this.setReturnCode(buttonId); if (buttonId == IDialogConstants.CLOSE_ID) { viewer.update(this.node, null); close(); } } public void setGuard() { if (this.node.getGuard() != null) { BT input = new BT(this.node.getGuard()); this.guardsViewer.setInput(input); } } } /** * Dialog that shows all the loaded leaf nodes and lets the user select one * to be inserted as the guard of a node. * * @author Ricardo Juan Palma Durán * */ private class GuardInsertionDialog extends Dialog { private BTNode node; private TableViewer leafNodesViewer; private GuardEditionDialog guardEditionDialog; public GuardInsertionDialog(Shell parentShell, BTNode node, GuardEditionDialog guardEditionDialog) { super(parentShell); this.node = node; this.setBlockOnOpen(false); this.guardEditionDialog = guardEditionDialog; this.setShellStyle(SWT.RESIZE | SWT.CLOSE | SWT.APPLICATION_MODAL); } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse * .swt.widgets.Composite) */ protected Control createContents(Composite parent) { Control contents = super.createContents(parent); return contents; } /** * * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); /* * We force the default size of the list displaying the list of leaf * nodes guard to be seven rows. */ Composite leafNodesViewerComposite = new Composite(composite, SWT.NONE) { public Point computeSize(int wHint, int hHint, boolean changed) { return new Point(leafNodesViewer.getTable().computeSize(SWT.DEFAULT, SWT.DEFAULT).x, leafNodesViewer.getTable().getItemHeight() * 7); } }; leafNodesViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); leafNodesViewerComposite.setLayout(new FillLayout()); this.leafNodesViewer = new TableViewer(leafNodesViewerComposite, SWT.BORDER | SWT.SINGLE); this.leafNodesViewer.setContentProvider(new ConceptualBTNodeListContentProvider()); this.leafNodesViewer.setLabelProvider(new ConceptualBTNodeListLabelProvider()); /* * Build a list with all the nodes that can be used as guards, which * are leaf nodes. */ List standardNodes = NodesLoader.getStandardNodes(); List nonStandardNodes = NodesLoader.getNonStandardNodes(); List leafNodes = new Vector(); for (ConceptualBTNode node : standardNodes) { if (node.getNumChildren() == 0) { leafNodes.add(node); } } for (ConceptualBTNode node : nonStandardNodes) { if (node.getNumChildren() == 0) { leafNodes.add(node); } } /* This list is the input to the viewer. */ this.leafNodesViewer.setInput(leafNodes); this.leafNodesViewer.getTable().select(0); /* * Set a sorter for the elements. First standard nodes, then * conditions, and finally actions. */ this.leafNodesViewer.setSorter(new ViewerSorter() { public int compare(Viewer viewer, Object e1, Object e2) { ConceptualBTNode node1 = (ConceptualBTNode) e1; ConceptualBTNode node2 = (ConceptualBTNode) e2; String node1Type = node1.getType(); String node2Type = node2.getType(); if ((node1Type.equals(NodeInternalType.ACTION.toString()) || node1Type .equals(NodeInternalType.CONDITION.toString())) && node2Type.equals(node1Type)) { return node1.getReadableType().compareToIgnoreCase(node2.getReadableType()); } if (node1Type.equals(NodeInternalType.ACTION.toString()) && node2Type.equals(NodeInternalType.CONDITION.toString())) { return 1; } if (node1Type.equals(NodeInternalType.CONDITION.toString()) && node2Type.equals(NodeInternalType.ACTION.toString())) { return -1; } if (!node1Type.equals(NodeInternalType.ACTION.toString()) && !node1Type.equals(NodeInternalType.CONDITION.toString())) { if (!node2Type.equals(NodeInternalType.ACTION.toString()) && !node2Type.equals(NodeInternalType.CONDITION.toString())) { return node1.getReadableType().compareToIgnoreCase( node2.getReadableType()); } else { return -1; } } return node1.getReadableType().compareToIgnoreCase(node2.getReadableType()); } }); /* Add double click listener. */ this.leafNodesViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { setGuardFromSelection(); treeChanged(this); close(); } }); return composite; } /** * Content provider for a list of {@link ConceptualBTNode} objects. * * Its input is a List<ConceptualBTNode>. * * @author Ricardo Juan Palma Durán * */ private class ConceptualBTNodeListContentProvider implements IStructuredContentProvider { public Object[] getElements(Object inputElement) { return ((List) inputElement).toArray(); } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } /** * Label provider for objects of type ConceptualBTNode. * * @author Ricardo Juan Palma Durán * */ private class ConceptualBTNodeListLabelProvider implements ILabelProvider { public Image getImage(Object element) { ConceptualBTNode node = (ConceptualBTNode) element; return ApplicationIcons.getIcon(node.getIcon()); } public String getText(Object element) { ConceptualBTNode node = (ConceptualBTNode) element; return node.getReadableType(); } public void addListener(ILabelProviderListener listener) { } public void dispose() { } public boolean isLabelProperty(Object element, String property) { return false; } public void removeListener(ILabelProviderListener listener) { } } /* * (non-Javadoc) * * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt. * widgets .Shell) */ protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText("Guard Editor"); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org. * eclipse .swt.widgets.Composite) */ protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, false); createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) */ protected void buttonPressed(int buttonId) { this.setReturnCode(buttonId); if (buttonId == IDialogConstants.CLOSE_ID) { close(); } else { setGuardFromSelection(); treeChanged(this); close(); } } private void setGuardFromSelection() { ConceptualBTNode selectedConceptualBTNode = (ConceptualBTNode) ((IStructuredSelection) this.leafNodesViewer .getSelection()).toList().get(0); this.node.setGuard(tree.createNode(selectedConceptualBTNode)); this.guardEditionDialog.setGuard(); } } /** * Dialog that is used to enter parameter values. * * @author Ricardo Juan Palma Durán * */ private class NodeParametersDialog extends Dialog { private BTNode node; private NameEditorComposite nameEditor; private ParametersEditorComposite nodeEditor; /** * Constructor. node must be a node with parameters. */ public NodeParametersDialog(Shell shell, BTNode node) { super(shell); this.setBlockOnOpen(false); this.node = node; this.setShellStyle(SWT.RESIZE | SWT.CLOSE | SWT.APPLICATION_MODAL); } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse * .swt.widgets.Composite) */ protected Control createContents(Composite parent) { Control contents = super.createContents(parent); return contents; } /** * * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); if (this.node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString())) { this.nameEditor = new NameEditorComposite(composite, SWT.NONE, this.node.getName()); this.nameEditor.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); } if (this.node.getConceptualNode().getParameters().size() != 0) { Group parametersGroup = new Group(composite, SWT.NONE); parametersGroup.setText("Parameters"); parametersGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); parametersGroup.setLayout(new GridLayout(1, false)); this.nodeEditor = new ParametersEditorComposite(parametersGroup, SWT.NONE, this.node); this.nodeEditor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); } return composite; } /* * (non-Javadoc) * * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt. * widgets .Shell) */ protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText("Node Editor"); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org. * eclipse .swt.widgets.Composite) */ protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) */ protected void buttonPressed(int buttonId) { this.setReturnCode(buttonId); if (buttonId == IDialogConstants.CLOSE_ID) { close(); } else { if (this.nodeEditor != null) { try { List params = this.nodeEditor.getParameters(); this.node.clearParameters(); for (Parameter p : params) { this.node.addParameter(p); } } catch (Exception e) { MessageDialog.openError(this.getShell(), "Error validating parameters", e.getMessage()); return; } } if (this.nameEditor != null) { try { this.node.setName(this.nameEditor.getNodeName()); } catch (Exception e) { MessageDialog.openError(this.getShell(), "Error validating name", e.getMessage()); return; } } treeChanged(viewer); if (node.getConceptualNode().getType() .equals(NodeInternalType.SUBTREE_LOOKUP.toString())) { viewer.update(node, null); } close(); } } } /** * Composite for editing the name of the root tree. * * @author Ricardo Juan Palma Durán * */ private class NameEditorComposite extends Composite { private Label nameLabel; private Text nameValue; /** * initialName is the name that is displayed when the * Composite is created. It may be null, in which case no name is * displayed. */ public NameEditorComposite(Composite parent, int style, String initialName) { super(parent, style); this.setLayout(new GridLayout(2, false)); this.nameLabel = new Label(this, SWT.NONE); this.nameLabel.setText("Name"); this.nameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); this.nameValue = new Text(this, SWT.BORDER); this.nameValue.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); this.nameValue.setText(initialName == null ? "" : initialName); } /** * Returns the name, or throws an exception if it is not a valid name. */ public String getNodeName() throws RuntimeException { if (this.nameValue.getText().equals("")) { throw new RuntimeException("Invalid name"); } return this.nameValue.getText(); } } /** * Composite for editing the parameters of a node. It internally stores many * {@link ParameterComposite}, one for every parameter of the node. * * @author Ricardo Juan Palma Durán * */ private class ParametersEditorComposite extends Composite { private BTNode node; private List parameterComposites; public ParametersEditorComposite(Composite parent, int style, BTNode node) { super(parent, style); this.node = node; this.setLayout(new GridLayout(1, false)); this.parameterComposites = new Vector(); List parameters = this.node .getConceptualNode().getParameters(); for (int i = 0; i < parameters.size(); i++) { String currentParameterValue = this.node.getParameters().size() > 0 ? this.node .getParameters().get(i).getValue() : null; boolean currentFromContext = this.node.getParameters().size() > 0 ? this.node .getParameters().get(i).getFromContext() : false; boolean isConstant = true; if (ParameterType.isVariable(this.node.getConceptualNode().getParameters().get(i).getType())) { String fieldValue = null; BTNode.VarParameter varNodeParameter = null; if (this.node.getParameters().size() > 0) { varNodeParameter = (BTNode.VarParameter)this.node.getParameters().get(i); fieldValue = varNodeParameter.getVariableName(); isConstant = varNodeParameter.getIsConstant(); } VarParameterComposite pc = new VarParameterComposite(this, SWT.NONE, parameters.get(i), currentParameterValue, currentFromContext, isConstant, fieldValue, varNodeParameter); this.parameterComposites.add(pc); pc.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); } else { ParameterComposite pc = new ParameterComposite(this, SWT.NONE, parameters.get(i), currentParameterValue, currentFromContext); this.parameterComposites.add(pc); pc.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); } } } public List getParameters() throws RuntimeException { List result = new Vector(); for (ParameterComposite pc : this.parameterComposites) { result.add(pc.getParameter()); } return result; } } /** * Composite for editing an individual parameter. If the parameter is * contextable, the value of the context location where to find the * parameter may be specified. * * @author Ricardo Juan Palma Durán * */ private class ParameterComposite extends Composite { private jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameter; private Label nameLabel; private Text valueText; private Checkbox isConstant; private Button fromContextButton; private static final int DEFAULT_TEXT_FIELD_WIDTH = 100; public ParameterComposite(Composite parent, int style) { super(parent, style); } /** * initialValue may be null. */ public ParameterComposite(Composite parent, int style, jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameter, String initialValue, boolean initialFromContext) { super(parent, style); this.setLayout(new GridLayout(3, false)); this.parameter = parameter; String tooltip = getTooltip(parameter); this.nameLabel = new Label(this, SWT.NONE); this.nameLabel.setText(this.parameter.getName() + " (" + this.parameter.getType().getReadableType() + ")"); this.nameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); this.nameLabel.setToolTipText(tooltip); this.valueText = new Text(this, SWT.BORDER); this.valueText.setText(initialValue == null ? "" : initialValue); GridData data = new GridData(SWT.FILL, SWT.CENTER, true, false); data.widthHint = DEFAULT_TEXT_FIELD_WIDTH; this.valueText.setLayoutData(data); this.valueText.setToolTipText(tooltip); if (this.parameter.getContextable()) { this.fromContextButton = new Button(this, SWT.CHECK); this.fromContextButton.setText("From context"); this.fromContextButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); this.fromContextButton.setSelection(initialFromContext); this.fromContextButton .setToolTipText("If activated, the specified value represents the place, in the context, where the value of the variable will be retrieved from"); if (this.parameter.getType() == ParameterType.OBJECT) { this.fromContextButton.setEnabled(false); this.fromContextButton.setSelection(true); } } } /** * Returns the parameter shown by the Composite, and throws an exception * in case there is some error in the parameter. */ public Parameter getParameter() throws RuntimeException { /* * If the parameter must come from the context, then any non-empty * string value is allowed. */ if (this.parameter.getContextable() && this.fromContextButton.getSelection()) { if (this.valueText.getText().equals("")) { throw new RuntimeException("Invalid parameter value for parameter " + this.parameter.getName() + ". Must be a non-empty string."); } } else { if (!BTNode.checkParameter(parameter, this.valueText.getText(), tree)) { throw new RuntimeException("Invalid parameter value (" + this.valueText.getText() + ") for parameter " + this.parameter.getName()); } } Parameter result = new Parameter(); result.setName(this.parameter.getName()); result.setValue(this.valueText.getText()); if (this.parameter.getContextable()) { result.setFromContext(this.fromContextButton.getSelection()); } return result; } /** * Returns an appropriate descriptive tooltip for a * {@link jbt.tools.bteditor.model.ConceptualBTNode.Parameter}. */ private String getTooltip( jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameterDefinition) { ParameterType parameterType = parameterDefinition.getType(); if (parameterType == ParameterType.BOOLEAN) { return "Boolean value. Can be either \"true\" or \"false\""; } if (parameterType == ParameterType.COORDINATE) { return "Coordinate value. Must be a sequence of real numbers separated by blank spaces"; } if (parameterType == ParameterType.DIRECTION) { return "Direction value. Can be any integer value"; } if (parameterType == ParameterType.INTEGER) { return "Integer value"; } if (parameterType == ParameterType.NODE_ID) { String validTypes = ""; if (parameterDefinition.getNodeClasses().size() != 0) { for (String type : parameterDefinition.getNodeClasses()) { validTypes += NodesLoader.getNode(type, null).getReadableType() + ", "; } validTypes = validTypes.substring(0, validTypes.length() - 2); } return "Node identifier. Must be the identifier of a node of the tree" + (validTypes.equals("") ? "" : ", with one of the following types: " + validTypes); } if (parameterType == ParameterType.OBJECT) { return "Object. Always retrieved from the context"; } if (parameterType == ParameterType.PARALLEL_POLICY) { return "Parallel task policy. Can be either \"sequence\" or \"selector\""; } if (parameterType == ParameterType.REAL) { return "Real value"; } if (parameterType == ParameterType.STATUS_CODE) { return "Task termination status code. Can be either \"success\" or \"failure\""; } if (parameterType == ParameterType.STRING) { return "String value. Any non-empty string"; } if (parameterType == ParameterType.LIST_OF_VARIABLES) { return "List of variables, surrounded each one by \"\", and separated by blank spaces"; } return ""; } protected Composite getCompositeParent() { return this.getParent(); } } /** * Composite for editing an variable individual parameter. If the parameter is * contextable, the value of the context location where to find the * parameter may be specified. Also check if the parameter is a key-value pair * and shows the dialog regarding that * * @author Fernando Matarrubia Garc�a * */ private class VarParameterComposite extends ParameterComposite { private jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameter; private Label nameLabel; private Text valueText; private final Button isConstant; private final Text variableNameText; private Button fromContextButton; private static final int DEFAULT_TEXT_FIELD_WIDTH = 100; private BTNode.VarParameter varNodeParameter; /** * initialValue may be null. */ public VarParameterComposite(Composite parent, int style, jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameter, String initialValue, boolean initialFromContext, boolean isConstant, String defaultVarText, BTNode.VarParameter varNodeParameter) { super(parent, style); this.varNodeParameter = varNodeParameter; this.setLayout(new GridLayout(4, true)); this.parameter = parameter; String tooltip = super.getTooltip(parameter); this.nameLabel = new Label(this, SWT.NONE); this.nameLabel.setText(this.parameter.getName() + " (" + this.parameter.getType().getReadableType() + ")"); this.nameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); this.nameLabel.setToolTipText(tooltip); this.isConstant = new Button(this, SWT.CHECK); this.isConstant.setText("Constant"); GridData data2 = new GridData(SWT.LEFT, SWT.CENTER, true, false); data2.widthHint = DEFAULT_TEXT_FIELD_WIDTH; this.isConstant.setLayoutData(data2); this.isConstant.setSelection(isConstant); this.isConstant.setToolTipText("If activated, the \"Constant\" field is enabled. That means that the parameter is a special key-value type"); this.variableNameText = new Text(this, SWT.BORDER); this.variableNameText.setText(defaultVarText == null ? "" : defaultVarText); GridData data3 = new GridData(SWT.FILL, SWT.CENTER, true, false); data2.widthHint = DEFAULT_TEXT_FIELD_WIDTH; this.variableNameText.setLayoutData(data3); this.variableNameText.setToolTipText(tooltip); this.variableNameText.setEnabled(!isConstant); this.valueText = new Text(this, SWT.BORDER); this.valueText.setText(initialValue == null ? "" : initialValue); GridData data = new GridData(SWT.FILL, SWT.CENTER, true, false); data.widthHint = DEFAULT_TEXT_FIELD_WIDTH; this.valueText.setLayoutData(data); this.valueText.setToolTipText(tooltip); Listener listener = new Listener() { public void handleEvent(Event event) { variableNameText.setEnabled(!VarParameterComposite.this.isConstant.getSelection()); setIsConstant(VarParameterComposite.this.isConstant.getSelection()); } }; this.isConstant.addListener(SWT.Selection, listener); if (this.parameter.getContextable()) { this.fromContextButton = new Button(this, SWT.CHECK); this.fromContextButton.setText("From context"); this.fromContextButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); this.fromContextButton.setSelection(initialFromContext); this.fromContextButton .setToolTipText("If activated, the specified value represents the place, in the context, where the value of the variable will be retrieved from"); if (this.parameter.getType() == ParameterType.OBJECT) { this.fromContextButton.setEnabled(false); this.fromContextButton.setSelection(true); } } } /** * Returns the parameter shown by the Composite, and throws an exception * in case there is some error in the parameter. */ public Parameter getParameter() throws RuntimeException { /* * If the parameter must come from the context, then any non-empty * string value is allowed. */ if (this.parameter.getContextable() && this.fromContextButton.getSelection()) { if (this.valueText.getText().equals("")) { throw new RuntimeException("Invalid parameter value for parameter " + this.parameter.getName() + ". Must be a non-empty string."); } } else { if (!BTNode.checkVarParameter(parameter, this.valueText.getText(), this.variableNameText.getText(), this.isConstant.getSelection(), tree)) { throw new RuntimeException("Invalid parameter value (" + this.valueText.getText() + ") for parameter " + this.parameter.getName()); } } BTNode.Parameter aux = new BTNode.Parameter(); aux.setName(this.parameter.getName()); aux.setValue(this.valueText.getText()); BTNode.VarParameter result = new BTNode.VarParameter(aux); result.setIsConstant(this.isConstant.getSelection()); result.setVariableName(this.variableNameText.getText()); if (this.parameter.getContextable()) { result.setFromContext(this.fromContextButton.getSelection()); } return result; } /* * Helper function that sets the Field Value of a Variable Node Parameter * This function is called from the checkbox event listener */ public void setIsConstant(boolean isConstant) { if (varNodeParameter != null) { varNodeParameter.setIsConstant(isConstant); } } } /** * Drag listener used by the BTEditor. This drag listener transfers the * selected element {@link BTNode}, and is compatible with the * {@link BTNodeIndentifierTransfer} type. * * @author Ricardo Juan Palma Durán * */ private class BTEditorDragSourceListener implements DragSourceListener { public void dragStart(DragSourceEvent event) { List selection = ((IStructuredSelection) viewer.getSelection()).toList(); if (selection.size() == 1) { if (!selection.get(0).getConceptualNode().getType() .equals(NodeInternalType.ROOT.toString())) { event.doit = true; return; } } event.doit = false; } public void dragSetData(DragSourceEvent event) { if (BTNodeIndentifierTransfer.getInstance().isSupportedType(event.dataType)) { event.data = ((IStructuredSelection) viewer.getSelection()).getFirstElement(); } } public void dragFinished(DragSourceEvent event) { } } /** * Drop target listener used by the BTEditor. This drop target listener is * compatible with two transfer types, {@link ConceptualBTNodeTransfer} and * {@link BTNodeIndentifierTransfer}. *

* When the data being transfered is compatible with * {@link ConceptualBTNodeTransfer}, a new empty BTNode is created as a * child or as a sibling of the target of the drop operation (depending on * the specific position of the cursor at the moment the operation is * performed). *

* When the data being transfered is compatible with * {@link BTNodeIndentifierTransfer}, that node is removed from its current * position and inserted as a sibling or as a child of the target of the * drop operation (depending on the specific position of the cursor at the * moment the operation is performed). *

* It should be noted that dragging from a BTEditor and dropping onto * another editor is not supported. * * @author Ricardo Juan Palma Durán * */ private class BTEditorDropTargetListener implements DropTargetListener { public void dragEnter(DropTargetEvent event) { for (int i = 0; i < event.dataTypes.length; i++) { if (BTNodeIndentifierTransfer.getInstance().isSupportedType(event.dataTypes[i])) { event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_MOVE; break; } if (ConceptualBTNodeTransfer.getInstance().isSupportedType(event.dataTypes[i])) { event.currentDataType = event.dataTypes[i]; event.detail = DND.DROP_MOVE; break; } } } public void dragLeave(DropTargetEvent event) { event.detail = DND.DROP_MOVE; } public void dragOperationChanged(DropTargetEvent event) { event.detail = DND.DROP_MOVE; } public void dragOver(DropTargetEvent event) { if (ConceptualBTNodeTransfer.getInstance().isSupportedType(event.currentDataType)) { if (event.item == null) { event.detail = DND.DROP_NONE; return; } BTNode node = (BTNode) event.item.getData(); if (node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString())) { /* * The root node is always allowed as a target for inserting * as a child. */ event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } else { /* * If the target is not the root, then the source node can * be inserted as a child or as a sibling of the target. */ if (!closeToBottom((TreeItem) event.item)) { /* * Cannot move if the limit number of children is * exceeded. */ if (node.getConceptualNode().getNumChildren() != -1) { if (node.getConceptualNode().getNumChildren() <= node.getNumChildren()) { event.detail = DND.DROP_NONE; event.feedback = DND.FEEDBACK_EXPAND; return; } } event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } else { /* * Cannot move if the limit number of children is * exceeded. */ BTNode parent = node.getParent(); if (parent.getConceptualNode().getNumChildren() != -1) { if (parent.getConceptualNode().getNumChildren() <= parent .getNumChildren()) { event.detail = DND.DROP_NONE; event.feedback = DND.FEEDBACK_EXPAND; return; } } event.feedback = DND.FEEDBACK_INSERT_AFTER | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } } event.detail = DND.DROP_MOVE; } else if (BTNodeIndentifierTransfer.getInstance().isSupportedType( event.currentDataType)) { if (event.item == null) { event.detail = DND.DROP_NONE; return; } BTNode node = (BTNode) event.item.getData(); /* Cannot move as a sibling of the root. */ if (node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString())) { event.detail = DND.DROP_NONE; event.feedback = DND.FEEDBACK_EXPAND; } else { BTNode parent = node.getParent(); if (!closeToBottom((TreeItem) event.item)) { /* * Cannot move if the limit number of children is * exceeded. */ if (node.getConceptualNode().getNumChildren() != -1) { if (node.getConceptualNode().getNumChildren() <= node.getNumChildren()) { event.detail = DND.DROP_NONE; event.feedback = DND.FEEDBACK_EXPAND; return; } } event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } else { /* * Cannot move if the limit number of children is * exceeded. */ if (parent.getConceptualNode().getNumChildren() != -1) { if (parent.getConceptualNode().getNumChildren() <= parent .getNumChildren()) { event.detail = DND.DROP_NONE; event.feedback = DND.FEEDBACK_EXPAND; return; } } event.feedback = DND.FEEDBACK_INSERT_AFTER | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } event.detail = DND.DROP_MOVE; } } } public void drop(DropTargetEvent event) { if (ConceptualBTNodeTransfer.getInstance().isSupportedType(event.currentDataType)) { Pair nodeIdentifier = (Pair) event.data; BTNode selectedNode = (BTNode) event.item.getData(); ConceptualBTNode newConceptualNode = NodesLoader.getNode(nodeIdentifier.getFirst(), nodeIdentifier.getSecond()); /* * This is a bit messy. * * If the target node ("selectedNode") is the root: * * 1) If the root has no child, then the dropped node is * inserted as the child of the root. * * 2) If it has one child (the root can only have one child), * the dropped node is inserted as its new only child, and the * old one is inserted as a child of the new one. If the dropped * node does not support children, then the drop operation is * canceled. * * If the target node is not the root, then proceed as usual: the * dropped node is inserted as a child or as a sibling of the * target node. */ if (selectedNode.getConceptualNode().getType() .equals(NodeInternalType.ROOT.toString())) { if (selectedNode.getNumChildren() != 0) { if (newConceptualNode.getNumChildren() == 0) { return; } else { BTNode newNode = tree.createNode(NodesLoader.getNode( nodeIdentifier.getFirst(), nodeIdentifier.getSecond())); BTNode oldRoot = selectedNode.getChildren().get(0); selectedNode.removeChild(oldRoot); newNode.setParent(selectedNode); selectedNode.addChild(newNode); oldRoot.setParent(newNode); newNode.addChild(oldRoot); viewer.refresh(selectedNode); viewer.expandToLevel(selectedNode, 1); treeChanged(viewer); clearErrors(); return; } } } BTNode newNode = tree.createNode(NodesLoader.getNode(nodeIdentifier.getFirst(), nodeIdentifier.getSecond())); /* * Insert the source node as a child or as sibling of the target * depending on the current relative position of the cursor to * the target. */ if (!closeToBottom((TreeItem) event.item)) { newNode.setParent(selectedNode); selectedNode.addChild(newNode); viewer.expandToLevel(selectedNode, 1); viewer.refresh(selectedNode); } else { selectedNode.addAsSibling(newNode); viewer.refresh(selectedNode.getParent()); } treeChanged(viewer); clearErrors(); } if (BTNodeIndentifierTransfer.getInstance().isSupportedType(event.currentDataType)) { BTNode target = (BTNode) event.item.getData(); Identifier id = (Identifier) event.data; BTNode source = tree.findNode(id); if (source.hasAsDescendant(target)) { event.detail = DND.DROP_NONE; return; } /* * Insert the source node as a child or as sibling of the target * depending on the current relative position of the cursor to * the target. */ if (!closeToBottom((TreeItem) event.item)) { target.addChild(source); source.getParent().removeChild(source); source.setParent(target); } else { target.addAsSibling(source); } viewer.refresh(); viewer.expandToLevel(target, 1); treeChanged(viewer); clearErrors(); } } public void dropAccept(DropTargetEvent event) { } /** * Given a TreeItem of the tree, this function analyses whether the * current cursor position is close to its bottom or not. This is used * to tell whether the dropped element is inserted as a child or as a * sibling of the target node. * * @return true if the cursor position is close to the bottom of * treeItem. */ private boolean closeToBottom(TreeItem treeItem) { Display display = treeItem.getDisplay(); Point cursorLocation = treeItem.getDisplay().getCursorLocation(); Rectangle itemLocation = treeItem.getBounds(); int height = viewer.getTree().getItemHeight(); int margin = ((int) height * 0.20) <= 1 ? 1 : (int) (height * 0.20); /* * If the margin is null, return true, since insertion as sibling * prevails over insertion as child. */ if (margin == 0) { return true; } int limitHeight = itemLocation.y + height - 1 - margin; Point mappedCoords = display.map(null, viewer.getTree(), cursorLocation); if (mappedCoords.y >= limitHeight) { return true; } else { return false; } } } /** * Method that is called when the tree has experimented a change. This will * notify all the listeners of the underlying tree by firing a * {@link TreeModifiedEvent}. */ private void treeChanged(Object source) { this.tree.fireTreeChanged(source); } /** * Opens a BTEditor for editing the guard of a BTNode. */ private void openGuardEditor(final BTNode node) throws PartInitException { IWorkbenchPage activePage = Utilities.getMainWindowActivePage(); BTEditorInput editorInput = new BTEditorInput( ((BTEditorInput) getEditorInput()).getEditorID() + File.pathSeparator + node.getID().toString(), false, true); BTEditor openEditor = (BTEditor) activePage.openEditor(editorInput, BTEditor.ID); if (!openGuardEditors.containsKey(node)) { openGuardEditors.put(node, openEditor); } } /** * The IPartListener that BTEditors use to manage close events. When a * BTEditor is closed, two things happen: * *

    *
  • If this is an BTEditor that contains nodes whose guards are being * edited in other editors, then those editors are dissociated from its * corresponding tree, so that they are no longer editing a guard, but a * normal behaviour tree. *
  • If this is a BTEditor that is editing a guard, then it removes itself * from the list of BTEditors {@link BTEditor#openGuardEditors}. *
* * @author Ricardo Juan Palma Durán * */ private class BTEditorPartListener implements IPartListener { public void partActivated(IWorkbenchPart part) { } public void partBroughtToTop(IWorkbenchPart part) { } public void partClosed(IWorkbenchPart part) { /* * Must be kept in mind that this IPartListener is shared among all * the editors, so trying to acces "this" will just not work. The * BTEditor that is closed is the input argument "part". */ if (part instanceof BTEditor) { /* * First dissociate all the editors that are editing nodes' * guards of this tree. */ BTEditor editorToClose = (BTEditor) part; Collection guardEditors = editorToClose.openGuardEditors.values(); for (BTEditor editor : guardEditors) { BTEditorInput editorInput = (BTEditorInput) editor.getEditorInput(); editorInput.setTreeName(editorInput.getName()); editor.dissociateFromParentTree(); editor.dirty = true; editor.firePropertyChange(PROP_TITLE); editor.firePropertyChange(PROP_DIRTY); } // if (guardEditors.size() != 0) { // StandardDialogs // .informationDialog( // "Guards still open", // "The closed behaviour tree contained some open guards. They have been dissociated from the tree, but they are kept open."); // } guardEditors.clear(); /* * Then, if this editor is editing a guard, remove itself from * the "openGuardEditors" list of the editor that contains the * tree that has the node whose guard is being edited by this * editor. */ if (editorToClose.isFromGuard()) { BTEditorInput editorInput = (BTEditorInput) editorToClose.getEditorInput(); String[] pieces = editorInput.getTreeName().split(File.pathSeparator); long parentEditorID = Long.parseLong(pieces[0]); BTEditor parentEditor = Utilities.getBTEditor(parentEditorID); if (parentEditor != null) { parentEditor.openGuardEditors.remove(editorToClose.guardNode); } } } } public void partDeactivated(IWorkbenchPart part) { } public void partOpened(IWorkbenchPart part) { } } /** * This is used for BTEditors that are editing guards. It dissociates the * BTEditor from the tree that contains the node whose guard is being * edited. By doing so, this editor will be considered not to come from a guard * (setIsFromGuard(false)), so it will change its title image to * that of a normal BT. Also, {@link BTEditor#guardNode} and * {@link BTEditor#guardTree} are set to null. Finally, the root node of the * tree (that of type ROOT) is modified so that it is a normal ROOT node * (that is, it can have a name). */ private void dissociateFromParentTree() { if (isFromGuard()) { this.setIsFromGuard(false); this.setTitleImage(ApplicationIcons.getIcon(IconsPaths.BT)); BTNode newRoot = this.tree.createNode(NodesLoader.getNode( NodeInternalType.ROOT.toString(), null)); if (this.tree.getRoot().getChildren().size() != 0) { newRoot.addChild(this.tree.getRoot().getChildren().get(0)); } this.tree.setRoot(newRoot); this.guardNode = null; this.guardTree = null; this.viewer.refresh(); this.viewer.expandAll(); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/editor/BTEditorCopyAndPasteManager.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.editor; import jbt.tools.bteditor.model.BTNode; /** * This class manages the copy and paste mechanism implemented by the * {@link BTEditor} class. *

* This class offers two main methods, {@link #copy(BTNode)} and * {@link #paste()}. When copy() gets called, the * BTEditorCopyAndPasteManager creates a copy (by cloning) of the BTNode to * copy. From then on, whenever paste() is called, a new copy (by * cloning) of it is returned. *

* This is a singleton class, so there is only one instance of * BTEditorCopyAndPasteManager. * * @author Ricardo Juan Palma Durán * */ public class BTEditorCopyAndPasteManager { /** The only instance of BTEditorCopyAndPasteManager. */ private static BTEditorCopyAndPasteManager instance; /** The BTNode that has been copied. */ private BTNode copiedBranch; /** * Copies the node branchToCopy. This makes the * BTEditorCopyAndPasteManager store a copy (by calling * {@link BTNode#clone()}) of branchToCopy. From then on, * {@link #paste()} will return a copy of the copy created by this method. */ public void copy(BTNode branchToCopy) { this.copiedBranch = branchToCopy.clone(); } /** * This method can be called only after {@link #copy(BTNode)} has been * called. This method returns a copy (by invoking {@link BTNode#clone()}) * of the node that was copied by {@link #copy(BTNode)}. */ public BTNode paste() { return this.copiedBranch.clone(); } /** * Returns the only instance of BTEditorCopyAndPasteManager. */ public static BTEditorCopyAndPasteManager getInstance() { if (instance == null) { instance = new BTEditorCopyAndPasteManager(); } return instance; } /** * Returns true if a {@link #copy(BTNode)} operation has been made, or false * if not. */ public boolean hasCopy() { return this.copiedBranch != null; } /** * Private constructor to force the singleton pattern. */ private BTEditorCopyAndPasteManager() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/editor/BTEditorIDGenerator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.editor; /** * A singleton class that generates IDs for BTEditor objects. Every new BTEditor * has an unique ID which is represented as a long value. * * @author Ricardo Juan Palma Durán * */ public class BTEditorIDGenerator { /** The only instance of this class. */ private static BTEditorIDGenerator instance; /** Counter for generated IDs. */ private long counter = 0; /** * Returns the only instance of BTEditorIDGenerator. */ public static BTEditorIDGenerator getInstance() { if (instance == null) { instance = new BTEditorIDGenerator(); } return instance; } /** * Returns the next BTEditor ID. */ public long getNextID() { return this.counter++; } /** * Private constructor to force the singleton pattern. */ private BTEditorIDGenerator() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/editor/BTEditorInput.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.editor; import java.io.File; import jbt.tools.bteditor.util.Utilities; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IPersistableElement; /** * EditorInput for a {@link BTEditor_}. *

* The BTEditorInput is used when creating a new BTEditor. It contains the name * of the tree and several fields that indicate where the tree comes from. *

* A behaviour tree may have three different origins: *

    *
  • New behaviour tree, in which case the tree is created from scratch (it is * a new empty tree). *
  • From a file, in which case the name of the tree is used as the name of * the file that contains the tree. *
  • From a node's guard, in which case the tree name must be the ID of the * editor (see {@link BTEditorIDGenerator}) that contains the tree that contains * the guard, followed by the {@link File#separatorChar}, followed by the ID of * the node that contains such guard. For instance, on Windows, a valid name * would be "23;Node_17" *
* * The BTEditorInput also contains the ID of its corresponding BTEditor. Such ID * is generated by the BTEditorInput at construction time through the class * {@link BTEditorIDGenerator}. * * @author Ricardo Juan Palma Durán * */ public class BTEditorInput implements IEditorInput { /** * Name of the tree. If {@link #isFromFile} is true, then it represents the * name of the file that contains the tree that will be loaded when opening * the editor. */ private String treeName; /** Flag that indicates if the tree comes from a file or not. */ private boolean isFromFile; /** Flag that indicates if the tree comes from a guard. */ private boolean isFromGuard; /** The ID of the editor associated to this guard. */ private long editorID; /** * In case the tree comes from a guard, this stores the name and tooltip * returned by {@link #getName()} and {@link #getToolTipText()}. */ private String guardsName; /** * Constructor. Constructs a new BTEditorInput for opening a BTEditor. The * behaviour tree that is loaded into the BTEditor depends on the * BTEditorInput. Actually, a behaviour tree may have three different * origins: * *
    *
  • New behaviour tree, in which case the tree is created from scratch * (it is a new empty tree), treeName is the name of the tree, * isFromFile must be false and isFromGuard must * be false. *
  • From a file, in which case treeName is used as the name * of the file that contains the tree, isFromFile must be true * and isFromGuard must be false. *
  • From a node's guard, in which case treeName must be the * ID of the editor (see {@link BTEditorIDGenerator}) that contains the tree * that contains the guard, followed by the {@link File#separatorChar}, * followed by the ID of the node that contains such guard. For instance, on * Windows, a valid name would be "23;Node_17". isFromFile must * be false and isFromGuard must be true. *
* * * @param treeName * the name of the tree. * @param isFromFile * true if the tree must be loaded from a file, and false * otherwise. Note that isFromFile and * isFromGuard cannot be both true. * @param isFromGuard * true if the tree comes from a guard, and false otherwise. Note * that isFromFile and isFromGuard * cannot be both true. */ public BTEditorInput(String treeName, boolean isFromFile, boolean isFromGuard) { this.treeName = treeName; this.isFromFile = isFromFile; this.isFromGuard = isFromGuard; if (this.isFromFile == this.isFromGuard && this.isFromFile) { throw new RuntimeException("A tree cannot come both from a file and from a guard"); } this.editorID = BTEditorIDGenerator.getInstance().getNextID(); if (this.isFromGuard) { String[] pieces = this.treeName.split(File.pathSeparator); BTEditor editor = Utilities.getBTEditor(Long.parseLong(pieces[0])); this.guardsName = "Guard of " + pieces[1] + " in " + ((BTEditorInput) editor.getEditorInput()).getName(); } } /** * Returns the name of the tree. *

* If {@link #isFromFile()} is true, this name is the name of the file that * contains the tree. *

* If {@link #isFromGuard()} is true, this is the ID of the editor (see * {@link BTEditorIDGenerator}) that contains the tree that contains the * guard, followed by the {@link File#separatorChar}, followed by the ID of * the node that contains such guard. * */ public String getTreeName() { return this.treeName; } /** * Sets the name of the tree. *

* If {@link #isFromFile()} is true, this name must be the name of the file * that contains the tree. *

* If {@link #isFromGuard()} is true, this must be the ID of the editor (see * {@link BTEditorIDGenerator}) that contains the tree that contains the * guard, followed by the {@link File#separatorChar}, followed by the ID of * the node that contains such guard. */ public void setTreeName(String treeName) { this.treeName = treeName; } /** * Returns if the tree comes from a file. */ public boolean isFromFile() { return this.isFromFile; } /** * Sets if the tree comes from a file. */ public void setIsFromFile(boolean isFromFile) { this.isFromFile = isFromFile; } /** * Returns true if the tree comes from a guard, and false otherwise. */ public boolean isFromGuard() { return isFromGuard; } /** * Sets if the tree comes from a guard. */ public void setIsFromGuard(boolean isFromGuard) { this.isFromGuard = isFromGuard; } /** * Returns the ID of the BTEditor managed by this BTEditorInput. */ public long getEditorID() { return this.editorID; } /** * * @see org.eclipse.ui.IEditorInput#exists() */ public boolean exists() { return false; } /** * * @see org.eclipse.ui.IEditorInput#getImageDescriptor() */ public ImageDescriptor getImageDescriptor() { return null; } /** *

    *
  • If {@link #isFromFile()} is true, then returns the short version of * the file returned by {@link #getTreeName()}.. *
  • If {@link #isFromGuard()} is true, returns * Guard of XXXX in YYYY, where XXXX is the ID of * the guard the tree comes from, and YYYY is the name of the * tree (as obtained by this {@link #getName()}) that contains the guard. *
  • If both are false, then returns {@link #getTreeName()}. * * @see org.eclipse.ui.IEditorInput#getName() */ public String getName() { if (this.isFromFile) { File f = new File(this.treeName); return f.getName(); } else if (this.isFromGuard) { return this.guardsName; } else { return this.treeName; } } /** * * @see org.eclipse.ui.IEditorInput#getPersistable() */ public IPersistableElement getPersistable() { return null; } /** * If {@link #isFromFile()} is false or {@link #isFromGuard()} is false, * this method returns {@link #getTreeName()}. If {@link #isFromGuard()} is * true, returns {@link #getName()}. * * @see org.eclipse.ui.IEditorInput#getToolTipText() */ public String getToolTipText() { if (this.isFromGuard) { return getName(); } return getTreeName(); } /** * * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ public Object getAdapter(Class adapter) { return null; } /** * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof BTEditorInput)) { return false; } BTEditorInput input = (BTEditorInput) o; if (this.treeName.equals(input.treeName) && (this.isFromFile == input.isFromFile) && (this.isFromGuard == input.isFromGuard)) { return true; } return false; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/event/ITreeModifierListener.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.event; import jbt.tools.bteditor.model.BT; /** * Interface for those entities that listen to changes in a behaviour tree ( * {@link BT}) * * @author Ricardo Juan Palma Durán * */ public interface ITreeModifierListener { /** * Called when a change in the BT that is being listened occurs. */ public void treeModified(TreeModifiedEvent event); } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/event/TreeModifiedEvent.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.event; import java.util.EventObject; import jbt.tools.bteditor.model.BT; /** * Event issued when a {@link BT} is modified. * * @author Ricardo Juan Palma Durán * */ public class TreeModifiedEvent extends EventObject { private static final long serialVersionUID = 1L; private BT tree; /** * Constructor. Must specify the tree that has been modified. */ public TreeModifiedEvent(Object source, BT tree) { super(source); this.tree = tree; } /** * Returns the tree that has been modified. */ public BT getTree() { return this.tree; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/model/BT.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.model; import java.util.List; import java.util.Vector; import jbt.tools.bteditor.event.ITreeModifierListener; import jbt.tools.bteditor.event.TreeModifiedEvent; import jbt.tools.bteditor.model.BTNode.Identifier; import jbt.tools.bteditor.model.ConceptualBTNode.Parameter; import jbt.tools.bteditor.model.ConceptualBTNode.ParameterType; /** * A BT represents a behaviour tree that can be edited, loaded and exported. This type of trees is * edited in the {@link BTEditor_}. A BT only stores the root of the tree, which is a {@link BTNode} * . * * @author Ricardo Juan Palma Durán * */ public class BT { /** The root of the tree. */ private BTNode root; /** * Counter of how many nodes have been created through the {@link #createNode(ConceptualBTNode)} * . This counter is used when generating ids for nodes of this tree. */ private long nodeCounter = 0; /** Listeners that are listening to changes in the tree. */ private List listeners = new Vector(); /** * Default constructor. Constructs a BT with no root. */ public BT() { } /** * Constructs a BT with a root. */ public BT(BTNode root) { this.root = root; updateNodeCounter(); } /** * Sets the root of the tree. */ public void setRoot(BTNode root) { this.root = root; updateNodeCounter(); } /** * Returns the root of the tree or null in case it has not been set. */ public BTNode getRoot() { return this.root; } /** * Finds a node in the tree, by using an identifier. Returns null if not found. */ public BTNode findNode(Identifier id) { return internalFindNode(this.root, id); } /** * Creates a BTNode from a ConceptualBTNode for this tree. This method creates a BTNode and sets * its ID to an unique identifier for the tree. Also it sets the underlying conceptual node of * the BTNode to conceptualNode. */ public BTNode createNode(ConceptualBTNode conceptualNode) { BTNode node = new BTNode(new Identifier(nodeCounter)); nodeCounter++; node.setConceptualNode(conceptualNode); node.setBT(this); return node; } /** * Adds a listener that will be notified when changes in the tree occur. */ public void addTreeModifiedListener(ITreeModifierListener listener) { this.listeners.add(listener); } /** * This method must be called when there is a change in the tree. By doing so, all entities that * are listening to changes in the tree will be notified. */ public void fireTreeChanged(Object source) { for (ITreeModifierListener l : this.listeners) { l.treeModified(new TreeModifiedEvent(source, this)); } } /** * Checks the validity of all the nodes of the tree. Returns a list with the nodes that contain * errors. */ public List checkTree() { List nodes = new Vector(); internalCheckTree(this.root, nodes); return nodes; } /** * Clear the errors of all of the nodes of the tree. */ public void clearErrors() { internalClearErrors(this.root); } /** * Given a BTNode, this method recompute its ID as well as the IDs of all its children and * guards. After calling this method, these IDs will not clash any of those of this BT's nodes. * This method can be used, for example, when a whole branch must be added to this tree. In such * case, this method can be used to recompute the IDs of the nodes of the branch so that there * is no conflict among IDs. *

    * It is important to notice that after recomputing the IDs, they are still consistent among * each other. For instance, if before recomputing the ID a node A had ID "Node_34", and * it referenced by another node B, after recomputing IDs the reference will still be * valid (it will not be "Node_34", but whatever new ID A has will be properly referenced * by B). * * @param node * the root of the branch (tree) whose nodes will get their IDs recomputed. */ public void recomputeIDs(BTNode node) { Identifier largestID = computeLargestID(this.root); Identifier lowestID = computeLowestID(node); reassignIDs(node, largestID.getValue() - lowestID.getValue() + 1); } /** * Given a BTNode, this method sets, as the current underlying BT of node and all * of its descendants (including guards), this BT. * * @param node * the root of the branch (tree) whose underlying BT will be modified. */ public void reassignUnderlyingBT(BTNode node) { node.setBT(this); for (BTNode child : node.getChildren()) { reassignUnderlyingBT(child); } if (node.getGuard() != null) { reassignUnderlyingBT(node.getGuard()); } } /** * Reassigns the all the IDs in the branch whose root is node. This method just * adds offset to the value of all the nodes (including guards) of the branch. */ private void reassignIDs(BTNode node, long offset) { node.getID().setValue(node.getID().getValue() + offset); /* If some parameter has type "node_id", then it must be changed too. */ if (node.getParameters().size() != 0) { for (int i = 0; i < node.getConceptualNode().getParameters().size(); i++) { Parameter cParameter = node.getConceptualNode().getParameters().get(i); if (cParameter.getType() == ParameterType.NODE_ID) { Identifier newValue = new Identifier(node.getParameters().get(i).getValue()); newValue.setValue(newValue.getValue() + offset); node.getParameters().get(i).setValue(newValue.toString()); } } } for (BTNode child : node.getChildren()) { reassignIDs(child, offset); } if (node.getGuard() != null) { reassignIDs(node.getGuard(), offset); } } /** * Computes the largest ID of a whole branch of a BT (including guards). The root of the branch * is node. */ private Identifier computeLargestID(BTNode node) { Identifier largestKnownID = node.getID().clone(); for (BTNode child : node.getChildren()) { computeLargestID(child, largestKnownID); } if (node.getGuard() != null) { Identifier guardLargestIdentifier = computeLargestID(node.getGuard()); if (guardLargestIdentifier.getValue() > largestKnownID.getValue()) { largestKnownID.setValue(guardLargestIdentifier.getValue()); } } return largestKnownID; } /** * Stores into largestKnownID (which initially must be a valid Identifier for the * BT) the largest Identifier of the nodes in node (including guards). */ private void computeLargestID(BTNode node, Identifier largestKnownID) { if (node.getID().getValue() > largestKnownID.getValue()) { largestKnownID.setValue(node.getID().getValue()); } for (BTNode currentChild : node.getChildren()) { computeLargestID(currentChild, largestKnownID); } if (node.getGuard() != null) { Identifier guardLargestIdentifier = computeLargestID(node.getGuard()); if (guardLargestIdentifier.getValue() > largestKnownID.getValue()) { largestKnownID.setValue(guardLargestIdentifier.getValue()); } } } /** * Computes the lowest ID of a whole branch of a BT (including guards). The root of the branch * is node. */ private Identifier computeLowestID(BTNode node) { Identifier lowestKnownID = node.getID().clone(); for (BTNode child : node.getChildren()) { computeLowestID(child, lowestKnownID); } if (node.getGuard() != null) { Identifier guardLowestIdentifier = computeLowestID(node.getGuard()); if (guardLowestIdentifier.getValue() < lowestKnownID.getValue()) { lowestKnownID.setValue(guardLowestIdentifier.getValue()); } } return lowestKnownID; } /** * Stores into lowestKnownID (which initially must be a valid Identifier for the * BT) the lowest Identifier of the nodes in node (including guards). */ private void computeLowestID(BTNode node, Identifier lowestKnownID) { if (node.getID().getValue() < lowestKnownID.getValue()) { lowestKnownID.setValue(node.getID().getValue()); } for (BTNode currentChild : node.getChildren()) { computeLowestID(currentChild, lowestKnownID); } if (node.getGuard() != null) { Identifier guardLowestIdentifier = computeLowestID(node.getGuard()); if (guardLowestIdentifier.getValue() < lowestKnownID.getValue()) { lowestKnownID.setValue(guardLowestIdentifier.getValue()); } } } /** * Clear the errors of the nodes of the tree whose root is node . */ private void internalClearErrors(BTNode node) { node.clearErrors(); for (BTNode child : node.getChildren()) { internalClearErrors(child); } } /** * Checks the validity of all the nodes of the tree whose root is node, and stores * those that are incorrect into ids. */ private void internalCheckTree(BTNode node, List nodes) { if (!node.check()) { nodes.add(node); } for (BTNode child : node.getChildren()) { internalCheckTree(child, nodes); } } /** * Finds a node by identifier, starting the search in currentNode. This is a * recursive method that is called again on the children. Returns null if the node cannot be * found. */ private BTNode internalFindNode(BTNode currentNode, Identifier id) { if (currentNode.getID().equals(id)) { return currentNode; } for (BTNode n : currentNode.getChildren()) { BTNode nodeFound = internalFindNode(n, id); if (nodeFound != null) { return nodeFound; } } return null; } /** * Updates the internal counter that the BT uses to create nodes via * {@link #createNode(ConceptualBTNode)}, so that the counter is bigger than the biggest number * associated to any of the node's identifiers in the tree. This method should be called when * the tree gets removed or added nodes. Note that it is automatically called when * {@link #setRoot(BTNode)} is called. */ public void updateNodeCounter() { this.nodeCounter = 1; recursiveUpdateNodeCounter(this.root); } /** * Updates the field {@link #nodeCounter} so that the counter is bigger than the biggest number * associated to any of the identifiers of descendants of * currentNode (including itself). */ private void recursiveUpdateNodeCounter(BTNode currentNode) { long order = currentNode.getID().getValue(); if (order >= this.nodeCounter) { this.nodeCounter = order + 1; } for (BTNode child : currentNode.getChildren()) { recursiveUpdateNodeCounter(child); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/model/BTNode.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.model; import java.io.Serializable; import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import jbt.tools.bteditor.model.ConceptualBTNode.NodeInternalType; import jbt.tools.bteditor.model.ConceptualBTNode.ParameterType; /** * The node of a behaviour tree that can be edited by the {@link BTEditor_}. * These are the nodes that a {@link BT} is composed of. *

    * A BTNode stores a list with the parameters of the node and their value. It * also stores the list of children of the node (which are other BTNode * objects). A BTNode also has a parent (or null in case it is the root of the * tree). A BTNode has an identifier ({@link Identifier}) that must be unique * among the nodes of the tree. *

    * A BTNode also stores a reference to a {@link ConceptualBTNode}. The * ConceptualBTNode is used for getting conceptual information about the node. * It could be said that, on the one hand, the ConceptualBTNode stores the * conceptual structure of the node, while on the other, the BTNode stores a * particular instance of a ConceptualBTNode (that is, the children the node * has, as well as values for each parameter). *

    * For a particular BT, BTNode objects should be created through * {@link BT#createNode(ConceptualBTNode)}. *

    * This class extends {@link Observable} so that {@link Observer}s can be * notified when changes take place. Basically, all the methods that somehow * change the BTNode will notify all Observers by calling * {@link #notifyObservers()}. Even this may not seem very efficient, we do not * have strong constraints on the performance of this application. * * @author Ricardo Juan Palma Durán * */ public class BTNode extends Observable implements Serializable { private static final long serialVersionUID = 1L; /** Name of the node. This is used only for the root of the tree. */ private String name; /** List of the parameters of the node. */ private List parameters; /** List of the children of the node. */ private List children; /** Guard of the node. */ private BTNode guard; /** Parent of the node. May be null if it has no parent. */ private BTNode parent; /** The underlying conceptual node. */ private ConceptualBTNode conceptualNode; /** * The current error message of the node. This is set when the node is * checked, and contains a description of any error found in the node. */ private String errorMessage; /** Identifier of the node. */ private Identifier ID; /** BT that the BTNode belongs to. */ private BT tree; /** Pattern for verifying {@link ParameterType#LIST_OF_VARIABLES} values. */ private static final Pattern pattern = Pattern.compile("(( )*\"[a-zA-Z_0-9\\s]+\"( )*)+"); /** * Identifier of a node. The identifier of a node is an integer, and its * representation as a String is "Node_X", where "X" is the integer. Note * that the integer must not be a negative value. * * @author Ricardo Juan Palma Durán * */ public static class Identifier implements Serializable { private static final long serialVersionUID = 1L; /** * The identifier is just an integer value. The string version of the * identifier is "Node_X", where "X" is #value. */ private long value; /** * Constructs an Identifier from a long value. */ public Identifier(long value) { if (value < 0) { throw new RuntimeException("Negative value are not allowed."); } this.value = value; } /** * Constructs an Identifier from a String. The String must be of the * form "Node_X", where "X" is a non-negative number. id is * initially trimmed. * * @param id */ public Identifier(String id) { try { String newID = id.trim(); String[] pieces = newID.split("_"); if (pieces.length != 2) { throw new RuntimeException(); } if (!pieces[0].equals("Node")) { throw new RuntimeException(); } this.value = Long.parseLong(pieces[1]); if (this.value < 0) { throw new RuntimeException("Negative value are not allowed."); } } catch (Exception e) { throw new RuntimeException( "Invalid String representation for an Identifier: " + id, e); } } public String toString() { return "Node_" + value; } public long getValue() { return this.value; } public void setValue(long value) { if (value < 0) { throw new RuntimeException("Negative value are not allowed."); } this.value = value; } public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Identifier)) { return false; } return this.value == ((Identifier) o).value; } /** * Makes a deep copy of this Identifier. * * @see java.lang.Object#clone() */ public Identifier clone() { return new Identifier(this.value); } } /** * A parameter of a BTNode. A BTNode parameter is composed of a name and a * value. Both name and value are strings. * * @author Ricardo Juan Palma Durán * */ public static class Parameter implements Serializable { private static final long serialVersionUID = 1L; /** Name of the parameter. */ private String value; /** Value of the parameter. */ private String name; /** * Flag that indicates whether the parameter must be read from the * context or not. If the parameter must be read from the context, its * value must be a non-empty string. */ private boolean fromContext; /** * Returns the name of the parameter, or null if not set. */ public String getName() { return name; } /** * Sets the name of the parameter. */ public void setName(String name) { this.name = name; } /** * Returns the value of the parameter, or null if not set. */ public String getValue() { return value; } /** * Sets the value of the parameter. */ public void setValue(String value) { this.value = value; } /** * Sets if the parameter must be read from the context or not. */ public void setFromContext(boolean fromContext) { this.fromContext = fromContext; } /** * Returns true if the value must be read from the context, and false * otherwise. */ public boolean getFromContext() { return this.fromContext; } /** * Makes a deep copy of this Parameter. * * @see java.lang.Object#clone() */ public Parameter clone() { Parameter copy = new Parameter(); copy.fromContext = this.fromContext; if (this.value != null) copy.value = new String(this.value); else copy.value = null; if (this.name != null) copy.name = new String(this.name); else copy.name = null; return copy; } } public static class VarParameter extends Parameter { private static final long serialVersionUID = 1L; /** Name of the field that describes the parameter. */ private String variableName; private boolean isConstant; public VarParameter(Parameter par) { super.value = par.value; super.name = par.name; super.fromContext= par.fromContext; } /** * Gets if the parameter is a key-value pair or not */ public boolean getIsConstant() { return isConstant; } /** * Sets if the parameter is a key-value pair or not */ public void setIsConstant(boolean isConstant) { this.isConstant = isConstant; } /** * Returns the name of the field stored in the parameter */ public String getVariableName() { return variableName; } public void setVariableName(String value) { this.variableName = value; } /** * Makes a deep copy of this Parameter. * * @see java.lang.Object#clone() */ public VarParameter clone() { VarParameter copy = new VarParameter(super.clone()); if (this.variableName != null) copy.variableName = this.variableName; else copy.variableName = null; copy.isConstant = this.isConstant; return copy; } } /** * Default constructor. Constructs a BTNode with no identifier. */ public BTNode() { this.parameters = new Vector(); this.children = new Vector(); } /** * Constructs a BTNode with an identifier. */ public BTNode(Identifier id) { this.parameters = new Vector(); this.children = new Vector(); this.ID = id; } /** * Returns the BT that this BTNode belongs to, or null if not set yet. * * @return the BT that this BTNode belongs to, or null if not set yet. */ public BT getBT() { return this.tree; } /** * Sets the BT that this BTNode belongs to. * * @param tree * the BT that this BTNode belongs to. */ public void setBT(BT tree) { this.tree = tree; } /** * Returns the underlying conceptual node, or null if not set yet. */ public ConceptualBTNode getConceptualNode() { return conceptualNode; } /** * Sets the underlying conceptual node. */ public void setConceptualNode(ConceptualBTNode conceptualNode) { this.conceptualNode = conceptualNode; setChanged(); notifyObservers(); } /** * Gets the parent of the node. Returns null if not set. If the node has no * parent, it also returns null. */ public BTNode getParent() { return parent; } /** * Sets the parent of the node. May be null for the root of the tree. */ public void setParent(BTNode parent) { this.parent = parent; setChanged(); notifyObservers(); } /** * Returns the identifier of the node, or null if not set. */ public Identifier getID() { return this.ID; } /** * Returns the name of the node, or null if not set. */ public String getName() { return name; } /** * Sets the name of the node. */ public void setName(String name) { this.name = name; setChanged(); notifyObservers(); } /** * Returns the parameters of the node, or an empty list if it has none. */ public List getParameters() { return parameters; } /** * Sets the list of parameters of the node. Can be an empty list if no * parameter is wanted. */ public void setParameters(List parameters) { this.parameters = parameters; setChanged(); notifyObservers(); } /** * Returns the number of children of the node. */ public int getNumChildren() { return this.children.size(); } /** * Returns a list with all the children of the node, or an empty list if it * has none. */ public List getChildren() { return this.children; } /** * Returns the error message, or null if no error message. */ public String getErrorMessage() { return this.errorMessage; } /** * Adds a child to the list of children of this node. */ public void addChild(BTNode child) { this.children.add(child); setChanged(); notifyObservers(); } /** * Removes a child from the list of children of the node. */ public void removeChild(BTNode child) { this.children.remove(child); setChanged(); notifyObservers(); } /** * Adds a parameter to the list of parameters of this node. */ public void addParameter(Parameter parameter) { this.parameters.add(parameter); setChanged(); notifyObservers(); } /** * Makes other be a sibling of this node. This method inserts * other into the list of children of this's parent, and * removes it from the list of children of its current parent (if it has * any). This node must have a parent. Otherwise, this method does nothing. */ public void addAsSibling(BTNode other) { if (this.parent != null) { if (other.hasAsDescendant(this)) { return; } if (other.parent != null) { other.parent.getChildren().remove(other); } int thisPos = this.parent.getChildren().indexOf(this); this.parent.getChildren().add(thisPos + 1, other); other.parent = this.parent; setChanged(); notifyObservers(); } } /** * Sets the identifier of this node. */ public void setID(Identifier id) { this.ID = id; setChanged(); notifyObservers(); } /** * Sets the guard of the node. * * @param guard */ public void setGuard(BTNode guard) { this.guard = guard; } /** * Returns the guard of the node, or null if it has no guard. */ public BTNode getGuard() { return this.guard; } /** * Returns true if other is a descendant of this node, and * false otherwise. */ public boolean hasAsDescendant(BTNode other) { if (other == this) { return true; } for (BTNode n : this.getChildren()) { if (n.hasAsDescendant(other)) { return true; } } return false; } /** * Clears the list of parameters of this node. */ public void clearParameters() { this.parameters.clear(); setChanged(); notifyObservers(); } /** * Clears the errors of this node. */ public void clearErrors() { this.errorMessage = null; setChanged(); notifyObservers(); } /** * Checks if this node has no error in its structure. The structure of the * node is checked against the underlying ConceptualNode. Returns true if no * error is detected, and false otherwise. In case there is any error, a * description of it will be accessible through {@link #getErrorMessage()}. * */ public boolean check() { boolean result = true; boolean hadErrors = this.errorMessage != null; /* This is for the root node. */ if (this.conceptualNode.getHasName() && this.conceptualNode.getType().equals(NodeInternalType.ROOT.toString())) { if (this.name == null || this.name.equals("")) { this.errorMessage = "Name cannot be empty"; result = false; } } if (this.conceptualNode.getNumChildren() != 0) { if (this.conceptualNode.getNumChildren() == -1) { if (this.children.size() == 0) { this.errorMessage = "Must have at least one child"; result = false; } } else { if (this.children.size() != this.conceptualNode.getNumChildren()) { this.errorMessage = "Must have exactly " + this.conceptualNode.getNumChildren() + " children"; result = false; } } } else { if (this.children.size() != 0) { this.errorMessage = "This type of node cannot have children"; result = false; } } if (conceptualNode.getParameters().size() != 0) { if (this.parameters.size() != conceptualNode.getParameters().size()) { this.errorMessage = "Not all the parameters have a value"; result = false; } else { for (int i = 0; i < conceptualNode.getParameters().size(); i++) { jbt.tools.bteditor.model.ConceptualBTNode.Parameter p = conceptualNode .getParameters().get(i); String actualValue; try { actualValue = this.parameters.get(i).getValue(); if (this.parameters.get(i).getFromContext()) { if (actualValue.equals("")) { this.errorMessage = "Invalid value for parameter " + p.getName(); result = false; } } else { if (ParameterType.isVariable(p.getType())) { BTNode.VarParameter varParameter = (BTNode.VarParameter)this.parameters.get(i); String defaultTextValue = varParameter.getVariableName(); if (!checkVarParameter(p, actualValue, defaultTextValue, varParameter.getIsConstant(), this.tree)){ this.errorMessage = "Invalid value for parameter " + p.getName(); result = false; } } if (!checkParameter(p, actualValue, this.tree)) { this.errorMessage = "Invalid value for parameter " + p.getName(); result = false; } } } catch (Exception e) { this.errorMessage = e.getMessage(); result = false; } } } } /* Do not forget to check the guard. */ if (this.guard != null) { if (!this.guard.check()) { this.errorMessage = "Error in the guard: " + this.guard.errorMessage; result = false; } } if (result) { if (hadErrors) { this.errorMessage = null; setChanged(); notifyObservers(); } } else { setChanged(); notifyObservers(); } return result; } /** * Checks if the value of a parameter is consistent with the type of the * parameter. Returns true if no error is detected, and false otherwise. The * BT that the parameter is supposed to belong must be provided, because the * correctness of some values can only be checked within a BT (for instance, * the {@link ParameterType#NODE_ID}). */ public static boolean checkParameter( jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameterDefinition, String value, BT tree) { ParameterType type = parameterDefinition.getType(); if (type == ParameterType.INTEGER) { try { Integer.parseInt(value); return true; } catch (Exception e) { return false; } } else if (type == ParameterType.REAL) { try { Double.parseDouble(value); return true; } catch (Exception e) { return false; } } else if (type == ParameterType.STRING) { if (value.length() == 0) { return false; } else { return true; } } else if (type == ParameterType.STATUS_CODE) { String[] values = ParameterType.STATUS_CODE.getPossibleValues(); for (String currentValue : values) { if (value.equals(currentValue)) { return true; } } return false; } else if (type == ParameterType.NODE_ID) { if (value.length() == 0) { return false; } else { try { Identifier id = new Identifier(value); /* * Check that node exists and that its type is compatible with * the types that the parameter supports. */ BTNode referencedNode = tree.findNode(id); if (referencedNode != null) { if (parameterDefinition.getNodeClasses().contains( referencedNode.getConceptualNode().getType()) || parameterDefinition.getNodeClasses().size() == 0) { return true; } } return false; } catch (Exception e) { return false; } } } else if (type == ParameterType.BOOLEAN) { String[] values = ParameterType.BOOLEAN.getPossibleValues(); for (String currentValue : values) { if (value.equals(currentValue)) { return true; } } return false; } else if (type == ParameterType.COORDINATE) { String[] numbers = value.split("( )+"); for (String number : numbers) { try { Double.parseDouble(number); } catch (Exception e) { return false; } } return true; } else if (type == ParameterType.DIRECTION) { try { Double.parseDouble(value); } catch (Exception e) { return false; } return true; } else if (type == ParameterType.PARALLEL_POLICY) { String[] values = ParameterType.PARALLEL_POLICY.getPossibleValues(); for (String currentValue : values) { if (value.equals(currentValue)) { return true; } } return false; } else if (type == ParameterType.LIST_OF_VARIABLES) { Matcher matcher = pattern.matcher(value); return matcher.matches(); } else if (ParameterType.isVariable(type)) { if (value.length() == 0) { return false; } else { return true; } } throw new IllegalArgumentException("Unexpected parameter type " + type.toString()); } /** * Checks if the value of a var parameter is consistent with the type of the * parameter. This is done this way to ensure that both the fields of the key-value pair * are well formed * the {@link ParameterType#NODE_ID}). */ public static boolean checkVarParameter( jbt.tools.bteditor.model.ConceptualBTNode.Parameter parameterDefinition, String value, String variableName, boolean isConstant, BT tree) { ParameterType type = parameterDefinition.getType(); if (ParameterType.isVariable(type)) { if (!isConstant) { if (variableName.length() == 0) return false; } switch (parameterDefinition.getType()) { case VARIABLE_FLOAT: { try { Float.parseFloat(value); if (value.length() == 0) return false; } catch (Exception e) { return false; } break; } case VARIABLE_INT: { try { Integer.parseInt(value); if (value.length() == 0) return false; } catch (Exception e) { return false; } break; } case VARIABLE_STRING: { if (value.length() == 0) return false; break; } } return true; } throw new IllegalArgumentException("Unexpected parameter type " + type.toString()); } /** * Makes a deep copy of this BTNode. Children are recursively cloned, as * well as the guard. The underlying ConceptualBTNode and the BT that this * BTNode belongs to are not deeply copied, but just assigned. The parent of * the returned node is null, but its children are properly linked. *

    * This method should be carefully used. Keep in mind that node identifiers * are unique in a BT, so the returned copy is only valid as long as the BT * it belongs to does not have any node with the same name. Otherwise, the * identifiers should be changed. * * @see java.lang.Object#clone() */ public BTNode clone() { BTNode copy = new BTNode(); if (this.conceptualNode != null) copy.conceptualNode = this.conceptualNode; else copy.conceptualNode = null; if (this.errorMessage != null) copy.errorMessage = this.errorMessage; else copy.errorMessage = null; if (this.guard != null) copy.guard = this.guard.clone(); else copy.guard = null; if (this.ID != null) copy.ID = this.ID.clone(); else copy.ID = null; if (this.name != null) copy.name = new String(this.name); else copy.name = null; copy.parameters = new LinkedList(); if (this.parameters.size() != 0) { for (Parameter p : this.parameters) { copy.parameters.add(p.clone()); } } copy.children = new LinkedList(); if (this.children.size() != 0) { for (BTNode child : this.children) { BTNode clonedChild = child.clone(); clonedChild.setParent(copy); copy.children.add(clonedChild); } } copy.tree = this.tree; return copy; } public String toString() { return "ID: " + this.ID + " - Type: " + this.conceptualNode.getReadableType() + " - Num. Children: " + this.children.size(); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/model/ConceptualBTNode.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.model; import gatech.mmpm.ActionParameterType; import java.io.Serializable; import java.util.LinkedList; import java.util.List; import java.util.Vector; import jbt.tools.bteditor.NodesLoader; /** * ConceptualBTNode represents a behaviour tree node conceptually, containing * information about its type of node, how many children it can contain, what * parameters it has, and so on. *

    * A ConceptualBTNode is used for building actual nodes of trees, as well as * checking the validity of actual nodes. * * @author Ricardo Juan Palma Durán * */ public class ConceptualBTNode implements Serializable { private static final long serialVersionUID = 1L; /** * Some types of nodes that are internal to the application. * * @author Ricardo Juan Palma Durán * */ public static enum NodeInternalType { ROOT { public String toString() { return "Root"; } }, ACTION { public String toString() { return "Action"; } }, CONDITION { public String toString() { return "Condition"; } }, SUBTREE_LOOKUP { public String toString() { return "SubtreeLookup"; } } } /** Type that is displayed in the application. */ private String readableType; /** * Actual type of the node. This is the type that is written into the XML * file when exporting a BT. */ private String type; /** * Conceptual description of the parameters of this node. May be empty * (empty list) for nodes with no parameters. */ private List parameters; /** * Number of children of the node. Ranges from -1 to infinity. A value of -1 * means that the node can have from 1 to infinity children. A value * different from -1 means that the node must have exactly that number of * children. */ private int numChildren; /** * Route of the icon that is displayed for this kind of node. */ private String icon; /** * Indicates if the node has a name. */ private boolean hasName; /** * Static name of the node. If {@link #hasName} is false, the no has no * name, an {@link #name} is null. Otherwise, this variable stores the * static name of the node (or null in case it has not been set yet). *

    * Actions and conditions have a static name. */ private String name; /** * Enum that contains all the types of the parameters that are accepted by * behaviour tree nodes. *

    * It must be noted that actions and conditions that are loaded from * external MMPM domain files have some other parameter types that are * converted into ParameterType before using them. For instance, the MMPM * {@link ActionParameterType#ENTITY_ID} is converted into a * {@link ParameterType#STRING} since behaviour trees do not care about * entities IDs. For more information about this conversion, see * {@link NodesLoader#fromMMPMParameterType(ActionParameterType type)}. * * @author Ricardo Juan Palma Durán * */ public static enum ParameterType implements Serializable { BOOLEAN { public String[] getPossibleValues() { return new String[] { "true", "false" }; } public String getReadableType() { return "Boolean"; } }, INTEGER { public String getReadableType() { return "Integer"; } }, REAL { public String getReadableType() { return "Real"; } }, STRING { public String getReadableType() { return "String"; } }, STATUS_CODE { public String[] getPossibleValues() { return new String[] { "success", "failure" }; } public String getReadableType() { return "Status code"; } }, NODE_ID { public String getReadableType() { return "Node id"; } }, PARALLEL_POLICY { public String[] getPossibleValues() { return new String[] { "sequence", "selector" }; } public String getReadableType() { return "Parallel policy"; } }, COORDINATE { public String getReadableType() { return "Coordinate"; } }, DIRECTION { public String getReadableType() { return "Direction"; } }, /** * A list of variables, each one surrounded by quotation marks, and * separated by blank spaces. For instance: *

    * "var1" "var2" "var3" */ LIST_OF_VARIABLES { public String getReadableType() { return "List of variables"; } }, OBJECT { public String getReadableType() { return "Object"; } }, VARIABLE_INT { public String getReadableType() { return "Int"; } }, VARIABLE_FLOAT { public String getReadableType() { return "Real"; } }, VARIABLE_STRING { public String getReadableType() { return "String"; } }; /** * Returns the set of possible values for an particular ParameterType. * In case the ParameterType has an infinite number of possible values, * it returns null. *

    * This method is used, for instance, for {@link #PARALLEL_POLICY}, * which has only two possible values, "SEQUENCE" and "PARALLEL". *

    * The default implementation of this method returns null. */ public String[] getPossibleValues() { return null; }; /** * Returns a user friendly version of the type. */ public abstract String getReadableType(); /** * Checks if a type is a variable */ public static boolean isVariable(ParameterType type) { if (type == VARIABLE_INT || type == VARIABLE_FLOAT || type == VARIABLE_STRING) return true; return false; } } /** * The specification of a ConceptualBTNode parameter. A Parameter is * basically a name and a type, meaning that a parameter is identified by a * name and it has a value of a particular type. * * @author Ricardo Juan Palma Durán * */ public static class Parameter implements Serializable { private static final long serialVersionUID = 1L; /** Name of the parameter. */ private String name; /** Type of the parameter. */ private ParameterType type; /** * Tells if the parameter can or cannot be read from the context. This * is mainly used by the BTEditor: some parameters can be read from the * context , but others cannot, and must have a specific value. */ private boolean contextable = false; /** * This is used for checking certain special parameters. When a * parameter is a reference to a node, that node may have to be of a * particular kind, in which case this field stores that kind. */ private List nodeClasses = new LinkedList(); /** * When a parameter is a reference to a node ( * {@link ParameterType#NODE_ID}), that node may have to be of a * particular kind, in which case this method returns the list of * allowed types. If an empty list is returned, it means all kinds * are allowed. */ public List getNodeClasses() { return nodeClasses; } /** * When a parameter is a reference to a node ( * {@link ParameterType#NODE_ID}), that node may have to be of a * particular kind, in which case this method adds one allowed * type. */ public void addNodeClass(String nodeClass) { this.nodeClasses.add(nodeClass); } /** * Returns the name of the node parameter. */ public String getName() { return name; } /** * Sets the name of the parameter. */ public void setName(String name) { this.name = name; } /** * Returns the type of the parameter. */ public ParameterType getType() { return type; } /** * Sets the type of the parameter. */ public void setType(ParameterType type) { this.type = type; } /** * Sets of the parameter can or cannot be read from the context. */ public void setContextable(boolean contextable) { this.contextable = contextable; } /** * Returns true if the parameter can be read from the context, of false * if it cannot. */ public boolean getContextable() { return this.contextable; } /** * This function returns a deep copy of this Parameter. * * @see java.lang.Object#clone() */ public Parameter clone() { Parameter copy = new Parameter(); copy.contextable = this.contextable; copy.name = this.name == null ? null : new String(this.name); copy.type = this.type; copy.nodeClasses = new LinkedList(); for (String nodeClass : this.nodeClasses) { copy.nodeClasses.add(new String(nodeClass)); } return copy; } } /** * Constructs an empty ConceptualBTNode. */ public ConceptualBTNode() { this.parameters = new Vector(); } /** * Returns the type of the node, or null if not set. This is the type that * is written into the XML file when exporting a behaviour tree. */ public String getType() { return type; } /** * Sets the type of the conceptual node. */ public void setType(String type) { this.type = type; } /** * Returns the list of parameters of the conceptual node, or an empty list * if it has no parameters. */ public List getParameters() { return parameters; } /** * Seths the list of parameters of the conceptual node. */ public void setParameters(List parameters) { this.parameters = parameters; } /** * Returns the number of children that this node can have. A value of -1 * means that the node can have an undetermined amount of children (but at * least one). A value other than -1 means that the node must have exactly * that number of children. */ public int getNumChildren() { return numChildren; } /** * Sets the number of children that this node can have. A value of -1 means * that the node can have an undetermined amount of children (but at least * one). A value other than -1 means that the node must have exactly that * number of children. */ public void setNumChildren(int numChildren) { this.numChildren = numChildren; } /** * Returns the path of the icon associated to this conceptual node, or null * if not set. */ public String getIcon() { return icon; } /** * Sets the path of the icon associated to this conceptual node. */ public void setIcon(String icon) { this.icon = icon; } /** * Adds a parameter to the list of parameters of this node. */ public void addParameter(Parameter parameter) { this.parameters.add(parameter); } /** * Returns a readable version of the type of the conceptual node, or null if * not set. */ public String getReadableType() { return readableType; } /** * Sets the readable version of the type of the node. */ public void setReadableType(String type) { this.readableType = type; } /** * Returns if this node has a name. If false, {@link #getName()} is null. * Otherwise, {@link #getName()} returns the name (or null in case it has * not been set yet). */ public boolean getHasName() { return hasName; } /** * Sets if the node has a name. If false, {@link #getName()} is null. * Otherwise, {@link #getName()} returns the name (or null in case it has * not been set yet). */ public void setHasName(boolean hasName) { this.hasName = hasName; } /** * If {@link #getHasName()} is true, returns the name of the node or null in * case it has not been set. If false, it returns null. */ public String getName() { return name; } /** * Sets the name of the node. */ public void setName(String name) { this.name = name; } /** * This method returns a deep copy of this ConceptualBTNode. * * @see java.lang.Object#clone() */ public ConceptualBTNode clone() { ConceptualBTNode copy = new ConceptualBTNode(); copy.hasName = this.hasName; copy.icon = this.icon == null ? null : new String(this.icon); copy.name = this.name == null ? null : new String(this.name); copy.numChildren = this.numChildren; copy.parameters = new LinkedList(); for (Parameter p : this.parameters) { copy.parameters.add(p.clone()); } copy.readableType = this.readableType == null ? null : new String(this.readableType); copy.type = this.type == null ? null : new String(this.type); return copy; } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/model/ConceptualNodesTree.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.model; import java.util.List; import java.util.Vector; /** * A tree-like structure used to organize {@link ConceptualBTNode}s in * categories. *

    * This tree is composed of two types of nodes: *

      *
    • {@link CategoryItem}: represents categories. It can contain both * CategoryItem (in order to create a hierarchy of categories) and NodeItem * objects. *
    • {@link ConceptualBTNodeItem}: contains a ConceptualBTNode. *
    * * This class defines a method, {@link #insertNode(String, ConceptualBTNodeItem)}, that is * used to insert nodes (CategoryItem and NodeItem) into the tree. *

    * This tree may have several roots, since there may be many nodes at the top * level (that is, within no category). All these nodes are roots of the tree. * * @author Ricardo Juan Palma Durán * */ public class ConceptualNodesTree { /** Categories separator. */ public static final String CATEGORY_SEPARATOR = new String("/"); /** Roots of the tree. */ protected List roots; /** * Constructs an empty ConceptualNodesTree. */ public ConceptualNodesTree() { this.roots = new Vector(); } /** * Returns the roots of the tree. */ public List getRoots() { return roots; } /** * Sets the roots of the tree. */ public void setRoots(List roots) { this.roots = roots; } /** * Inserts a NodesTreeItem into the tree, at a particular position. The position * is specified by the category parameter. This parameter is a * String representing the category where the NodeItem must be inserted. *

    * category can be null, in which case the node is inserted as * a root of the tree. Otherwise, the category must follow this format: *

    * subCat1 + {@link #CATEGORY_SEPARATOR} + subCat2 + {@link #CATEGORY_SEPARATOR} + ... + * {@link #CATEGORY_SEPARATOR} + subCatN *

    * If the specified category does not exist in the tree, it is created on * the fly. */ public void insertNode(String category, NodesTreeItem node) { /* If the category is null, the node is inserted at the top level. */ if (category == null) { node.setParent(null); this.roots.add(node); return; } /* * Otherwise, follow the standard algorithm. First we extract each of * the categories that the path where this node is going to be inserted * is composed of. */ String[] subCategories = category.split(CATEGORY_SEPARATOR); /* * Main loop. Here we find the CategoryItem where the NodeItem must be * inserted. "currentItem" represents the CategoryItem we are looking * for. */ CategoryItem currentItem = null; for (int i = 0; i < subCategories.length; i++) { /* * At every level of the tree, we get the children of the current * category, and check if there is one whose name matches * "subCategories[i]". If there is such a category, we move on to * it. Otherwise, we create a new category whose name is * "subCategories[i]". */ List children = i == 0 ? this.roots : currentItem .getChildren(); /* Check if there is a category matching the current one. */ boolean subCategoryExists = false; for (int j = 0; j < children.size(); j++) { if (children.get(j) instanceof CategoryItem && children.get(j).getName().equals(subCategories[i])) { /* * If the category does exist, we move on to the next level * of the tree. */ currentItem = (CategoryItem) children.get(j); subCategoryExists = true; break; } } /* * If the category does not exist, it must be created. Then, move on * to the next level of the tree. */ if (!subCategoryExists) { CategoryItem newCategory = new CategoryItem(); newCategory.setName(subCategories[i]); if (i == 0) { newCategory.setParent(null); this.roots.add(newCategory); } else { newCategory.setParent(currentItem); currentItem.addChild(newCategory); } currentItem = newCategory; } } /* * When we have reached the category where the node must be inserted, it * is inserted into it. */ currentItem.addChild(node); node.setParent(currentItem); } /** * A node of the {@link ConceptualNodesTree}. * * @author Ricardo Juan Palma Durán * */ public static abstract class NodesTreeItem { protected NodesTreeItem parent; public NodesTreeItem getParent() { return parent; } public void setParent(NodesTreeItem parent) { this.parent = parent; } public abstract String getName(); } /** * A category of the {@link ConceptualNodesTree}. * * @author Ricardo Juan Palma Durán * */ public static class CategoryItem extends NodesTreeItem { private String name; private List children; public CategoryItem() { this.children = new Vector(); } public void addChild(NodesTreeItem child) { this.children.add(child); } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getChildren() { return this.children; } public int getNumChildren() { return this.children.size(); } } /** * A {@link ConceptualBTNode} of the {@link ConceptualNodesTree}. * * @author Ricardo Juan Palma Durán * */ public static class ConceptualBTNodeItem extends NodesTreeItem { private ConceptualBTNode nodeModel; public ConceptualBTNodeItem(ConceptualBTNode nodeModel) { this.nodeModel = nodeModel; } public String getName() { return this.nodeModel.getReadableType(); } public ConceptualBTNode getNodeModel() { return this.nodeModel; } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/DetailsDialog.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IconAndMessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; /** * This class represents a dialog that shows an icon, a message, and a text area * where plain text can be shown. *

    * This class represents the classic "details" dialog that shows a "details" * button to see a detailed report of the message. *

    * This dialog has two buttons, a "close" button and a "details" button. By * pressing the details button, the details area can be hidden or made visible. * The icon that is showed by the dialog can also be set. * * @author Ricardo Juan Palma Durán * */ public class DetailsDialog extends IconAndMessageDialog { /** * Type of icon to use. Must be {@link #ERROR}, {@link #WARNING}, * {@link #INFORMATION} or {@link #QUESTION}. */ protected int iconType; /** * Text shown in the details area. */ protected String details; /** * Text area where the details are shown. */ protected Text detailsText; /** * Boolean that tells if the details text ({@link #detailsText}) is * currently created or not. The visualization and hiding of the details * area is done by creating and destroying {@link #detailsText}. This flag * tells if {@link #detailsText} is actyallu created or not. */ protected boolean detailsTextCreated = false; /** * Constant for an error icon. */ public static final int ERROR = 0; /** * Constant for a warning icon. */ public static final int WARNING = 1; /** * Constant for an information icon. */ public static final int INFORMATION = 2; /** * Constant for a question icon. */ public static final int QUESTION = 3; /** * Button used to hide and visualize the details area. */ protected Button detailsButton; /** * Title of the dialog. */ protected String title; /** * Constructs a DetailsDialog. The style of the dialog (the shell) is * SWT.CLOSE | SWT.RESIZE | SWT.MAX | SWT.MODELESS. . * * @param title * title of the dialog. * @param message * main message shown by the dialog. * @param details * text that is shown in the details area. * @param iconType * type of the icon that is shown in the dialog. Must be one of * the following: {@link #ERROR}, * {@link #WARNING}, * {@link #INFORMATION} , * {@link #QUESTION}. * @param parentShell * this dialog's parent shell. If null, it is a top level shell. */ public DetailsDialog(String title, String message, String details, int iconType, Shell parentShell) { this(title, message, details, iconType, parentShell, SWT.CLOSE | SWT.RESIZE | SWT.MAX | SWT.MODELESS); } /** * Constructs a DetailsDialog. The dialog style can be specified. * * @param title * title of the dialog. * @param message * main message shown by the dialog. * @param details * text that is shown in the details area. * @param iconType * type of the icon that is shown in the dialog. Must be one of * the following: {@link #ERROR}, * {@link #WARNING}, * {@link #INFORMATION} , * {@link #QUESTION}. * @param parentShell * this dialog's parent shell. If null, it is a top level shell. * @param shellStyle * style of the dialog's shell. */ public DetailsDialog(String title, String message, String details, int iconType, Shell parentShell, int shellStyle) { super(parentShell); if (message == null) { throw new IllegalArgumentException("Message cannot be null"); } this.message = message; if (iconType < 0 || iconType > 3) { throw new IllegalArgumentException("Invalid icon type (" + iconType + ")"); } if (details == null) { throw new IllegalArgumentException("Details cannot be null"); } if (title == null) { throw new IllegalArgumentException("Title cannot be null"); } this.details = details; this.iconType = iconType; this.title = title; this.setBlockOnOpen(false); this.setShellStyle(shellStyle); } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets * .Composite) */ protected Control createDialogArea(Composite parent) { return createMessageArea(parent); } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse * .swt.widgets.Composite) */ protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, true); this.detailsButton = createButton(parent, IDialogConstants.DETAILS_ID, IDialogConstants.SHOW_DETAILS_LABEL, false); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) */ protected void buttonPressed(int buttonId) { if (buttonId == IDialogConstants.DETAILS_ID) { /* * If the details button has been pressed, then the details area * must be shown or hidden, depending on whether it as hidden or * not. After doing so, the dialog must be resized. */ Point dialogOldDimensions = getShell().getSize(); if (this.detailsTextCreated) { /* * If the details area is being showed, we delete it. Also, the * label on the details button must be changed. */ this.detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL); this.detailsText.dispose(); } else { /* * If the text area is not being showed, it must be created and * showed. In order to do so, we initialize "this.detailsText". * Also, the label on the details button must be changed. */ this.detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL); this.detailsText = new Text((Composite) getContents(), SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.WRAP | SWT.H_SCROLL | SWT.V_SCROLL); this.detailsText.setText(this.details); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); this.detailsText .setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); data.horizontalSpan = 2; this.detailsText.setLayoutData(data); } getContents().getShell().layout(); this.detailsTextCreated = !this.detailsTextCreated; /* * The dialog is finalli resized. */ Point dialogNewDimensions = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT); int screenHeight = Display.getCurrent().getClientArea().height; getShell() .setSize( new Point(dialogOldDimensions.x, Math.min(dialogNewDimensions.y, screenHeight))); } else { /* * Close the dialog... */ close(); } setReturnCode(buttonId); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage() */ protected Image getImage() { switch (this.iconType) { case ERROR: return getErrorImage(); case WARNING: return getWarningImage(); case INFORMATION: return getInfoImage(); case QUESTION: return getQuestionImage(); default: throw new RuntimeException("Invalid type of image: " + this.iconType); } } /* * (non-Javadoc) * * @see * org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets * .Shell) */ protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText(this.title); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/Extensions.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; /** * Class that stores the file extensions that are supported by the application. * * @author Ricardo Juan Palma Durán * * Modified by Fernando Matarrubia (adding c++ extension) */ public class Extensions { /** Extensions of the files that can contain behaviour trees. */ private static final String[] BTFileExtensions = new String[] { "xbt", "xml" }; /** Extension of the file that can contain an inline c++ version of the behaviour tree */ private static final String[] BTCppFileExtensions = new String[] {"cpp"}; /** Extension of the file that can contain an inline c++ version of the behaviour tree */ private static final String[] BTInlFileExtensions = new String[] {"inl"}; /** Extensions of MMPM domain files. */ private static final String[] MMPMDomainfileExtensions = new String[] { "xml" }; /** * Returns an array containing the extensions of the files than can contain * behaviour trees. */ public static String[] getBTFileExtensions() { return BTFileExtensions; } /** * Returns an array containing the extensions of the special cpp files that * can contain behaviour trees */ public static String[] getCppFileExtensions(){ return BTCppFileExtensions; } /** * Returns an array containing the extensions of the special inline files that * can contain behaviour trees */ public static String[] getInlFileExtensions(){ return BTInlFileExtensions; } /** * Returns an array containing the extensions of MMPM domain files. */ public static String[] getMMPMDomainFileExtensions() { return MMPMDomainfileExtensions; } /** * Given an array with file extensions, this method returns filters for * those extensions to be used in SWT dialogs */ public static String[] getFiltersFromExtensions(String[] extensions) { String[] result = new String[extensions.length]; for (int i = 0; i < result.length; i++) { result[i] = "*." + extensions[i]; } return result; } /** * Given a set of file extensions, this method returns a single filter for * all of those extensions. */ public static String getUnifiedFilterFromExtensions(String[] extensions) { String result = new String(); for (int i = 0; i < extensions.length - 1; i++) { result += "*." + extensions[i] + ";"; } result += "*." + extensions[extensions.length - 1]; return result; } /** * Joins two arrays of String. */ public static String[] joinArrays(String[] array1, String[] array2) { String[] result = new String[array1.length + array2.length]; System.arraycopy(array1, 0, result, 0, array1.length); System.arraycopy(array2, 0, result, array1.length, array2.length); return result; } /** * Joins a file name and an extension. If the file name ends with "."+ * extension , then fileName itself is returned. * Otherwise, it returns fileName+"."+extension. */ public static String joinFileNameAndExtension(String fileName, String extension) { if (fileName.endsWith("." + extension)) { return fileName; } else { return fileName + "." + extension; } } private Extensions() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/IconsPaths.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; /** * Paths of application general icons (these do not change). * * @author Ricardo Juan Palma Durán * */ public class IconsPaths { public static final String OPEN_BT = "/icons/openBT.png"; public static final String NEW_BT = "/icons/newBT.png"; public static final String ROOT = "/icons/root.png"; public static final String ACTION = "/icons/action.png"; public static final String CATEGORY = "/icons/category.png"; public static final String CONDITION = "/icons/condition.png"; public static final String LOAD_MMPM_DOMAIN = "/icons/loadMMPMDomain.png"; public static final String GUARD = "/icons/guard.png"; public static final String BT = "/icons/BT.png"; public static final String CPP = "/icons/cpp.png"; public static final String INL = "/icons/inl.png"; private IconsPaths() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/OverlayImageIcon.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; import org.eclipse.jface.resource.CompositeImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; /** * This class is used for overlaying image icons. * * @author balajik * */ public class OverlayImageIcon extends CompositeImageDescriptor { /** * Base image of the object */ private Image baseImage; /** * Size of the base image */ private Point sizeOfImage; /** * Decoration */ private Image decoration; /** * Constructor for overlayImageIcon. */ public OverlayImageIcon(Image baseImage, Image decoration) { // Base image of the object this.baseImage = baseImage; // Demo Image Object this.decoration = decoration; this.sizeOfImage = new Point(baseImage.getBounds().width, baseImage.getBounds().height); } /** * @see org.eclipse.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, * int) DrawCompositeImage is called to draw the composite image. * */ protected void drawCompositeImage(int arg0, int arg1) { // Draw the base image drawImage(baseImage.getImageData(), 0, 0); ImageData imageData = decoration.getImageData(); // Draw on bottom right corner drawImage(imageData, sizeOfImage.x - imageData.width, sizeOfImage.y - imageData.height); } /** * @see org.eclipse.jface.resource.CompositeImageDescriptor#getSize() get * the size of the object */ protected Point getSize() { return sizeOfImage; } /** * Get the image formed by overlaying different images on the base image * * @return composite image */ public Image getImage() { return createImage(); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/Pair.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; import java.io.Serializable; /** * Pair represents a pair of objects * * @author Wikipedia * @param * type of the first element of the pair. * @param * type of the second element of the pair. */ public class Pair implements Serializable { private static final long serialVersionUID = 1L; /* * First element of the pair. */ private T first; /* * Second element of the pair. */ private S second; /** * Constructs a Pair. * * @param f * first element of the pair. * @param s * second element of the pair. */ public Pair(T f, S s) { first = f; second = s; } /** * Returns the first element of the pair. * * @return the first element of the pair. */ public T getFirst() { return first; } /** * Returns the second element of the pair. * * @return the second element of the pair. */ public S getSecond() { return second; } /** * Sets the value of the first element of the pair. * * @param f * value for the first element of the pair. */ public void setFirst(T f) { first = f; } /** * Sets the value of the second element of the pair. * * @param s * value for the second element of the pair. */ public void setSecond(S s) { second = s; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ public String toString() { return "(" + first.toString() + ", " + second.toString() + ")"; } public boolean equals(Object o){ if(this==o) return true; if(o instanceof Pair){ return first.equals(((Pair)o).first) && second.equals(((Pair)o).second); } else{ return false; } } public int hashCode(){ return first.hashCode()+second.hashCode(); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/StandardDialogs.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; import java.util.List; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; /** * Class that contains a bunch of standard SWT dialogs to be used by calling * simple methods. *

    * All these dialogs block when opened. * * @author Ricardo Juan Palma Durán * */ public class StandardDialogs { /* * Private constructor. */ private StandardDialogs() {} public static void informationDialog(String title, String informationMessage) { MessageDialog.openInformation(null, title, informationMessage); } public static void errorDialog(String title, String errorMessage) { MessageDialog.openError(null, title, errorMessage); } public static void exceptionDialog(String title, String errorMessage, Exception e) { DetailsDialog dialog = new DetailsDialog(title, errorMessage, Utilities.stackTraceToString(e), DetailsDialog.ERROR, null, SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.MIN | SWT.MAX | SWT.CLOSE); dialog.setBlockOnOpen(true); dialog.open(); } public static void exceptionDialog(String title, String errorMessage, List exceptions) { String exceptionMessage = new String(); for (Exception currentException : exceptions) { exceptionMessage += "** Exception **\n\n" + Utilities.stackTraceToString(currentException) + "\n\n"; } DetailsDialog dialog=new DetailsDialog(title, errorMessage, exceptionMessage, DetailsDialog.ERROR, null, SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.MIN | SWT.MAX | SWT.CLOSE); dialog.setBlockOnOpen(true); dialog.open(); } public static void warningDialog(String title, String warningMessage) { MessageDialog.openWarning(null, title, warningMessage); } public static boolean confirmationDialog(String title, String confirmationMessage) { return MessageDialog.openConfirm(null, title, confirmationMessage); } public static boolean questionDialog(String title, String question) { return MessageDialog.openQuestion(null, title, question); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/util/Utilities.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.util; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.LinkedList; import java.util.List; import java.util.Vector; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.editor.BTEditorIDGenerator; import jbt.tools.bteditor.editor.BTEditorInput; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.EditorPart; /** * General utilities used in the tool. * * @author Ricardo Juan Palma Durán * */ public class Utilities { /** * Returns a IViewPart by its class. If cannot be found, returns null. */ public static IViewPart getView(Class c) { IWorkbenchPage page = getMainWindowActivePage(); if (page != null) { IViewReference[] views = page.getViewReferences(); for (int i = 0; i < views.length; i++) { if (views[i].getView(true) != null) { if (c.isInstance(views[i].getView(false))) { return views[i].getView(false); } } } } return null; } /** * Returns a List containing all the BTEditor that are currently open. */ public static List getBTEditors() { IWorkbenchPage activePage = getMainWindowActivePage(); if (activePage != null) { IEditorReference[] editors = activePage.getEditorReferences(); if (editors.length == 0) return new Vector(); List returnedEditors = new Vector(); for (int i = 0; i < editors.length; i++) { if (editors[i].getEditor(false) instanceof BTEditor) { returnedEditors.add((BTEditor) editors[i].getEditor(false)); } } return returnedEditors; } return new LinkedList(); } /** * Given a BTEditor ID (see {@link BTEditorIDGenerator}), this method * returns the corresponding BTEditor, or null if not found. */ public static BTEditor getBTEditor(long btEditorID) { List btEditors = getBTEditors(); for (BTEditor editor : btEditors) { if (((BTEditorInput) editor.getEditorInput()).getEditorID() == btEditorID) { return editor; } } return null; } /** * Returns the currently active BTEditor, or null if no BTEditor is active. */ public static BTEditor getActiveBTEditor() { IWorkbenchPage page = getMainWindowActivePage(); if (page != null) { IEditorPart editor = page.getActiveEditor(); if (editor instanceof BTEditor) return (BTEditor) editor; else return null; } return null; } /** * Returns the main application window. */ public static IWorkbenchWindow getMainWindow() { return PlatformUI.getWorkbench().getWorkbenchWindows()[0]; } /** * Returns the active page of the window returned by * {@link #getMainWindow()}, or null if not found. */ public static IWorkbenchPage getMainWindowActivePage() { return getMainWindow().getActivePage(); } /** * Returns the application's display. */ public static Display getDisplay() { return PlatformUI.getWorkbench().getDisplay(); } /** * Returns the application's shell. */ public static Shell getShell() { return PlatformUI.getWorkbench().getDisplay().getActiveShell(); } /** * Returns the stack trace of a Throwable as a String. This can be used for * exceptions, for example. This method may return an empty ("") String in * case an error occurs or in case t does not have a strack * trace. */ public static String stackTraceToString(Throwable t) { String retValue = ""; StringWriter sw = null; PrintWriter pw = null; try { sw = new StringWriter(); pw = new PrintWriter(sw); t.printStackTrace(pw); retValue = sw.toString(); } finally { try { if (pw != null) pw.close(); if (sw != null) sw.close(); } catch (IOException e) { } } return retValue; } /** * Activates an editor. * * @param editor * the editor to activate. */ public static void activateEditor(EditorPart editor) { IWorkbenchPage page = editor.getSite().getPage(); page.activate(editor); } private Utilities() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/viewers/BTNodeIndentifierTransfer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.viewers; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.model.BTNode.Identifier; import org.eclipse.swt.dnd.ByteArrayTransfer; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TransferData; /** * This transfer class transfers BTNode objects. However, only the BTNode identifier is * transfered. * * @author Ricardo Juan Palma Durán * */ public class BTNodeIndentifierTransfer extends ByteArrayTransfer { private static final String TYPENAME = "BTNodeIndentifierTransfer"; private static final int TYPEID = registerType(TYPENAME); private static BTNodeIndentifierTransfer instance = null; private BTNodeIndentifierTransfer(){} public static BTNodeIndentifierTransfer getInstance(){ if(instance == null){ instance = new BTNodeIndentifierTransfer(); } return instance; } /* * (non-Javadoc) * @see org.eclipse.swt.dnd.Transfer#getTypeNames() */ protected String[] getTypeNames(){ return new String[]{TYPENAME}; } /* * (non-Javadoc) * @see org.eclipse.swt.dnd.Transfer#getTypeIds() */ protected int[] getTypeIds(){ return new int[]{TYPEID}; } /* * (non-Javadoc) * @see org.eclipse.swt.dnd.Transfer#validate(java.lang.Object) */ protected boolean validate(Object object){ return(object != null && object instanceof BTNode); } /* * (non-Javadoc) * @see org.eclipse.swt.dnd.ByteArrayTransfer#javaToNative(java.lang.Object, * org.eclipse.swt.dnd.TransferData) */ public void javaToNative(Object object, TransferData transferData){ if(!validate(object)){ DND.error(DND.ERROR_INVALID_DATA); } if( !isSupportedType(transferData)){ DND.error(DND.ERROR_INVALID_DATA); } BTNode node=(BTNode)object; try{ ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream writeOut = new ObjectOutputStream(out); writeOut.writeObject(node.getID()); byte[] buffer = out.toByteArray(); writeOut.close(); out.close(); super.javaToNative(buffer, transferData); } catch(IOException e){ e.printStackTrace(); } } /* * (non-Javadoc) * @see * org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(org.eclipse.swt.dnd * .TransferData) */ public Object nativeToJava(TransferData transferData){ if(!isSupportedType(transferData)){ return null; } byte[] buffer = (byte[])super.nativeToJava(transferData); if(buffer == null) return null; try{ ByteArrayInputStream in = new ByteArrayInputStream(buffer); ObjectInputStream readIn = new ObjectInputStream(in); Identifier id=(Identifier)readIn.readObject(); readIn.close(); in.close(); return id; } catch(Exception ex){ ex.printStackTrace(); return null; } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/viewers/ConceptualBTNodeTransfer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.viewers; import jbt.tools.bteditor.util.Pair; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import jbt.tools.bteditor.model.ConceptualBTNode; import org.eclipse.swt.dnd.ByteArrayTransfer; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TransferData; /** * This transfer class transfers {@link ConceptualBTNode} objects. However, only * the pair "type"-"name" is transfered, and recovered as a Pair * whose first element is "type" and whose second element is "name". * * @author Ricardo Juan Palma Durán * */ public class ConceptualBTNodeTransfer extends ByteArrayTransfer { private static final String TYPENAME = "ConceptualBTNodeTransfer"; private static final int TYPEID = registerType(TYPENAME); private static ConceptualBTNodeTransfer instance = null; private ConceptualBTNodeTransfer() { } public static ConceptualBTNodeTransfer getInstance() { if (instance == null) { instance = new ConceptualBTNodeTransfer(); } return instance; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#getTypeNames() */ protected String[] getTypeNames() { return new String[] { TYPENAME }; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#getTypeIds() */ protected int[] getTypeIds() { return new int[] { TYPEID }; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#validate(java.lang.Object) */ protected boolean validate(Object object) { return (object != null && object instanceof ConceptualBTNode); } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.ByteArrayTransfer#javaToNative(java.lang.Object, * org.eclipse.swt.dnd.TransferData) */ public void javaToNative(Object object, TransferData transferData) { if (!validate(object)) { DND.error(DND.ERROR_INVALID_DATA); } if (!isSupportedType(transferData)) { DND.error(DND.ERROR_INVALID_DATA); } ConceptualBTNode node = (ConceptualBTNode) object; try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream writeOut = new ObjectOutputStream(out); writeOut.writeObject(node.getType()); writeOut.writeObject(node.getName()); byte[] buffer = out.toByteArray(); writeOut.close(); out.close(); super.javaToNative(buffer, transferData); } catch (IOException e) { e.printStackTrace(); } } /* * (non-Javadoc) * * @see * org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(org.eclipse.swt.dnd * .TransferData) */ public Object nativeToJava(TransferData transferData) { if (!isSupportedType(transferData)) { return null; } byte[] buffer = (byte[]) super.nativeToJava(transferData); if (buffer == null) return null; try { String type; String name; ByteArrayInputStream in = new ByteArrayInputStream(buffer); ObjectInputStream readIn = new ObjectInputStream(in); type = (String) readIn.readObject(); name = (String) readIn.readObject(); readIn.close(); in.close(); return new Pair(type, name); } catch (Exception ex) { ex.printStackTrace(); return null; } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/viewers/ConceptualNodesTreeViewer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.viewers; import java.util.List; import java.util.Vector; import jbt.tools.bteditor.ApplicationIcons; import jbt.tools.bteditor.model.ConceptualNodesTree; import jbt.tools.bteditor.model.ConceptualNodesTree.CategoryItem; import jbt.tools.bteditor.model.ConceptualNodesTree.ConceptualBTNodeItem; import jbt.tools.bteditor.model.ConceptualNodesTree.NodesTreeItem; import jbt.tools.bteditor.util.IconsPaths; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Tree; /** * ConceptualNodesTreeViewer is a Composite that is able to display, in a * tree-like fashion, several {@link ConceptualNodesTree}s. *

    * It also defines a drag and drop mechanism for dragging nodes from the view so * that they can be dropped anywhere else in the application. This drag and drop * mechanism is compatible with the transfer type * {@link ConceptualBTNodeTransfer}. *

    * Double clicking a category node expands -or collapses- that category. * * @author Ricardo Juan Palma Durán * */ public class ConceptualNodesTreeViewer extends Composite { /** The TreeViewer that is internally used for displaying the trees. */ private TreeViewer treeViewer; /** The trees thar are being displayed. */ private List trees; /** * Constructor. */ public ConceptualNodesTreeViewer(Composite parent, int style) { super(parent, style); this.trees = new Vector(); this.setLayout(new FillLayout()); this.treeViewer = new TreeViewer(this, SWT.SINGLE); Tree treeWidget = (Tree) this.treeViewer.getControl(); // treeWidget.setLinesVisible(true); this.treeViewer.setContentProvider(new BTContentProvider()); this.treeViewer.setLabelProvider(new BTLabelProvider()); this.treeViewer.setInput(this.trees); /* Drag and drop support. */ Transfer[] transfers = new Transfer[] { ConceptualBTNodeTransfer.getInstance() }; this.treeViewer.addDragSupport(DND.DROP_MOVE, transfers, new NodesTreeViewerDragListener()); /* Double click listener for expanding and collapsing categories. */ this.treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { Object node = ((StructuredSelection) event.getSelection()).getFirstElement(); if (node instanceof CategoryItem) { if (treeViewer.getExpandedState(node)) { treeViewer.collapseToLevel(node, 1); } else { treeViewer.expandToLevel(node, 1); } } } }); } /** * Adds a new {@link ConceptualNodesTree} that will be displayed together * with all the trees that were being displayed. */ public void addTree(ConceptualNodesTree tree) { this.trees.add(tree); this.treeViewer.refresh(); } /** * Drag source listener of the tree. It is compatible with * {@link ConceptualBTNodeTransfer}. * * @author Ricardo Juan Palma Durán * */ private class NodesTreeViewerDragListener implements DragSourceListener { public void dragFinished(DragSourceEvent event) { } public void dragSetData(DragSourceEvent event) { if (ConceptualBTNodeTransfer.getInstance().isSupportedType(event.dataType)) { event.data = ((ConceptualBTNodeItem) ((IStructuredSelection) treeViewer .getSelection()).getFirstElement()).getNodeModel(); } } public void dragStart(DragSourceEvent event) { IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); if (!selection.isEmpty()) { if (selection.getFirstElement() instanceof ConceptualBTNodeItem) { ConceptualBTNodeItem selectedNode = (ConceptualBTNodeItem) selection .getFirstElement(); event.doit = true; return; } } event.doit = false; } } /** * Contente provider of the tree. * * @author Ricardo Juan Palma Durán * */ private static class BTContentProvider implements ITreeContentProvider { public Object[] getChildren(Object parentElement) { if (parentElement instanceof CategoryItem) { CategoryItem category = (CategoryItem) parentElement; return category.getChildren().toArray(); } else { return new Object[] {}; } } public Object getParent(Object element) { return ((NodesTreeItem) element).getParent(); } public boolean hasChildren(Object element) { if (element instanceof CategoryItem) { CategoryItem category = (CategoryItem) element; return category.getNumChildren() > 0; } else { return false; } } public Object[] getElements(Object inputElement) { List trees = (List) inputElement; Object[] elements = new Object[trees.size()]; for (int i = 0; i < trees.size(); i++) { ConceptualNodesTree tree = trees.get(i); elements[i] = tree.getRoots().get(0); } return elements; } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } /** * Label provider of the tree. * * @author Ricardo Juan Palma Durán * */ private static class BTLabelProvider implements ILabelProvider { public Image getImage(Object element) { if (element instanceof CategoryItem) { return ApplicationIcons.getIcon(IconsPaths.CATEGORY); } else { ConceptualBTNodeItem nodeItem = (ConceptualBTNodeItem) element; return ApplicationIcons.getIcon(nodeItem.getNodeModel().getIcon()); } } public String getText(Object element) { return ((NodesTreeItem) element).getName(); } public void addListener(ILabelProviderListener listener) { } public void dispose() { } public boolean isLabelProperty(Object element, String property) { return false; } public void removeListener(ILabelProviderListener listener) { } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/viewers/NodeInfoViewer.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.viewers; import java.util.List; import java.util.Observable; import java.util.Observer; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.model.ConceptualBTNode.NodeInternalType; import jbt.tools.bteditor.model.BTNode.Parameter; import jbt.tools.bteditor.util.Utilities; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.TableWrapData; import org.eclipse.ui.forms.widgets.TableWrapLayout; /** * This class is used to display some information about a {@link BTNode}. This * Composite implements the {@link Observer} interface so that it gets notified * whenever the BTNode changes. By doing so, it can properly keep the displayed * information updated. * * @author Ricardo Juan Palma Durán * */ public class NodeInfoViewer extends Composite implements Observer { /** * The BTNode whose information is being displayed. */ private BTNode node; /** * Toolkit used to manage the form that displays the information ( * {@link #global}). */ private FormToolkit toolkit; /** * Form that displays all the node's information. */ private ScrolledForm global; /** * Constructor. */ public NodeInfoViewer(Composite parent, int style) { super(parent, style); this.setLayout(new FillLayout()); this.toolkit = new FormToolkit(Utilities.getDisplay()); this.global = this.toolkit.createScrolledForm(this); TableWrapLayout layout = new TableWrapLayout(); layout.numColumns = 2; this.global.getBody().setLayout(layout); /* For disposing the toolkit. */ this.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { toolkit.dispose(); } }); /* For the wheel event. */ this.global.addListener(SWT.Activate, new Listener() { public void handleEvent(Event event) { global.setFocus(); } }); } /** * Sets the node whose information will be displayed. If node * is null, no information is displayed. This method registers the * NodeInfoViewer as an observer of node, so whenever * node changes, the NodeInfoViewer will be notified and will * update the displayed information accordingly. */ public void setNode(BTNode node) { if (this.node != null) { this.node.deleteObserver(this); } this.node = node; if (node != null) this.node.addObserver(this); updateView(); } /** * * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void update(Observable o, Object arg) { updateView(); } /** * Updates all the information that is displayed on the Composite, by * obtaining it from the BTNode. */ private void updateView() { /* Clean previos information. */ if (this.global.getBody().getChildren().length != 0) { for (Control c : this.global.getBody().getChildren()) { c.dispose(); } } /* If child is null, do nothing. */ if (this.node == null) { return; } Composite parent = this.global.getBody(); this.toolkit.createLabel(parent, "Type").setLayoutData( new TableWrapData(TableWrapData.LEFT)); Label valueLabel = this.toolkit.createLabel(parent, "", SWT.RIGHT); valueLabel.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); if (!this.node.getConceptualNode().getType().equals(NodeInternalType.ACTION.toString()) && !this.node.getConceptualNode().getType() .equals(NodeInternalType.CONDITION.toString())) { valueLabel.setText(this.node.getConceptualNode().getReadableType()); } else { valueLabel.setText(this.node.getConceptualNode().getType()); } /* Shows node's ID. */ this.toolkit.createLabel(parent, "ID"); this.toolkit.createLabel(parent, this.node.getID().toString(), SWT.RIGHT).setLayoutData( new TableWrapData(TableWrapData.FILL_GRAB)); /* Shows name of the node. */ if (this.node.getConceptualNode().getHasName()) { this.toolkit.createLabel(parent, "Name"); valueLabel = this.toolkit.createLabel(parent, "", SWT.RIGHT); valueLabel.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); /* This is for the root. */ if (this.node.getConceptualNode().getType().equals(NodeInternalType.ROOT.toString())) { if (this.node.getName() != null) { valueLabel.setText(this.node.getName()); } else { valueLabel.setText("Not assigned"); } } else { valueLabel.setText(this.node.getConceptualNode().getReadableType()); } } /* Show parameters. */ List parameters = this.node.getParameters(); for (Parameter p : parameters) { Label nameLabel = this.toolkit.createLabel(parent, ""); valueLabel = this.toolkit.createLabel(parent, p.getValue(), SWT.RIGHT); valueLabel.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); nameLabel.setText(p.getName() + (p.getFromContext() ? " (from context)" : "")); } /* Show error message. */ if (this.node.getErrorMessage() != null) { this.toolkit.createLabel(parent, "ERROR"); this.toolkit.createLabel(parent, this.node.getErrorMessage(), SWT.RIGHT).setLayoutData( new TableWrapData(TableWrapData.FILL_GRAB)); } /* Lay out the Composite so that it refreshes. */ global.reflow(true); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/views/NodeInfo.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.views; import java.util.List; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.util.Utilities; import jbt.tools.bteditor.viewers.NodeInfoViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; /** * ViewPart that shows the information of the selected node of the currently * active editor. Internally, this view just stores a NodeInfoViewer. * * @author Ricardo Juan Palma Durán * */ public class NodeInfo extends ViewPart { public static String ID = "jbt.tools.bteditor.views.NodeInfo"; private NodeInfoViewer nodeInfoViewer; public void createPartControl(Composite parent) { this.nodeInfoViewer = new NodeInfoViewer(parent, SWT.NONE); /* Initialize view's content with the currently selected node. */ BTEditor activeBTEditor = Utilities.getActiveBTEditor(); if (activeBTEditor != null) { List selectedElements = activeBTEditor.getSelectedElements(); if (selectedElements.size() != 0) { this.nodeInfoViewer.setNode(selectedElements.get(0)); } } } public void setFocus() { } /** * Sets the node whose information is being displayed. */ public void setNode(BTNode node) { this.nodeInfoViewer.setNode(node); } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/views/NodesNavigator.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.views; import jbt.tools.bteditor.NodesLoader; import jbt.tools.bteditor.model.ConceptualNodesTree; import jbt.tools.bteditor.viewers.ConceptualNodesTreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; /** * This is the ViewPart that shows the list of available nodes, that is, the * list of nodes that can be used to build behaviour trees. *

    * Internally, this view just contains a {@link ConceptualNodesTreeViewer} that shows the * list of nodes that are currently loaded into the application (see * {@link NodesLoader}. * * @author Ricardo Juan Palma Durán * */ public class NodesNavigator extends ViewPart { public static String ID = "jbt.tools.bteditor.views.NodesNavigator"; private ConceptualNodesTreeViewer viewer; public void createPartControl(Composite parent) { /* Load both standard and non-standard nodes. */ this.viewer = new ConceptualNodesTreeViewer(parent, SWT.NONE); this.viewer.addTree(NodesLoader.getStandardNodesTree()); for (ConceptualNodesTree tree : NodesLoader.getNonStandardNodesTrees()) { this.viewer.addTree(tree); } } /** * Adds a {@link ConceptualNodesTree} to the list of trees displayed by this * view. All the nodes of the tree will be displayed. */ public void addTree(ConceptualNodesTree tree) { viewer.addTree(tree); } /** * * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ public void setFocus() { } } ================================================ FILE: JBTEditor/jbt.tools.bteditor/src/jbt/tools/bteditor/views/NodesSearcher.java ================================================ /* * Copyright (C) 2012 Ricardo Juan Palma Durán * * 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 jbt.tools.bteditor.views; import java.util.List; import java.util.Vector; import jbt.tools.bteditor.editor.BTEditor; import jbt.tools.bteditor.model.BT; import jbt.tools.bteditor.model.BTNode; import jbt.tools.bteditor.model.BTNode.Identifier; import jbt.tools.bteditor.util.Utilities; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.part.ViewPart; /** * The NodesSearcher class is an Eclipse view that lets the user search nodes in * a behaviour tree. This view shows a simple text field where the user can * input the identifier or the partial identifier of a behaviour tree node ( * {@link BTNode}), and then perform the search by clicking a button or pressing * the "enter". The view shows a list containing the nodes of the BT in the * currently active BTEditor that match such an identifier. By clicking a node * of the search result, it gets selected, and the BTEditor that contains it * gets activated. If the text field is left empty, the search list will contain * all the nodes of the tree. * * @author Ricardo Juan Palma Durán * */ public class NodesSearcher extends ViewPart { public static String ID = "jbt.tools.bteditor.views.NodesSearcher"; /** Composite that stores all the widget that this ViewPart displays. */ private Composite global; /** TableViewer that displays the search results. */ private TableViewer resultsTable; /** The result of the search. It stores a set of BTNode identifiers. */ private List searchResult; /** * Text field where the user inputs the identifier or the partial identifier * of the node. */ private Text searchTextField; /** BTEditor whose BT was the target of the search. */ private BTEditor targetEditor; /** * Label that displays the name of the BTEditor whose BT was the target of * the search. */ private Label targetEditorName; /** * * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { this.searchResult = new Vector(); /* Initialize the global composite. */ this.global = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, false); layout.marginHeight = 0; layout.marginWidth = 0; this.global.setLayout(layout); /* * Create the top part of the composite (the part containing the search * text field and the search button. */ createTopComposite(this.global); /* * Create the bottom part of the composite (the part containing the * search result). */ createBottomComposite(this.global); } /** * * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ public void setFocus() { } /** * Creates the Composite that contains the result of the search. The * Composite contains the {@link #resultsTable} element. * * @param parent * the Composite where the created Composite will be placed. */ private void createBottomComposite(Composite parent) { this.resultsTable = new TableViewer(parent, SWT.SINGLE | SWT.BORDER); this.resultsTable.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); this.resultsTable.setLabelProvider(new ResultsTableLabelProvider()); this.resultsTable.setContentProvider(new ResultsTableContentProvider()); this.resultsTable.setInput(this.searchResult); /* Sort elements by its String representation. */ this.resultsTable.setSorter(new ViewerSorter()); /* * Listener that will select the node in the target BTEditor and which * also activates the target BTEditor. */ this.resultsTable.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (!selection.isEmpty()) { Identifier selectedNode = (Identifier) ((IStructuredSelection) selection) .getFirstElement(); targetEditor.selectNode(selectedNode); Utilities.activateEditor(targetEditor); } } }); this.targetEditorName = new Label(parent, SWT.NONE); this.targetEditorName.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); } /** * Creates the Composite that shows the search text field and the search * button. * * @param parent * the Composite where the created Composite will be placed. */ private void createTopComposite(Composite parent) { Composite searchComposite = new Composite(parent, SWT.NONE); searchComposite.setLayout(new GridLayout(3, false)); searchComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); Label label = new Label(searchComposite, SWT.NONE); label.setText("Node ID:"); label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); this.searchTextField = createTextField(searchComposite); createSearchButton(searchComposite); } /** * Creates the search Button. * * @param parent * the Composite where the Button is placed. */ private void createSearchButton(Composite parent) { Button button = new Button(parent, SWT.PUSH); button.setText("Search"); button.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); /* When clicked, the button performs the search. */ button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { String searchText = searchTextField.getText(); performSearch(searchText); } }); } /** * Creates the search text field (Text). * * @param parent * the Composite where the text field is placed. * @return the text field. */ private Text createTextField(Composite parent) { final Text textField = new Text(parent, SWT.BORDER); textField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); /* If "enter" is pressed, perform the search. */ textField.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.CR) { performSearch(textField.getText()); } } }); return textField; } /** * Label provider for the search results table ( * {@link NodesSearcher#resultsTable}). * * @author Ricardo Juan Palma Durán * */ private class ResultsTableLabelProvider implements ITableLabelProvider { public void addListener(ILabelProviderListener listener) { } public void dispose() { } public boolean isLabelProperty(Object element, String property) { return false; } public void removeListener(ILabelProviderListener listener) { } public Image getColumnImage(Object element, int columnIndex) { return null; } public String getColumnText(Object element, int columnIndex) { return ((Identifier) element).toString(); } } /** * Content provider for the search results table ( * {@link NodesSearcher#resultsTable}). * * @author Ricardo Juan Palma Durán * */ private class ResultsTableContentProvider implements IStructuredContentProvider { public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public Object[] getElements(Object inputElement) { return ((List) inputElement).toArray(); } } /** * Given the search text, this method searches, in the BT of the currently * active BTEditor, those nodes whose ID contains text, * ignoring case. It stores, in {@link NodesSearcher#searchResult}, the set * of nodes with a matching ID. Also it updates the * {@link NodesSearcher#resultsTable} and * {@link NodesSearcher#targetEditorName} fields. * * @param text * the search text. */ private void performSearch(String text) { BTEditor activeEditor = Utilities.getActiveBTEditor(); this.searchResult.clear(); if (activeEditor != null) { this.targetEditor = activeEditor; BT currentBT = activeEditor.getBT(); searchNode(text, this.searchResult, currentBT.getRoot()); this.targetEditorName.setText("Searched in: " + this.targetEditor.getTitle()); } this.global.layout(); this.resultsTable.refresh(); } /** * Method that recursivelly perform the search, starting from * currentNode. If stores in foundNodes the * matching nodes. text is the search text. */ private void searchNode(String text, List foundNodes, BTNode currentNode) { if (currentNode.getID().toString().toLowerCase().contains(text)) { foundNodes.add(currentNode.getID()); } for (BTNode child : currentNode.getChildren()) { searchNode(text, foundNodes, child); } } } ================================================ FILE: JBTEditor/jbt.tools.bteditor.feature/.project ================================================ jbt.tools.bteditor.feature org.eclipse.pde.FeatureBuilder org.eclipse.pde.FeatureNature ================================================ FILE: JBTEditor/jbt.tools.bteditor.feature/build.properties ================================================ bin.includes = feature.xml ================================================ FILE: JBTEditor/jbt.tools.bteditor.feature/feature.xml ================================================ [Enter Feature Description here.] [Enter Copyright Description here.] [Enter License Description here.] ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ================================================ FILE: README.txt ================================================ ******* License ******* JBT is released under the Apache License, Version 2.0. **************************** What's Java Behaviour Trees? **************************** JBT is a Java framework for building and running behaviour trees. In the past few years, behaviour trees have been widely accepted as a tool for defining the behaviour of video games characters. However, to the best of our knowledge, there is no free-software Java implementation of such concept. With JBT we intend to provide a solid framework to build and run behaviour trees in Java. JBT has two main parts. On the one hand, there is the JBT Core (it is the Eclipse SDK project under the "./JBTCore" directory), which implements all the classes needed to create and run behaviour trees. JBT Core basically lets the user create behaviour trees in pure Java and then run them. In order to ease the task of creating behaviour trees, JBT Core includes several tools that automatize the process of creating behaviour trees. In particular, it can create the Java source code of a behaviour tree from its description in an XML format. By doing so, the user of this framework basically has to worry only about defining behaviour trees in XML files and implementing the low level actions and conditions that his trees will use, which are domain-dependant (that is, they depend on the game being played). On the other hand, there is the JBT Editor (which is composed of two Eclipse SDK projects under the "./JBTEditor" directory). The JBT Editor is a GUI application that can be used for defining behaviour trees, and then exporting them into XML files in the format that the JBT Core understands. The JBT Editor offers a set of standard nodes for building behaviour trees. It includes nodes such as sequences, parallels, decorators, etc. For low level actions and conditions, the user can provide their conceptual definition through Make Me Play Me (MMPM) domain files (for more information on MMPM, see the Sourceforge page of the project "Darmok 2"). The JBT Editor is an Eclipse RCP application. You must use Eclipse SDK in order to run it. JBT implements a behaviour tree model which is mainly based on that of the book "Artificial Intelligence for Games", second edition, by Ian Millington and John Funge. JBT also includes the concept of "guard" and static and dynamic priority lists, which make use of guards. JBT behaviour trees are driven by ticks, which means that, in order for them to have CPU time, they need to be externally ticked. By following this pattern, the user can control how much CPU time the behaviour tree consumes. *********************** For more information... *********************** For more information on JBT, see the user's guide, which is located under the directory "./UserGuide". It contains the documentation itself along with the Latex source files. ********************* Directories structure ********************* ./Documentation: contains the project's documentation, explaining what JBT is and how the framework works. It also contains the source code of the documentation, which is written in Latex. Images are in several formats, including ODG. ./JBTCore: contains the main framework of JBT, that is, the set of classes that are needed in order to use it. However, it is encouraged to use the JBT Editor, placed in "./JBTEditor". This project is structured as an Eclipse project, so we encourage the user to use Eclipse SDK when working on it. ./JBTEditor: contains the JBT Editor, a GUI application that lets the user define behaviour trees and export them into XML files that are easily handled by JBT. When creating behaviour trees, the user should use this application. This is an Eclipse RCP application, so Eclipse SDK must be used to run it. In order to run the application, just open the file "bteditor.product" with the "Product Configuration Editor" (right click on the file, then "Open With -> Product Configuration Editor"), and in the "Overview" page, click on "Launch an Eclipse application". ./UserGuide: contains the user's guide. It also contains the source code of the user's guide, which is written in Latex. Images are in several formats, including ODG. ******************* Binary distribution ******************* If you just want to get the compiled version of JBT or its documentation, go to our SourceForge download page: https://sourceforge.net/projects/jbt/files/?source=navbar ================================================ FILE: UserGuide/UserGuide.tex ================================================ \documentclass[a4paper]{article} \usepackage{graphicx} \usepackage{minted} \usepackage{xspace} \usepackage{verbatim} \newcommand{\starcraft}{StarCraft\xspace} \title{Java Behaviour Trees, User Guide} \author{Ricardo Juan Palma Dur\'an} \begin{document} \maketitle \vspace{-0.5cm} \tableofcontents \clearpage \section{Introduction} Java Behaviour Trees (JBT) is a Java framework for building and running behaviour trees (BTs). In the past few years, BTs have been widely accepted as a tool for defining the behaviour of video games characters. However, to the best of our knowledge, there is no free-software Java implementation of such technology. With JBT we intend to provide a solid framework to build and run BTs in Java. JBT has two main parts. On the one hand, there is the JBT Core (it is the Eclipse SDK project under the "./JBTCore" directory of the repository), which implements all the classes needed to create and run BTs. JBT Core basically lets the user create BTs in pure Java and then run them. In order to ease the task of creating BTs, JBT Core includes several tools that automatize the process of creating BTs. In particular, it can create the Java source code of a BT from its description in an XML format. By doing so, the user of this framework basically has to worry only about defining BTs in XML files and implementing the low level actions and conditions that his trees will use, which are domain-dependant (that is, they depend on the game being played). We provide a .jar file with all the JBT Core classes. Of course, in order to get the last version of the JBT Core the repository can be accessed. On the other hand, there is the JBT Editor (which is composed of two Eclipse SDK projects under the "./JBTEditor" directory of the repository). The JBT Editor is a GUI application that can be used for defining BTs, and then exporting them into XML files in the format that the JBT Core understands. The JBT Editor offers a set of standard nodes\footnote{Note that, when talking about BTs, \textit{node} and \textit{task} are used interchangeably.} for building BTs. It includes nodes such as sequences, parallels, decorators, etc. For low level actions and conditions, the user can provide their conceptual definition through Make Me Play Me (MMPM) domain files (for more information on MMPM, see the Sourceforge page of the project "Darmok 2"). The JBT Editor is an Eclipse RCP application, so you must use Eclipse SDK in order to run it. An alternative to run it is to use the executable files provided for each platform. Of course, if order to get the last version of the JBT Editor the repository can be accessed. JBT implements a BT model which is mainly based on that of the book "Artificial Intelligence for Games", second edition, by Ian Millington and John Funge. JBT also includes the concept of "guard" and static and dynamic priority lists, which make use of guards. JBT BTs are driven by ticks, which means that, in order for them to have CPU time, they need to be externally ticked. By following this pattern, the user can control how much CPU time the BT consumes. In this document we explain how JBT can be used to build and run BTs. This process has the following steps: \begin{itemize} \item Defining low level actions and conditions to be used in the trees. These actions and conditions are defined in the MMPM format. \item Implementing the low level actions and conditions. The user has to define how the low level actions and conditions work. JBT does not know how these domain-dependent actions and conditions work, so the user has to provide a Java implementation of them. \item Creating BTs with the JBT Editor. Here, the user creates BTs that are exported into generic XML files. \item Creating the Java declaration of the BTs that were declared in the XML files. This is automatically done by one of JBT's tools. \item Running the BTs by using the core classes of JBT. \end{itemize} In the next sections we will describe all of these steps. Also, we will conceptualize them through a real example on a real game, since we will build a tree that is able to control a Terran Marine of the Real Time Strategy Game \starcraft. \section{JBT, an Overview} In this section we describe the JBT architecture as well as the main features that BTs have. \subsection{Model Driven by Ticks}\label{sec:ModelDrivenByTicks} JBT implements a BT model driven by ticks. A BT must be evaluated through ticks, so every game cycle an external caller \textit{ticks} the tree in order for the tree to update its status. A tick is just a way of giving the tree some CPU time to update their status; in particular, ticks are used to give the nodes of the tree some time to evaluate whether they have finished or not, and consequently make the tree evolve. The simplest approach to BTs driven by ticks is that of ticking the root node and then letting each node recursively tick its children according to its semantics. However, this is a very inefficient process, since in general the major part of the nodes of the tree are just waiting for their children to finish. Therefore, they should not receive ticks, since unless their children are done they will do nothing useful when receiving the tick. Therefore, in general only very few nodes should be ticked at a game cycle, and as a result JBT implements a model in which there is a list of \textit{tickable} nodes. Only the nodes in the list can be ticked. \subsection{Model Independent from Execution}\label{sec:ModelIndependentFromExecution} When running a BT, there should be a clear distinction between the tree that is being run (the model) and how it is actually being run (the execution). For each particular behaviour, we distinguish between the \textit{Model BT} that defines it and how it is being run. The \textit{how} is what the \textit{BT Executor} does. Basically, for every entity in the game that wants to run a behaviour (Model BT), there is a BT Executor. The BT Executor takes the Model BT and processes it (without modifying it), simulating the behaviour that is represented by the Model BT. This choice implies that, apart from the Model BT, there is another type of tree, the \textit{Execution BT}. When an entity wants to execute a behaviour, the BT Executor takes the Model BT and creates an Execution BT to execute the behaviour. The BT Executor along with the Execution BT know how to run the behaviour that the Model BT represents. \subsection{Architecture}\label{sec:Architecture} Figure \ref{fig:Overview} shows an overview of the JBT Core architecture. There is a Model BT that represents a particular behaviour. Also, there is a BT Executor for every entity that wants to run the Model BT. Each BT Executor makes use of the Model BT and builds an Execution BT that actually runs the behaviour conceptualized by the Model BT. An external Game AI ticks the BT Executors, in order for them to update the trees that they are running. \begin{figure} \centering \includegraphics[width=\textwidth]{./Images/Overview.pdf} \caption{Overview of the BT architecture} \label{fig:Overview} \end{figure} The user of the framework does not have to know all the details about how JBT internally works. However, since he has to implement some classes in order to run his own trees, at least he should know the general architecture of JBT. \subsection{BT Model}\label{sec:BTModel} Before even starting to explain all the steps required to build and run BTs with JBT, we have to first think about what BT model JBT offers. JBT implements a BT model that is mainly based on that of \cite{Millington09}. Our model also include guards and static and dynamic priority lists, as described in \cite{QueryEnabledBTs}. With this model the user can implement a wide range of behaviours. For instance, the tree of figure \ref{fig:SimpleOpenDoor} represents a simple tree that is used by a game character that wants to open a door. First of all, it checks if the door is closed (condition \textit{DoorClosed}). If so, then it tries to open it by executing the action \textit{OpenDoor}. \begin{figure} \centering \includegraphics[width=0.3\textwidth]{./Images/SimpleOpenDoor.png} \caption{a simple behaviour tree} \label{fig:SimpleOpenDoor} \end{figure} In the tree of figure \ref{fig:SimpleOpenDoor} we can see four nodes. The node called \textit{Root} is just the root of the tree, and it has no actual meaning apart from it. Then there is a \textit{Sequence} node, which runs in sequence both of its children, the \textit{DoorClosed} condition and the \textit{OpenDoor} action. The Sequence node is a standard node, but both the DoorClosed and the OpenDoor nodes are domain dependent, that is, they have been defined by the user so they have a useful meaning within the context of the game being played. The tree of figure \ref{fig:ComplexEnterRoom} represents the behaviour of a character that is trying to enter a room. The topmost selector succeeds as long as one of its children succeed. The first child tries to enter the room when the door is locked. In such case, the character tries several tactics to open the door. First, if it has the key, it uses it to open the door. If it does not have the key, but it has a grenade, then it uses the grenade in order to blow the door up. Finally, if none of the above conditions are met, the character will try to enter the room through its window (note that here a \textit{Subtree Lookup} node is used. This node just runs a tree that is already defined; in this case, the tree that will be run is \textit{EnterThroughWindow}). On the other hand, if the door is not locked and it is closed, the character will just open it up. \begin{figure} \centering \includegraphics[width=0.7\textwidth]{./Images/ComplexEnterRoom.png} \caption{a complex behaviour tree} \label{fig:ComplexEnterRoom} \end{figure} \subsubsection{Execution Context}\label{sec:ExecutionContext} All nodes in a BT have an \textit{execution context}, which is usually shared by all of them. The execution context, or \textit{context} for short, acts as a blackboard that can be used by nodes in order to write and read variables. For instance, a node may write a variable into the context, under a name \textit{MyVariable}. This variable can be read then by using its name, \textit{MyVariable}. This way, the context can be seen as a way for the nodes of a BT to communicate. However, not always all the nodes share the same context. In general, the context of a BT is passed down from parents to children. Thus, the initial context is that of the root of the tree, which will pass it to its children. The root's children will pass the context to their own children, and so on. Nevertheless, some nodes do not pass their own context to their children, but another one instead. This new context may be empty or not, and it may be of a different type. JBT supports the following types: \begin{itemize} \item Basic Context: this is just a normal context, with no especial features. It is the context that the user of the framework can create. Other types of contexts are managed through decorator tasks, and the user cannot create them. \item Hierarchical Context: a Hierarchical Context has another context (the \textit{input context}) as a base. When the Hierarchical Context cannot find a variable within its own set of variables, it will ask its input context for the variable. Note that the Hierarchical Context can be used to build a complex hierarchy of context: if the input context is a Hierarchical Context too, the request for the variable may go up the hierarchy until a non-Hierarchical Context is reached. \item Safe Context: a Safe Context has another context (the \textit{input context}) as a base. Initially, all variables are read form the input context. However, when a variable is modified, its value is not modified in the input context, but locally modified instead. From then on, the variable will be locally read (that is, from the set of variables of the Safe Context) instead of reading if from the input context. Thus, the input context is never modified. A Safe Context can be used to situations in which a certain context (the input context) must be used in read-only mode. \item Safe Output Context: a Safe Output Context behaves much in the same way as the Safe Context. It has another context, the \textit{input context}, as a base. However, this context also contains a set of \textit{output variables} (that is, a list of variables' names). The list of output variables represents the variables that can be modified in the input context. Variables other than those in the list of output variables will be stored locally in the set of variables of Safe Output Context, just as if it were a Safe Context. Thus, when the Safe Output Context modifies the value of a variable, it will normally set its value in a local variable (that is, a variable belonging to the Safe Output Context). However, if the variable is one of the list of output variables, the value will be set in the input context, which will therefore be modified. When retrieving variables, a variable in the list of output variables will always be retrieved from the input context. A variable that is not in the list of output variables will also be retrieved from the input context; however, when such variable is modified, the value will be retrieved from the Safe Output Context (that is, from the moment a variable that is not in the list of output variables is modified, it is managed locally). \end{itemize} \subsubsection{Native Tasks} JBT offers a wide range of tasks that can be used to build behaviour trees. JBT basically implements the BT model described in \cite{Millington09}, but extended with guards. JBT supports the following tasks: \begin{itemize} \item Composite tasks: tasks with one or more children, whose execution depends on the execution of their children. The task's children are ordered. \begin{itemize} \item Sequence: task that sequentially executes all its children in order. If one fails, the Sequence task fails. If all succeeds, the Sequence task succeeds. \item Selector: task that sequentially executes all its children in order. If one succeeds, the Selector task succeeds. If all fail, the Selector task fails. \item Parallel: task that concurrently executes all its children. A Parallel task does have a \textit{parallel policy}. If the parallel task's policy is \textit{sequence}, the parallel fails if one child fails; if all succeed, then the parallel succeed. If the parallel task's policy is \textit{selector}, the parallel fails if all its children fail. If one succeeds, then the parallel also succeeds. \item Random Selector: task that executes all its children in a random order. If one fails, the Sequence task fails. If all succeeds, the Sequence task succeeds. \item Random Sequence: task that sequentially executes all its children in random order. If one succeeds, the Selector task succeeds. If all fail, the Selector task fails. \item Dynamic Priority List: task that executes the child with the highest priority whose guard is evaluated to true. At every AI cycle, the children's guards are re-evaluated, so if the guard of the running child is evaluated to false, it is terminated, and the child with the highest priority starts running. The Dynamic Priority List task finishes when no guard is evaluated to true (thus failing) or when its active child finishes (returning the active child's termination status). \item Static Priority List: task that executes the child with the highest priority whose guard is evaluated to true. Unlike the Dynamic Priority List, the Static Priority List does not keep evaluating its children's guards once a child is spawned. The Static Priority List task finishes when no guard is evaluated to true (thus failing) or when its active child finishes (returning the active child's termination status). \end{itemize} \item Decorator tasks: tasks with one child whose purpose is to alter the way other tasks behave. \begin{itemize} \item Interrupter: task that controls the termination of its child task. An Interrupter simply lets its child task run normally. If the child returns a result, the Interrupter will return it. However, the Interrupter can be asked to terminate the child task and return an specified status when done so. The task that can interrupt an Interrupter is the Perform Interruption task. \item Inverter: task used to invert the status code returned by its child. When the decorated task finishes, its status code gets inverted. \item Limit: task that limits the number of times a task can be executed. This decorator is used when a task (the child of the decorator) must be run a maximum number of times. When the maximum number of times is exceeded, the decorator will fail forever on. \item Repeat: task that runs its child task forever. When its child task finishes, it runs it once more. \item Until Fail: task that runs its child as long as it does not fail. When the child task fails, Until Fail succeeds. \item Succeeder: task that runts its child but, no matter its returned status, the succeeder will always succeed. \item Hierarchical Context Manager: task that creates a new context for its child. The context that it creates is an empty (with no variables) Hierarchical Context whose input context is the context that is passed to the Hierarchical Context Manager. \item Safe Output Context Manager: task that creates a new context for its child. The context that it creates is an empty (with no variables) Safe Output Context whose input context is the context that is passed to the Safe Output Context Manager. \item Safe Context Manager: task that creates a new context for its child. The context that it creates is an empty (with no variables) Safe Context whose input context is the context that is passed to the Safe Context Manager. \end{itemize} \item Leaf tasks: tasks with no children. \begin{itemize} \item Wait: task that keeps running for a period of time, and then succeeds. The user can specify for how long (in milliseconds) the Wait task should be running. \item Subtree Lookup: see the following sections to see what this node does. \item Perform Interruption: task that interrupts an Interrupter task. \item Variable Renamer: task that renames a variable in the context. \item Success: task that immediately succeeds. \item Failure: task that immediately fails. \item Action: generic action that is executed in the game engine. \item Condition: generic condition that is executed in the game engine. \end{itemize} \end{itemize} \section{Step 1: Defining Low Level Actions and Conditions}\label{sec:DefiningLowLevelActionsAndConditions} The first step\footnote{Well, it does not necessarily have to be the first step, but we have to start at somewhere.} when creating BTs is to define the set of low level actions and conditions that the trees will be using. These actions and conditions are domain dependent, that is, they depend on the game that the trees will be run for. For instance, if we are dealing with a first person shooter (FPS from now on), then we may need actions and conditions such as those used in the trees of section \ref{sec:BTModel}. However, we are going to build a more complex example. Here we are going to define a behaviour tree that is able to control a Terran Marine of \starcraft, so we have to define actions and conditions that are useful for such context. The behaviour that we want to implant in the Terran Marine is as follows: The marine is constantly checking three conditions. If there is no danger around the marine, then he just patrols around its current position. Patrolling around a position means that the marine will move randomly around a central point, and will attack whatever he finds on its way. However, if he finds himself in a low level danger situation (that is, a dangerous situation he thinks he can survive), he will try to kill whatever enemy finds dangerous. On the other hand, if he finds himself in a high level danger situation (that is, a dangerous situation he thinks he cannot survive), he will run away to the closest base. We therefore define some actions and conditions that will be used by the BT: \begin{itemize} \item Actions: \begin{itemize} \item Attack: this action just makes the marine attacks a specific unit. \item Move: this action makes the marine move to a specific target position on the map. \item AttackMove: this action makes the marine mote to a specific target position on the map. Also, if he finds an enemy on its way, he will combat the enemy. \item ComputeClosestBasePosition: this action computes the position of the base that is closest to the marine. \item ComputeCharacterPosition: this action just computes the current position of the marine. \item ComputeRandomClosePosition: given a position $A$, this action computes a random position that is close to $A$. \end{itemize} \item Conditions: \begin{itemize} \item LowDanger: this condition checks if the marine is in a low danger situation. \item HighDanger: this condition checks if the marine is in a high danger situation. \end{itemize} \end{itemize} Actions and conditions must be defined according to the MMPM domain file format. For those unaware of what MMPM is, this does not really pose a problem, since what we are really interested in is the format that MMPM follows in order to define actions and conditions. A MMPM domain file defines the conceptual level of a game (its \textit{domain}), by declaring what \textit{entities}, \textit{actions}, \textit{sensors} and \textit{goals} are present in the game. We will not describe all of them, but only what we need to declare actions and conditions that can be used in BTs. A MMPM domain file has the following structure: \begin{minted}{xml} \end{minted} However, we are only interested in the set of actions and conditions, so $$ and $$ can be left empty (but they actually have to be present). The $$ element defines the set of actions that are present in the game, which are also the set of low level actions that can be used when building BTs. An $$ element contains a sequence of $$ elements, each one being an action. An $$ element has one attribute, its name (which is called $name$). The $$ element defines the set of \textit{sensors} that can be used in the game. A $$ element contains a sequence of $$ elements, each one being a sensor. A $$ element has two attributes: its $name$ and its $type$. In MMPM, a \textit{sensor} is an operation that queries something about the world. As a result, a sensor can return \textit{any}\footnote{Really not \textit{any}.} type of value. Here we are interested in sensors whose type is \textit{boolean} ($BOOLEAN$ in the MMPM domain file), since they represent what in BTs is known as conditions, that is, a query operation that returns either true or false. Therefore, the set of boolean sensors of the MMPM domain file is the set of conditions that can be used when building BTs. Both actions and sensors may have input parameters. An input parameter is a parameter that is supposed to be used by the action or sensor when running. For instance, the \textit{Move} action above does have one input parameter, which is the target position where the unit must go to. An input parameter has a name and a type. Thus, both $$ and $$ elements may have a sequence of $$ elements, each one being a parameter. Each $$ element has two attributes, its $name$ and its $type$. Therefore, actions and sensors have the following structure: \begin{minted}{xml} ... ... \end{minted} MMPM supports the following types for parameter types: $FLOAT$, $BOOLEAN$, $STRING$, $INTEGER$, $DIRECTION$, $COORDINATE$, $PLAYER$, $ENTITY\_ID$ and $ENTITY\_TYPE$. This set of parameter types may seem overwhelming, which is why in general several of them are not used. $FLOAT$, $BOOLEAN$, $STRING$ and $INTEGER$ are self-explanatory. $DIRECTION$ represents an integer value, which is why, if it is used as the type of a parameter, JBT will treat it just as an integer. $COORDINATE$ represents a coordinate in an N-dimensional coordinate system. In practice, a $COORDINATE$ value is a non-empty sequence of real values (for instance, "23 -4.5 67", "-3.45 " or "12.45 -0.34 9.44 -12.3"). $PLAYER$ represents the name of a player of the game, so in practice it is treated as a string ($STRING$). $ENTITY\_ID$ represents the identifier of an entity in the game. In practice, it is treated as a string ($STRING$). $ENTITY\_TYPE$ represents the type of an entity. In practice, it is treated as a string ($STRING$). As a consequence, the user will generally use just $FLOAT$, $BOOLEAN$, $STRING$, $INTEGER$ and $COORDINATE$, since the rest of MMPM parameter types are equivalent to $STRING$. MMPM format, however, does not include an important parameter type, \textit{object}. In general, there will be actions and sensors will make use of input parameters of many types. In order to be able to manage a wide range of types, JBT extends the MMPM domain file format so that parameters also accept the $OBJECT$ type. An $OBJECT$ is just a variable of any type. The MMPM domain file that defines the set of actions and conditions for the Terran Marine example is as follows: \begin{minted}{xml} \end{minted} One of the ways nodes in BTs communicate with each other is by using the execution \textit{context}: a node may write a variable into the context and another node may use it later. In this scenario it is therefore very important that nodes know the name of the variables that other nodes put into the context. In the set of actions and conditions above there are several nodes that manipulate the context. In particular: \begin{itemize} \item The \textit{ComputeCharacterPosition} action writes into the context a variable of type $COORDINATE$ containing the current position of the unit. The name of such variable is \textit{CharacterPosition}. \item The \textit{ComputeClosestBasePosition} action writes into the context a variable of type $COORDINATE$ containing the position of the closest base. The name of such variable is \textit{ClosestBasePosition}. \item The \textit{ComputeRandomClosePosition} action writes into the context a variable of type $COORDINATE$ containing a random position that is close to the input position. The name of such variable is \textit{RandomClosePosition}. \item The \textit{LowDanger} sensor writes into the context a variable of type $ENTITY\_ID$ containing the identifier of the closest dangerous enemy. The name of such variable is \textit{LowDangerTarget}. \end{itemize} \section{Step 2: Implementing Low Level Actions and Conditions}\label{sec:ImplementingLowLevelActionsAndConditions} Once that low level actions and conditions have been defined (see section \ref{sec:DefiningLowLevelActionsAndConditions}), the next step is to provide an implementation for them. JBT does not know what does an action such as \textit{ComputeCharacterPosition} or \textit{AttackMove}. Therefore, it is the user of the framework who has to tell JBT how they work. The life cycle of actions and conditions of a BT is very simple: initially, when the flow of execution reaches the node, it is \textit{spawned}. From then on, at every \textbf{game tick}, the node is \textit{ticked}. Every time the node is ticked, it has to report about its termination status, so that the tree may evolve in case the node has finished. As a result, the programmer will have to define how all the domain dependent actions and conditions behave when they are spawned and ticked. Actions and conditions are each represented by two classes of the JBT Core, \textit{jbt.model.task.leaf.action.ModelAction.java} and \textit{jbt.execution.task.leaf.action.ExecutionAction.java} in the case of actions, and \textit{jbt.model.task.leaf.condition.ModelCondition.java} and \textit{jbt.execution.task.leaf.condition.ExecutionCondition.java} in the case of conditions. Domain dependent actions such as the ones defined above must extend these classes in order for JBT to know how to work with them. In JBT there are two classes for every type of node. Remember from section \ref{sec:ModelIndependentFromExecution} that in JBT there are two types of BTs, the \textit{Model BT} and the \textit{Execution BT}. The Model BT is composed of \textit{model tasks\footnote{Remember that in BT terminology, a task and a node are the same thing.}}, while the Execution BT is composed of \textit{execution tasks}. It is the execution tasks that define how the tasks work, that is, how they behave when they are spawned and ticked. In the case of actions and conditions, the four classes presented above are the base classes for their respective representation as model tasks and execution tasks. JBT Core offers a tool that semi-automatize the task of creating all the classes from each action and condition of the MMPM domain file. It is the Java class \textit{jbt.tools.btlibrarygenerator.ActionsAndConditionsGenerator.java}. For every MMPM action, ActionsAndConditionsGenerator creates two classes: one extending ModelAction, which conceptually represents the action, and another one extending ExecutionAction, which represents how the action actually works -whose abstract methods must be completed in order for the action to perform any task at all. We will explain this later-. Also, for every MMPM boolean sensor, two classes are created: one extending ModelCondition, which conceptually represents the condition (sensor), and another one extending ExecutionCondition, which represents how the condition actually works -whose abstract methods must be completed in order for the condition to perform any task at all. We will explain this later-. The syntax of the program is as follows: \begin{verbatim} ActionsAndConditionsGenerator -c configurationFile [-r relativePath] [-o] \end{verbatim} Where \verb@configurationFile@ is an XML file that contains all the information required to run the application. The syntax of such file is: \begin{minted}{xml} MMPMDomainFile1 MMPMDomainFile2 ... MMPMDomainFileN Name of the package for generated model action classes Name of the package for generated model condition classes Name of the directory where model actions are created Name of the directory where model conditions are created Name of the package for generated execution action classes Name of the package for generated execution condition classes Name of the directory where execution actions are created Name of the directory where execution conditions are created \end{minted} The order in which the elements are specified is not relevant. If the input files do contain only actions, parameters related to conditions may not be specified, and vice versa. The -r option is used to add a path to the beginning of the files listed in the configuration file; as a result, each file is considered to be placed at the path specified in the -r option. The -r option may not be specified, in which case the files are considered to be at the current execution directory. The -o option (standing for \textit{overwrite}) is either is specified or not. If it is not specified, generated output files will not overwrite any existing file in the file system, and as a result, the corresponding class file will not be produced in case there is a file with the same name in the file system. If the option -o is specified, then generated output files will overwrite any file in the file system whose name matches. So, in brief, this program parses a MMPM domain file and, for each action and boolean sensors produces the JBT classes that are required to run such actions and conditions in a BT. In particular, the created execution classes define what they will do when spawned and ticked. The generated ModelAction and ModelCondition classes are complete, so they do not need to be modified after being created by the ActionsAndConditionsGenerator. However, the ExecutionAction and ExecutionCondition generated classes contain a set of abstract method that must be implemented according to the semantics of the respective actions and conditions, so that they do what they are expected to do. This is the only step that must be done in order for JBT to be able to work with the low level actions and conditions provided by the user. In particular, the abstract methods that must be implemented are: \begin{minted}{java} protected void internalSpawn(); protected Status internalTick(); protected void internaTerminate(); protected void restoreState(ITaskState state); protected ITaskState storeState(); protected ITaskState storeTerminationState(); \end{minted} \textit{internalSpawn()} and \textit{internalTick()} are the most important methods, so they should be well implemented. \textit{internalSpawn()} represents the spawning process of the task (action or condition). When the flow of execution of the tree reaches the task, \textit{internalSpawn()} gets called. Therefore, this method must be defined so that it starts the process associated to the task. For instance, the \textit{internalSpawn()} method of the \textit{Move} action above should order the current unit to go to the target position; the \textit{internalSpawn()} method of the \textit{Attack} action above should order the current unit to attack the target enemy. The automatically generated skeleton contains an initial implementation of the \textit{internalSpawn()} method, which is as follows: \begin{minted}{java} protected void internalSpawn() { /* * Do not remove this first line unless you know what it does and you * need not do it. */ this.getExecutor().requestInsertionIntoList( jbt.execution.core.BTExecutor.BTExecutorList.TICKABLE, this); /* TODO: this method's implementation must be completed. */ System.out.println(this.getClass().getCanonicalName() + " spawned"); } \end{minted} This initial definition contains a very important aspect of the execution process of the task. As it is said in the comments, the first line should not be removed unless the user knows what it does and he thinks it is not necessary to do it. What that line does is to request that the task be inserted into the list of tickable nodes (section \ref{sec:ModelDrivenByTicks}). Since in general this is what we want the task to do (because we want the task to receive ticks), \textbf{that line should not be removed}. When implementing all these abstract methods, the user may access the execution context of the task by calling \textit{this.getContext()}. That method just returns the context of the task as an \textit{IContext} object. The \textit{IContext} interface defines two main methods, one for reading a variable from the context, and another one for writing a variable into the context: \begin{minted}{java} public interface IContext { /** * Returns the value of a variable whose name is name, or null * if it is not found. */ public Object getVariable(String name); /** * Sets the value of a variable. If the variable already existed, its value * is overwritten. value may be null in order to clear the * value of the variable. */ public boolean setVariable(String name, Object value); ... } \end{minted} Remember that MMPM domain files also let the designer specify input parameters for actions and sensors. These are parameters that actions and sensors are supposed to use when running. The generated JBT ExecutionAction and ExecutionCondition classes include \textit{getter} methods for such input parameters. As a result, the programmer will be able to access the value of the input parameters in the abstract methods he has to implement, by using the getter methods. The value for the input parameters are either retrieved from the execution context(IContext) or directly provided at construction time, but these details are hidden from the programmer that implements the action or condition (he should just use the getter methods to retrieve whatever input parameters may be needed). For instance, for the \textit{Attack} action, the next getter method is created: \begin{minted}{java} /** * Returns the value of the parameter "target", or null in case it has not * been specified or it cannot be found in the context. */ public java.lang.String getTarget(){...} \end{minted} Thus, in the implementation of all the abstract methods, the user should use this \textit{getTarget()} method if he wanted to retrieve the identifier of the target unit to attack. This whole thing about the getter methods may be a little bit confusing at first. However, bear in mind that when the user creates a behaviour tree (which we will explain later), he can either specify that the input parameter of a task must be retrieved from a variable of the context, or provide an actual value for the parameter. When the task is spawned and run, it does not really care about where the parameter comes from as long as there is a value that it can use. The generated getter methods hide these details, so no matter where the parameter comes from (either from the context or from an actual value provided by the user when he created the BT), it just provides the value that the task expects to use. Once the task has been spawned, it will be ticked whenever the BT gets ticked. The ticking process is performed by the \textit{internalTick()} method. Thus, from the moment the task gets spawned, at every tick, \textit{internalTick()} will be called. \textit{internalTick()} is in charge of keeping track of the termination status of the task. If the task has not finished yet when \textit{internalTick()} is called, then it must return the termination status \textit{Status.RUNNING}. If the task has finished successfully, then the method should return the termination status \textit{Status.SUCCESS}. If the task has finished unsuccessfully, then the method should return \textit{Status.FAILURE}. For instance, the \textit{internalTick()} method of the \textit{Move} action of our Terran Marine example should check if the unit has arrived at the target position. If it has not, then \textit{Status.RUNNING} should be returned. If the unit has arrived at the target position, then \textit{Status.SUCCESS} should be returned. Finally, y for some reason the action could not be completed (for instance because the target position is unreachable), then \textit{Status.FAILURE} should be returned. Note that, even though the \textit{Status} enum has more values other than \textit{SUCCESS}, \textit{FAILURE} and \textit{RUNNING}, the \textit{internalTick()} method must return only one of these three. Doing otherwise will throw an exception. One of the ideas behind the \textit{driven by ticks} architecture is that, when the tree is ticked, it should not take very long for it to return, so \textit{internalSpawn()} and \textit{internalTick()} are supposed to return very quickly. However, sometimes tasks perform computationally expensive processes. In that case, instead of performing the expensive computation inside the \textit{internalSpawn()} method of the task, it should be performed in another execution thread. When \textit{internalSpawn()} is called, it should create another thread that carries out the computation. On the other hand, the \textit{internalTick()} method would query the thread to check if its computation has finished or not, and return \textit{Status.RUNNING}, \textit{Status.SUCCESS} or \textit{Status.FAILURE} accordingly. The \textit{internalTerminate()} method is not so important. Sometimes, when a BT is running, some of its tasks get abruptly interrupted. This happens for instance when one of the children of a parallel task (which is following the \textit{sequence policy}) fails. When that happens, all of its children, which were being concurrently evaluated, get terminated, so they must stop running. Other scenario in which this happens is when a perform interruption task interrupts an interrupter. In that case, the interrupter and its child stop running. It is for cases like these that the \textit{internalTerminate()} method is defined. The \textit{internalTerminate()} method must make the task stop running. For instance, in the case of the \textit{Move} action, it should order the unit to stop moving. Moreover, if the task started some other thread to perform some computations, it should stop it. In general, when the \textit{internalTerminate()} is called, the task should stop running and should also free whatever resources it acquired. With respect to the \textit{storeState()}, \textit{storeTermination} and \textit{restoreState(ITaskState state)}, they are related to \textit{persistent tasks}. Some tasks in BTs are persistent in the sense that, after finishing, if they are spawned again, they should remember past information. Take for example the Limit task. A Limit task allows to run its child node only a certain number of times (for example, 5). After being spawned, it has to remember how many times it has been run so far, so that, once the threshold is exceeded, it fails. In general, it could be said that some tasks need to retain some persistent information to be used in the future when the task is spawned again. All the persistent information of a task should be saved into an \textit{ITaskState} object. The ITaskState interface represents a collection of variables that can be accessed: \begin{minted}{java} public interface ITaskState { /* Returns the value of a state variable by its name. */ public Object getStateVariable(String name); } \end{minted} When a task finishes, (it returns \textit{Status.SUCCESS} or \textit{Status.FAILURE} in \textit{internalTick()}), the \textit{storeState()} method is automatically called by the framework. This method should then create and return an \textit{ITaskState} object containing all the persistent information that the task may need if it is spawned again in the future. If no persistent information is needed, then \textit{null} must be returned. An \textit{ITaskState} object is just a collection of variables that can be retrieved by name. In order for the user to create \textit{ITaskState} objects, he can make use of the factory class \textit{jbt.execution.core.TaskStateFactory}. It also may be the case that a task that is abruptly terminated (\textit{internalTerminate()} is called) needs to store persistent information. If that is the case, then \textit{storeTerminationState()} is automatically called by the framework, instead of \textit{storeState()}. \textit{storeTerminationState()} follows the same semantics as \textit{storeState()}. The persistent information that a task stores via its \textit{storeState()} and \textit{storeTerminationState()} methods is restored via the \textit{restoreState(ITaskState state)} method. This method is called just before the task is spawned (that is, before calling \textit{internalSpawn()}). In \textit{restoreState(ITaskState state)} the task should analyse the input \textit{ITaskState} object and restore whatever information it contains (if \textit{null}, then it means that there is no past information to remember). In the case of the \textit{Move} action of the Terran Marine example, the \textit{storeState()}, \textit{storeTerminationState()} and \textit{restoreState(ITaskState state)} methods are empty. As a final example on implementing low level actions and conditions, lets follow the whole process for the few actions and conditions defined in the domain explained in section \ref{sec:DefiningLowLevelActionsAndConditions}. Let us suppose that the MMPM domain file is stored in the file \textit{TerranMarineDomain.xml}. Then, the ActionsAndConditionsGenerator application could be run with the next configuration file (stored in \textit{configurationFile.xml}): \begin{minted}{xml} TerranMarineDomain.xml bts.actions bts.conditions src/bts/actions src/bts/conditions bts.actions.execution bts.conditions.execution src/bts/actions/execution src/bts/conditions/execution \end{minted} Let us suppose that the ActionsAndConditionsGenerator is called with the following arguments: \begin{verbatim} ActionsAndConditionsGenerator -c configurationFile.xml -r /home/outputDirectory \end{verbatim} Then, the ActionsAndConditionsGenerator will parse the domain file \textit{/home/outputDirectory/\-TerranMarine.xml}, and it will create output classes for each action and boolean sensor in the domain file, which will be stored in the corresponding files. For instance, for the \textit{Attack} action, two classes will be created, \textit{/home/outputDirectory/src/bts/actions/Attack.java} (the model task class) and \textit{/home/outputDirectory/src/bts/actions/execution/Attack.java} (the execution task class). For the boolean \textit{LowDanger} sensor, two classes will be created, \textit{/home/outputDirectory/src/bts/conditions/LowDanger.java} (the model task class) and \textit{/home/outputDirectory/src/bts/conditions/execution/LowDanger.java} (the execution task class). Then we should implement the abstract methods of all the execution classes. For instance, the implementation of the \textit{Move.java} execution class may be like this: \begin{minted}{java} /** ExecutionAction class created from MMPM action Move. */ public class Move extends jbt.execution.task.leaf.action.ExecutionAction { ... /** * Returns the value of the parameter "target", or null if not found * anywhere. */ public float[] getTarget() { /* Whatever has been automatically generated. */ ... } protected void internalSpawn() { this.getExecutor().requestInsertionIntoList( jbt.execution.core.BTExecutor.BTExecutorList.TICKABLE, this); /* * Retrieve the identifier of the entity (Terran Marine) running * this action from the context. Here we are supposing that the * context will contain it. */ String currentEntityID = (String) this.getContext(). getVariable("CurrentEntityID"); /* * Now we assume that there is a generic "Game Engine" that can * be used to send generic orders to units of the game. Note that, in * order to retrieve the target position, we use the automatically * generated getter method. */ GameEngine.sendMoveOrder(currentEntityID, this.getTarget()); } protected jbt.execution.core.ExecutionTask.Status internalTick() { /* * In this method we will just check whether the unit has reached the * target position. If the target position is unreachable, then * Status.FAILURE is returned. Otherwise, if the unit has reached the * target position, Status.SUCCESS is returned. Otherwise, * Status.RUNNING is returned. */ String currentEntityID = (String) this.getContext(). getVariable("CurrentEntityID"); if(!Util.reachablePosition(currentEntityID, this.getTarget())){ return Status.FAILURE; } float[] currentPosition = GameEngine.getPosition(currentEntityID); float[] targetPosition = this.getTarget(); if(Math.distance(currentPosition, targetPosition) < 0.5){ return Status.SUCCESS; } else{ return Status.RUNNING; } } protected void internalTerminate() { /* We just order the unit to stop. */ String currentEntityID = (String) this.getContext().getVariable( Constants.CONTEXT_CURRENT_ENTITY); GameEngine.stopUnit(currentEntityID); } protected void restoreState(jbt.execution.core.ITaskState state) { /* Does nothing. */ } protected jbt.execution.core.ITaskState storeState() { /* No persistent state information to return. */ return null; } protected jbt.execution.core.ITaskState storeTerminationState() { /* No persistent state information to return. */ return null; } } \end{minted} \section{Step 3: Creating BTs with the JBT Editor}\label{sec:CreatingBTsWithTheJBTEditor} Once that the domain dependent actions and conditions of the game have been defined and a JBT implementation for them has been provided, the next steps are quite easy to follow. It is now when the user should define the behaviour trees to use in the game. Following our Terran Marine example, we will define several trees that implement the behaviour that we described in section \ref{sec:DefiningLowLevelActionsAndConditions}. Behaviour trees are first described in XML files. Here we are not going to describe the XML format that JBT understands, since we discourage the user from writing them in plain text. Instead, we propose to use the JBT Editor (composed of the two projects under the ``./JBTEditor'' directory of the repository), a GUI application through which it is really easy to define behaviour trees. The JBT Editor is an Eclipse RCP application. As such, it must be run under the Eclipse SDK environment or use the executable files provided for each platform. When opening if from Eclipse, you have to open the project \textit{jbt.tools.bteditor}, and then open the file \textit{bteditor.product} with the Product Configuration Editor. Once it has been opened, go to the ``Overview'' page, and click on ``Launch an Eclipse application''. A window like that of figure \ref{fig:JBTEditor} should show up. \begin{figure} \centering \includegraphics[width=\textwidth]{./Images/JBTEditor.png} \caption{JBT Editor after being opened} \label{fig:JBTEditor} \end{figure} The JBT Editor is a very simple tool, so learning how to use it should not take very long. In order to create a new BT, just click on the ``new BT'' icon or select ``File->New BT''. A new editor should open up showing an empty BT (that is, a tree containing only a single node, the root of the tree. To the right of the window there is a tree-like menu (the \textit{Nodes Navigator}) where the user can select nodes to build the tree. In order to add a node to a tree, just drag it from the Nodes Navigator and drop it onto whatever node of the tree to insert it as a child or sibling of the target node. The root node can have only one child. However, other nodes, such as sequences or selectors may have many of them. Decorators can have only one child, and leaf nodes do not have any children. By using the set of provided standard nodes of the Nodes Navigator, the user can build complex behaviour trees. However, in order to build really useful trees, the user must use the domain-dependent actions and conditions from the game. The JBT Editor lets the user load a MMPM domain file as described in section \ref{sec:DefiningLowLevelActionsAndConditions}. Just click on the ``Load MMPM Domain'' or select ``File->Load MMPM Domain'' and select the file that contains the low level actions and conditions. After doing so, the Nodes Navigator will be added a new entry for the actions and conditions within the domain file, which the user will be able to use when building BTs. For instance, if we load the domain file described in section \ref{sec:DefiningLowLevelActionsAndConditions}, we get the actions and conditions shown in figure \ref{fig:LoadedDomain}. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/LoadedDomain.png} \caption{The Nodes Navigator after loading the domain file} \label{fig:LoadedDomain} \end{figure} The JBT Editor lets the user specify values for the input parameters of nodes. By double clicking on a node that has input parameters, a dialog where the user can specify values for the input parameters. For instance, if the AttackMove action is double clicked, then the dialog of figure \ref{fig:InputParameterDialog} is shown. In the dialog the user can specify a value for the parameter ``target'', whose type is $COORDINATE$. Thus, the text field only supports values such as ``45 62'' or ``10 -3 45''. As we mentioned in section \ref{sec:ImplementingLowLevelActionsAndConditions}, when building a BT, the user can specify whether the input parameters of actions and conditions are retrieved from the context or an actual value is provided at construction time. The ``From context'' check box lets the user specify if the parameter has to be retrieved from the context or not. If the check box is not ticked, then the user must provide a value for the parameter in the text field. However, if the check box is ticked, then what the user does is to specify the name of the context's variable where the value of the parameter will be retrieved from. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/InputParameterDialog.png} \caption{The dialog for editing the input parameters of the AttackMove action} \label{fig:InputParameterDialog} \end{figure} For instance, in figure \ref{fig:ParameterNotFromContext} the user has specified a value (``12 34 4.5'') for the input parameter ``target'' of the AttackMove action. However, in figure \ref{fig:ParameterFromContext} the user has indicated that the value of the input parameter ``target'' will be retrieved from the variable ``TargetVariable'' of the context. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/ParameterNotFromContext.png} \caption{A parameter for which an actual value is provided} \label{fig:ParameterNotFromContext} \end{figure} \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/ParameterFromContext.png} \caption{A parameter whose value will be retrieved from the context} \label{fig:ParameterFromContext} \end{figure} Once the BT has been completed, the user can save it as an XML file. In order to do so, just click on the ``Save'' or ``Save As'' icons (or select ``File->Save'' or ``File->Save As'') and enter a file name. When saving a BT, the JBT Editor checks if the structure of the tree is correct. If not, incorrect nodes are highlighted in red color and an explanation for the error is shown in the \textit{Node Info} view (to the right of the window) when the node is selected. Also note that a name for the BT must be provided. The name of a BT is specified in the root node of the tree. When the root is double clicked, a dialog appears that lets the user assign the tree a name. BTs' names are very important, because it is the way that trees are referenced. In particular, names are essential in terms of reusability. For instance, the \textit{Subtree Lookup} task simulates a particular BT, and the way that the Subtree Lookup knows what BT to simulate is by providing the name of the tree. Let us now design the BT that implements the Terran Marine behaviour that we described in section \ref{sec:DefiningLowLevelActionsAndConditions}. An initial implementation for such tree could be that of figure \ref{fig:InitialTerranMarineBT}. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/InitialTerranMarineBT.png} \caption{Initial tree for the Terran Marine behaviour} \label{fig:InitialTerranMarineBT} \end{figure} The behaviour is pretty simple: the tree is always executing (see the \textit{Repeat} node) a \textit{Dynamic Priority List} (DPL) that is constantly checking three conditions: \begin{itemize} \item If the current unit is in a low danger situation, then the unit is ordered to attack the closest dangerous enemy. This is represented by the first child of the DPL. \item If the current unit is in a high danger situation, then the unit runs away to the closest base. This is represented by the second child of the DPL (the Sequence). \item Finally, if none of the above conditions are met, the unit just ``patrols''. This is represented by the third child of the DPL, the Subtree Lookup node (we will further explain this later). \end{itemize} There are some details that must still be defined though. So far, the tree does not provide a way of checking the three conditions above. In order to do so, we can make use of guards, since the DPL interacts with children that have guards defined. In order to add a guard to a node, just right click on the node and select ``Edit Guard''. A dialog should appear that lets the user edit the guard of the node. In JBT, guards are represented by BTs. In particular, a guard can be a single node or a complete and correct BT. When the user clicks on ``Add simple guard'', a dialog lets the user select a single leaf node (action, condition or a standard leaf node) to be used as a guard. If the user clicks on the ``Add complex guard'', however, a new editor is opened so the user can create a complete BT to be used as the node's guard. In our example, the guard of the \textit{Attack} action is just the condition \textit{LowDanger}. Therefore, we have to click on the ``Add simple guard'' button and select the ``LowDanger'' task from the list, as shown in figure \ref{fig:LowDangerGuard}. Note that after inserting the guard, a small shield icon will appear on top of the node that has been added a guard. \begin{figure} \centering \includegraphics[width=0.55\textwidth]{./Images/LowDangerGuard.png} \caption{Selecting a guard for the \textit{Attack} node} \label{fig:LowDangerGuard} \end{figure} In the case of the \textit{Sequence} node, its guard is just the condition \textit{HighDanger}, so we add it just the same way. Now we have to define the input parameters of the tasks. First of all there is the \textit{Attack} node. The intended behaviour is for the soldier to attack the closest unit once that the \textit{LowDanger} condition has been triggered. Thankfully, \textit{LowDanger} writes into the context the identifier of the closest entity (as we described in section \ref{sec:DefiningLowLevelActionsAndConditions}), in a variable with name \textit{LowDangerTarget}. Therefore, the input parameter of \textit{Attack} should be as represented in figure \ref{fig:AttackParameter}, since the value of the input parameter of the \textit{Attack} action is present in a variable of the context whose name is \textit{LowDangerTarget}. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/AttackParameter.png} \caption{The input parameter of \textit{Attack}} \label{fig:AttackParameter} \end{figure} With respect to the \textit{Move} action, the idea is that the unit goes to the closest base. Since the position of the closest base has been computed by the \textit{ComputeClosestBasePosition} action and written into the context in a variable with name \textit{ClosestBasePosition}, then the input argument of \textit{Move} must be read from the context and its value must be the variable name \textit{ClosestBasePosition}. Now let us look at the final part of the tree. When none of the danger conditions are met, the marine has to patrol around its current position. Since \textit{patrolling} is a complex task that may be reused in another trees, we decide to put it into another BT and reuse of from the Terran Marine BT. This is accomplished by the Subtree Lookup task, whose input parameter is set to \textit{StandardPatrol}. \textit{StandardPatrol} is the name of the tree that will implement the patrol behaviour. The tree of figure \ref{fig:StandardPatrol} implements the patrol behaviour. Initially, the current position of the unit is computed by the \textit{ComputeCharacterPosition} action. This position is written into the variable \textit{CharacterPosition} of the context, as we mentioned in section \ref{sec:DefiningLowLevelActionsAndConditions}. From then on, the is a forever loop (Repeat node) that constantly computes a random position that is close to the one computed by the \textit{ComputeCharacterPosition} task, and then orders the unit to \textit{AttackMove} to that target position. It is important to note that the tree must have a name (it is set in the root of the tree), in this case \textit{StandardPatrol}, which is the name that was used in the \textit{SubtreeLookup} task of the Terran Marine BT. \begin{figure} \centering \includegraphics[width=0.4\textwidth]{./Images/StandardPatrol.png} \caption{The BT for the Standard Patrol behaviour} \label{fig:StandardPatrol} \end{figure} Note that a name for the Terran Marine BT must be provided, so we will assume that its name is \textit{TerranMarine}. \section{Step 4: Creating a Java Declaration of the BTs} Once our BTs have been defined in the XML format of the JBT Editor, the next steps are quite easy. Actually, there is no more complex work to be done by the user from now on. So far we have provided a definition and implementation of domain dependent low level actions and conditions. We have also defined our BTs and stored them in XML files using the JBT Editor. The next step is to provide a Java implementation of the trees so that JBT can actually run them. This step is automatically performed by the JBT Core. The JBT Core has an application, the \textit{jbt.tools.btlibrarygenerator.BTLibraryGenerator.java}, that basically takes the XML definition of some BTs and creates a .java file that contains the implementation of such trees. In JBT, BTs are grouped together in BT libraries. A BT library is just a collection of BTs that can be retrieved by name (actually, the name that is specified for the tree in the JBT Editor). A BT library is implemented by the \textit{IBTLibrary} interface. This interface just represents a set of BTs that can be retrieved by name: \begin{minted}{java} public interface IBTLibrary extends Iterable> { /* Returns the BT of name name, or "null" if not found. */ public ModelTask getBT(String name); } \end{minted} Note that, in JBT, a Model BT (see section \ref{sec:ModelIndependentFromExecution}) is represented by the abstract class \textit{ModelTask}. Thus, when we ask an \textit{IBTLibrary} to give us a BT by its name, it just retrieves the Mode BT that represents the tree, which is represented by a \textit{ModelTask}. What the BTLibraryGenerator does is to automatically create a BT library, that is, a class that implements the \textit{IBTLibrary} interface, that can be used to retrieve the BTs defined in the XML files. In particular, given a set of behaviour trees specified in XML files and the MMPM definition of the low level actions and conditions that are used in the trees, it creates the corresponding Java class. The syntax of this program is as follows: \begin{verbatim} BTLibraryGenerator -c configurationFile [-r relativePath] [-o] \end{verbatim} Where \verb@configurationFile@ is an XML file that contains all the information required to run the application. The syntax of such a file is: \begin{minted}{xml} BTFile1 BTFile2 ... BTFileN MMPMDomainFile1 MMPMDomainFile2 ... MMPMDomainFileN Name of the package where model action classes are placed Name of the package where model condition classes are placed Name of the class that is going to be created Name of the package for the generated BT library Name of the directory where the generated library is going to be stored ... ... \end{minted} The order in which the elements are specified is not relevant. In the file the user can define several BT libraries, each one within the \textit{BTLibrary} element. For each BT library defined in a \textit{BTLibrary} element, the program will produce an output file (class implementing the \textit{IBTLibrary} interface) for the library. The -r option is used to add a path to the beginning of the files listed in the configuration file; as a result, each file is considered to be placed at the path specified in the -r option. The -r option may not be specified, in which case the files are considered to be at the current execution directory. The -o option (standing for overwrite) is either is specified or not. If it is not specified, the generated output files will not overwrite any existing file in the file system, and as a result, a behaviour tree library may not be produced in case there is a file with the same name in the file system. If the option -o is specified, then generated output files will overwrite any file in the file system whose name matches. In the Terran Marine example, we want to create a BT library that contains the two BTs that we created in section \ref{sec:CreatingBTsWithTheJBTEditor}. Let us suppose that the tree defining the Terran Marine behaviour was stored in the file \textit{TerranMarine.xbt}, and that the tree defining the patrol behaviour was stored in the file \textit{StandardPatrol.xbt}. Then, we could use the following configuration file to produce the BT library: \begin{minted}{xml} TerranMarine.xbt StandardPatrol.xbt TerranMarineBTLibrary bts.btlibrary src/bts/btlibrary TerranMarineDomain.xml bts.actions bts.conditions \end{minted} Where note that \textit{TerranMarineDomain.xml} is the domain file that we created in section \ref{sec:DefiningLowLevelActionsAndConditions}. The \textit{ModelActionsPackage} and \textit{ModelConditionsPackage} must also be those specified in the configuration file of the ActionsAndConditionsGenerator. So now let us suppose that the configuration file above is stored in a file called \textit{BTConfigurationFile.xml}. The BTLibraryGenerator may be called with the following arguments: \begin{verbatim} BTLibraryGenerator -c BTConfigurationFile.xml -r /home/outputDirectory \end{verbatim} Then, the BTLibraryGenerator will parse the configuration file and it will realize that it has to create just one BT library (the only \textit{BTLibrary} element in the configuration file). It will then parse the XML files of the trees that the library will contain (\textit{TerranMarine.xbt} and \textit{StandardPatrol.xbt}) and finally will create an BT library class named \textit{TerranMarineBTLibrary}, which will be placed in the output directory \textit{home/outputDirectory/stc/bts/btlibrary}. Our purpose here is not to analyse all the details of the produced class, but just point out that it can be used through the public interface that it implements (the \textit{IBTLibrary} mentioned earlier). The class \textit{TerranMarineBTLibrary} will contain both trees, so \begin{minted}{java} getBT("TerranMarine"); \end{minted} will return the tree implementing the Terran Marine behaviour, and \begin{minted}{java} getBT("StandardPatrol"); \end{minted} will return the tree implementing the patrol behaviour. The way we can actually run these trees is explained in section \ref{sec:RunningTheBehaviourTrees}. \section{Step 5: Running the Behaviour Trees}\label{sec:RunningTheBehaviourTrees} So that is almost all. The last step is to run the trees that have been put into one or several BT libraries. The way BTs are run in JBT is really simple, and follows the ideas mentioned in sections \ref{sec:ModelDrivenByTicks}, \ref{sec:ModelIndependentFromExecution} and \ref{sec:Architecture}. Basically, the IBTLibrary is used to retrieve Model BTs. Once a Model BT has been retrieved, a BT Executor must be created to run it. The BT Executor must be fed with an initial context that the BT will use when running\footnote{Remember that the BT that actually runs is the Execution BT, but that detail is of no interest at this point.}. Let's suppose that we have created our \textit{TerranMarineBTLibrary}, and that we want to use it in order to control a particular Terran Marine in the game. First of all, two details must be taken into account. On the one hand, the actions that we implemented (well, we actually implemented only one action, \textit{Move}, but you get the idea) made the assumption that the identifier of the unit running the action was present in the context, under a variable named ``CurrentEntityID'' (you can revisit the implementation in section \ref{sec:ImplementingLowLevelActionsAndConditions}). In general, it may be necessary for the context that is used by the tree to have some initial variables in it. In such case, an appropriate context should be created. On the other hand, the \textit{SubtreeLookup} task poses a big problem when it is run. Remember that the \textit{SubtreeLookup} node simulates the behaviour of a tree given its name. However..., how does it know from where to retrieve a tree given its name? For instance, in the case of the Terran Marine tree, the \textit{SubtreeLookup} is suppose to emulate a tree named \textit{StandardPatrol}, but it does not know where to get such a tree from. The context is used in order to fix this problem. Actually, the context must contain all the trees that are used within the internal execution of the tree. In this case it means that the context that is initially passed to the tree must contain the BT \textit{StandardPatrol}. If it does not, when the Terran Marine tree is run it will throw an exception complaining about not being able to find the corresponding tree. We can see that an appropriate context must be created to run the tree. JBT Core provides a factory class, the \textit{jbt.execution.core.ContextFactory.java} that defines several methods for creating generic contexts (\textit{IContext} objects of the Basic Context type described in section \ref{sec:ExecutionContext}). In our case, we can make use of the method that takes as input an \textit{IBTLibrary} and makes an \textit{IContext} that contains all the BTs of the BT library. Then, we could make use of the methods in the \textit{IContext} interface to add the identifier of the marine that is supposed to be run by the tree: \begin{minted}{java} /* First of all, we create the BT library. */ IBTLibrary btLibrary = new TerranMarineBTLibrary(); /* Then we create the initial context that the tree will use. */ IContext context = ContextFactory.createContext(btLibrary); /* * Now we are assuming that the marine that is going to be * controlled has an id of "terranMarine1" */ context.setVariable("CurrentEntityID","terranMarine1"); \end{minted} The next step is to create the BT Executor to run the tree. In JBT, a BT Executor is represented by the \textit{IBTExecutor} interface. In order to create an \textit{IBTExecutor} object to run a particular BT, the factory class \textit{jbt.execution.core.BTExecutorFactory.java} must be used. The \textit{BTExecutorFactory} has several methods for creating BT Executors; one of them receives as input the Model BT to run and the initial context: \begin{minted}{java} /* Now we get the Model BT to run. */ ModelTask terranMarineTree = btLibrary.getBT("TerranMarine"); /* Then we create the BT Executor to run the tree. */ IBTExecutor btExecutor = BTExecutorFactory.createBTExecutor(terranMarineTree, context); /* And finally we run the tree through the BT Executor. */ do{ btExecutor.tick(); }while(btExecutor.getStatus() == Status.RUNNING); \end{minted} Note that running a BT is a very simple process. The \textit{IBTExecutor} interface defines one main method, \textit{tick()}, which implements the ticking process of a BT. Every time the \textit{tick()} method is called, the BT is given some CPU time to do its work, as was explained in section \ref{sec:ModelDrivenByTicks}. In order to check the current execution status of the tree, the \textit{getStatus()} method is used. As long as the status is \textit{Status.RUNNING}, the tree has not finished so it should continue to receive ticks. \bibliographystyle{plain} \bibliography{UserGuideBib} \end{document} ================================================ FILE: UserGuide/UserGuideBib.bib ================================================ @Book{Millington09, author = {Ian Millington and John Funge}, title = {Artificial Intelligence for Games}, publisher = {Morgan Kaufmann}, year = {2009}, edition = {Second} } @misc{AIGameDev, author = {Alex J. Champandard}, title = {http://aigamedev.com} } @article{QueryEnabledBTs, author = {Fl{\'{o}}rez-Puga, Gonzalo and G{\'{o}}mez-Mart{\'{\i}}n, Marco A. and G{\'{o}}mez-Mart{\'{\i}}n, Pedro P. and D{\'{\i}}az-Agudo, Bel{\'{e}}n and Gonz{\'{a}}lez-Calero, Pedro A.}, editor = {Lucas, Simon M.}, month = nov, title = {Query Enabled Behaviour Trees}, journal = {IEEE Transactions On Computational Intelligence And AI In Games}, volume = {1}, number = {4}, year = {2009}, pages = {298-308}, issn = {1943-068X}, url = {http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=5325892}, doi = {10.1109/TCIAIG.2009.2036369} } ================================================ FILE: UserGuide/userguide.kilepr ================================================ [General] def_graphic_ext=eps img_extIsRegExp=false img_extensions=.eps .jpg .jpeg .png .pdf .ps .fig .gif kileprversion=2 kileversion=2.0.85 lastDocument=UserGuide.tex masterDocument= name=UserGuide pkg_extIsRegExp=false pkg_extensions=.cls .sty .bbx .cbx .lbx src_extIsRegExp=false src_extensions=.tex .ltx .latex .dtx .ins [Tools] MakeIndex= QuickBuild= [document-settings,item:UserGuide.tex] Bookmarks= Encoding=UTF-8 Highlighting=LaTeX Indentation Mode= Mode=LaTeX ReadWrite=true [item:UserGuide.tex] archive=true column=73 encoding=UTF-8 highlight=LaTeX line=512 mode=LaTeX open=true order=0 [item:userguide.kilepr] archive=true column=4 encoding= highlight= line=0 mode= open=false order=-1 [view-settings,view=0,item:UserGuide.tex] CursorColumn=73 CursorLine=512