├── Core ├── BotActions.cs ├── BotControl.cs ├── BotPerception.cs └── IBotDeliberator.cs ├── LICENSE ├── README.md └── SideComponents ├── ArgsList.cs ├── SmartObjects.cs └── StateBook.cs /Core/BotActions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// This class is an action interface for the BotControl. 8 | /// 9 | /// 10 | /// The action must be registerd in this class through the RegisterNewAction 11 | /// function. 12 | /// 13 | /// These actions can be invoked by BotControl. 14 | /// 15 | /// \author Davide Aversa 16 | /// \version 2.0 17 | /// \date 2013 18 | /// \pre This class needs an instance BotControl. 19 | [RequireComponent(typeof(BotControl))] 20 | public class BotActions : MonoBehaviour 21 | { 22 | 23 | /// 24 | /// Dictionary that map commands strings to actions. 25 | /// 26 | private Dictionary> actions; 27 | 28 | /// 29 | /// The abort action. 30 | /// 31 | private Action abortAction; 32 | 33 | /// 34 | /// True if the last action is completed. 35 | /// 36 | private bool actionComplete = true; 37 | 38 | /// 39 | /// True if the last action is completed successfully. 40 | /// 41 | private bool actionSuccess = true; 42 | 43 | /// 44 | /// A reference to a BotControl instance. 45 | /// 46 | private BotControl parentControl; 47 | 48 | void Awake () 49 | { 50 | parentControl = gameObject.GetComponent (); 51 | actions = new Dictionary> (); 52 | } 53 | 54 | /// 55 | /// Registers an action to the BotActions component. 56 | /// 57 | /// 58 | /// This operation is needed in order to execute the desired action. 59 | /// 60 | /// 61 | /// The command associated to the action. 62 | /// 63 | /// 64 | /// A function that execute the desired action. 65 | /// 66 | public void RegisterNewAction (string command, Action action) 67 | { 68 | Debug.Log ("BotACTIONS: Registering " + command + " command"); 69 | actions [command] = action; 70 | } 71 | 72 | /// 73 | /// Register the action that has to be executed to block every other action 74 | /// in progress. 75 | /// 76 | /// 77 | /// The "stop" action function. 78 | /// 79 | public void RegisterAbortAction (Action abortAction) 80 | { 81 | this.abortAction = abortAction; 82 | } 83 | 84 | /// 85 | /// Perform the given action (if exists). 86 | /// 87 | /// 88 | /// True if the action can be executed. False otherwise. 89 | /// 90 | /// 91 | /// The action that must be executed. 92 | /// 93 | public bool DoAction (string fullCommand) 94 | { 95 | Debug.Log ("Action Received: " + fullCommand); 96 | string[] commands = fullCommand.Split (' '); 97 | if (fullCommand == "stop") { 98 | abortAction.Invoke (); 99 | return true; 100 | } 101 | if (actionComplete && actions.ContainsKey (commands [0])) { 102 | actionComplete = false; 103 | actionSuccess = false; 104 | actions [commands [0]].Invoke (commands); 105 | } 106 | return false; 107 | } 108 | 109 | /// 110 | /// Check if the lasts the action is complete. 111 | /// 112 | /// 113 | /// True if the last action is completed. False otherwise. 114 | /// 115 | public bool LastActionComplete () 116 | { 117 | return actionComplete; 118 | } 119 | 120 | /// 121 | /// Check if the last action is completed succesfully. 122 | /// 123 | /// 124 | /// True if the last action is completed succesfully. False otherwise. 125 | /// 126 | public bool LastActionCompletedSuccessfully () 127 | { 128 | return actionComplete && actionSuccess; 129 | } 130 | 131 | /// 132 | /// Notify to BotAction that the action is complete. 133 | /// 134 | public void NotifyActionComplete (string action) 135 | { 136 | actionComplete = true; 137 | NotifyAction(action); 138 | } 139 | 140 | /// 141 | /// Notify to BotAction that the action is completed succesfully. 142 | /// 143 | public void NotifyActionSuccess () 144 | { 145 | actionSuccess = true; 146 | } 147 | 148 | /// 149 | /// Notify the action to the BotController. 150 | /// 151 | /// 152 | /// The name of action succesfully completed. 153 | /// ( 154 | private void NotifyAction (string action) 155 | { 156 | parentControl.NotifyAction (action); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /Core/BotControl.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | /** 7 | * The main brain of a Bot. 8 | * 9 | * The Bot Controller class is the core of the Bot AI. It is the nexus between all the AI elements 10 | * like perception, action and planning/behavior components. 11 | * 12 | * \author Davide Aversa 13 | * \version 1.0 14 | * \date 2013 15 | */ 16 | public class BotControl : MonoBehaviour 17 | { 18 | 19 | // CONTROL INSPECTOR PARAMETERS 20 | public bool onDemand = false; //If true the think loop must be executed on demand. 21 | public float thinkTick = 1; //Time interval between a think cicle. 22 | public string deliberatorName; //Name of the IBotDeliberator implementation. 23 | 24 | private BotActions botActions; //Reference to the BotAction component. 25 | private IBotDeliberator deliberator; //Reference to a IBotDeliberator interface. 26 | 27 | private List objectInFov; // Contains the list of object in the FOV. 28 | 29 | // STATE 30 | private enum Status { IDLE, EXECUTING }; 31 | private Status controlStatus; // Controller Status. 32 | 33 | //public StateBook internalKnowledge; 34 | 35 | // Use this for initialization 36 | protected void Awake() 37 | { 38 | controlStatus = Status.IDLE; 39 | objectInFov = new List(); 40 | botActions = gameObject.GetComponent(); 41 | deliberator = gameObject.GetComponent(deliberatorName) as IBotDeliberator; 42 | // Run Thread Function Every `n` second 43 | if (!onDemand) { 44 | InvokeRepeating("ThinkLoop", 0, thinkTick); 45 | } 46 | } 47 | 48 | /** 49 | * CheckCondition parse a condition formula and return a single boolean value. 50 | * 51 | * TODO: Define formula syntax. 52 | * 53 | * \param condition The input condition. 54 | * \return The thruth value for the condition formula. 55 | */ 56 | public bool CheckCondition(string condition) { 57 | // PARSE AND 58 | string[] andConditions = condition.Split('&'); 59 | if (andConditions.Length > 1) { 60 | foreach (string c in andConditions) { 61 | if (!CheckCondition(c)) return false; 62 | } 63 | return true; 64 | } 65 | // PARSE OR 66 | string[] orConditions = condition.Split('|'); 67 | if (orConditions.Length > 1) { 68 | foreach (string c in orConditions) { 69 | if (CheckCondition(c)) return true; 70 | } 71 | return false; 72 | } 73 | // PARSE CONDITION 74 | bool not = condition.StartsWith("!"); 75 | if (not) condition = condition.Substring(1); 76 | switch (condition) { 77 | default : 78 | return false; //TODO: Default true or default false? 79 | } 80 | } 81 | 82 | // TODO: ThinkLoop 83 | public void ThinkLoop() { 84 | if (controlStatus == Status.IDLE) { 85 | string nextaction = deliberator.GetNextAction(); 86 | Debug.Log("NEXT ACTION: " + nextaction); 87 | if (nextaction != "") 88 | { 89 | controlStatus = Status.EXECUTING; 90 | botActions.DoAction(nextaction); 91 | } 92 | } 93 | } 94 | 95 | public void DoAction(string command) 96 | { 97 | botActions.DoAction(command); 98 | } 99 | 100 | /** 101 | * Used by BotAction to notify the controller about the success of the given action. 102 | * 103 | * \param action The action notification string (TODO: to be defined). 104 | */ 105 | public void NotifyAction(string action) { 106 | controlStatus = Status.IDLE; 107 | switch (action) 108 | { 109 | case "grab": 110 | Debug.Log("Grab Completed"); 111 | break; 112 | default: 113 | break; 114 | } 115 | } 116 | 117 | /** 118 | * Notification callback for an object in the FOV which are changing its state. 119 | * 120 | * \param The changing object. 121 | */ 122 | public void NotifyObjectChange(GameObject obj) 123 | { 124 | if (System.Array.IndexOf(deliberator.interestType,obj.tag) != -1) 125 | { 126 | deliberator.NotifyObjectChange(obj); 127 | } 128 | } 129 | 130 | public void NotifyObjectChange(GameObject obj, bool isLeaving) 131 | { 132 | if (System.Array.IndexOf(deliberator.interestType,obj.tag) != -1) 133 | { 134 | deliberator.NotifyObjectChange(obj, isLeaving); 135 | } 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /Core/BotPerception.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// Implement a perception by collision system for the attached object. 7 | /// 8 | /// \author Davide Aversa 9 | /// \version 1.0 10 | /// \date 2013 11 | /// \pre This component must be attached to a *perception mesh* attached to the bot. 12 | /// The bot must have a BotControl instance attached to itself. 13 | [RequireComponent (typeof(Collider))] 14 | public class BotPerception : MonoBehaviour 15 | { 16 | 17 | /// 18 | /// Eanble the built-in deep test for the entering object. 19 | /// 20 | public bool raycastTest = true; 21 | 22 | public string interestType; 23 | 24 | /// 25 | /// A reference to the IBotControl instance attache to the bot. 26 | /// ( 27 | private BotControl parentControl; 28 | 29 | /// 30 | /// List of the object inside the perception mesh. 31 | /// 32 | private List objectInMesh; 33 | 34 | protected void Awake () 35 | { 36 | // Extract the controller component from the parent object. 37 | parentControl = gameObject.transform.parent.gameObject.GetComponent (); 38 | objectInMesh = new List (); 39 | } 40 | 41 | // Update is called once per frame 42 | // TODO: Remove. 43 | void Update () 44 | { 45 | 46 | } 47 | 48 | /// 49 | /// Raises the trigger enter event. 50 | /// 51 | /// 52 | /// The object which is entering the perception mesh. 53 | /// 54 | void OnTriggerEnter (Collider other) 55 | { 56 | GameObject obj = other.gameObject; // Reference to the entering object. 57 | if (interestType != "" && obj.tag != interestType) return; 58 | if (raycastTest) { 59 | GameObject bot = gameObject.transform.parent.gameObject; // Reference to the bot object. 60 | if (!RayCastVisibility (obj, bot)) { 61 | return; 62 | } 63 | } 64 | SmartObjects so = obj.GetComponent (); 65 | if (so != null) { 66 | so.AddObserver (this); 67 | } 68 | // Add to the object list. 69 | objectInMesh.Add (obj); 70 | // Notify ingress to the controller. 71 | parentControl.NotifyObjectChange(obj); 72 | } 73 | 74 | /// 75 | /// Raises the trigger exit event. 76 | /// 77 | /// 78 | /// The object which is leaving the perception mesh. 79 | /// 80 | void OnTriggerExit (Collider other) 81 | { 82 | GameObject obj = other.gameObject; 83 | SmartObjects so = obj.GetComponent (); 84 | if (so != null) { 85 | so.RemoveObserver (this); 86 | } 87 | // Add to the object list. 88 | objectInMesh.Remove (obj); 89 | // Notify ingress to the controller. 90 | parentControl.NotifyObjectChange(obj,true); 91 | } 92 | 93 | /// 94 | /// Notifies the object change. 95 | /// 96 | /// 97 | /// The changing game object. 98 | /// 99 | /// 100 | /// The object type. 101 | /// 102 | public void NotifyObjectChange (GameObject go, string type) 103 | { 104 | parentControl.NotifyObjectChange(go); 105 | } 106 | 107 | /// 108 | /// Check for raycast visibility. 109 | /// 110 | /// 111 | /// True if the object is visible from the bot. False otherwise. 112 | /// 113 | /// 114 | /// The target object. 115 | /// 116 | /// 117 | /// The bot object. 118 | /// 119 | private bool RayCastVisibility (GameObject obj, GameObject bot) 120 | { 121 | RaycastHit hit = new RaycastHit (); 122 | Vector3 offset = new Vector3 (0, 1, 0); 123 | // Direction between obj and other. 124 | Vector3 direction = (obj.transform.position - (bot.transform.position + offset)).normalized; 125 | Physics.Raycast (bot.transform.position + offset, direction, out hit); 126 | return hit.transform.gameObject.Equals (obj); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /Core/IBotDeliberator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | /// 5 | /// A public interface for the bot higher level. 6 | /// 7 | /// 8 | /// IBotDeliberator offers a communication interface between BotControl 9 | /// and the higher decisional level. 10 | /// 11 | /// \author Davide Aversa 12 | /// \version 1.0 13 | /// \date 2013 14 | public interface IBotDeliberator { 15 | 16 | /// 17 | /// Return the next bot action. 18 | /// 19 | /// 20 | /// The action is computed by the deliberator according to the information 21 | /// available in BotController. 22 | /// 23 | /// 24 | /// The next valid (?) bot action. 25 | /// 26 | string GetNextAction(); 27 | 28 | /// 29 | /// Notifies the object change. 30 | /// 31 | /// 32 | /// The changing object. 33 | /// 34 | /// 35 | /// The object type. 36 | /// 37 | /// 38 | /// Set this to true if the notification comes from a leaving object. 39 | /// 40 | void NotifyObjectChange(GameObject obj, bool isLeaving = false); 41 | 42 | /// 43 | /// List of the interesting object types for the deliberator. 44 | /// 45 | /// 46 | /// The type of the interest for the deliberator. 47 | /// 48 | string[] interestType { get; } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Davide Aversa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-CogBot 2 | 3 | ## What is? 4 | 5 | **Unity-CogBot** is a free implementation of the CogBot NPC architecture for the popular 3D game engine [Unity3D][1]. 6 | 7 | CogBot is a modular architecture for NPC development that ensures *modularity* and *reusability* of the code. 8 | 9 | It is based on the standard cognitive AI agents components: **perception**, **control**, **deliberation** and **action**. 10 | 11 | [1]: http://unity3d.com/ -------------------------------------------------------------------------------- /SideComponents/ArgsList.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | /*! 5 | * ArgsList stores an ordered list of arguments in an efficient way. 6 | */ 7 | public class ArgsList { 8 | 9 | private string[] args; 10 | 11 | // INDEXER. See http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx for 12 | // more information. 13 | /*! 14 | * Indexer for the ArgsList class. 15 | */ 16 | public string this[int i] 17 | { 18 | get 19 | { 20 | return args[i]; 21 | } 22 | 23 | set 24 | { 25 | throw new System.InvalidOperationException("ArgsList are immutable!"); 26 | } 27 | } 28 | 29 | /*! 30 | * Return the number of arguments in the list. 31 | */ 32 | public int Length 33 | { 34 | get { return args.Length; } 35 | } 36 | 37 | /*! 38 | * Constructor. 39 | * 40 | * Build an ArgsList from a list of strings. 41 | */ 42 | public ArgsList(params string[] args) 43 | { 44 | this.args = args; 45 | } 46 | 47 | /*! 48 | * Constructor. 49 | * 50 | * Build an ArgsList from a single string. 51 | * 52 | * @param args The input string. 53 | * @param separator The separator character between the arguments. 54 | */ 55 | public ArgsList(string args, char separator = ' ') 56 | { 57 | this.args = args.Split(separator); 58 | } 59 | 60 | public string[] ToStringArray() 61 | { 62 | return args.Clone() as string[]; 63 | } 64 | 65 | public override bool Equals(object obj) 66 | { 67 | // If parameter is null return false. 68 | if (obj == null) 69 | { 70 | return false; 71 | } 72 | 73 | // If parameter cannot be cast to ArgsList return false. 74 | ArgsList otherList = obj as ArgsList; 75 | if ((System.Object)otherList == null) 76 | { 77 | return false; 78 | } 79 | 80 | // Return true if other have the same elements in the same order. 81 | if (otherList.Length != this.Length) return false; 82 | for (int i = 0; i < this.Length; ++i) 83 | { 84 | if (otherList[i] != this[i]) return false; 85 | } 86 | return true; 87 | } 88 | 89 | public override int GetHashCode() 90 | { 91 | int prime = 31; 92 | int result = 1; 93 | foreach (string s in args) 94 | { 95 | result = prime * result + s.GetHashCode(); 96 | } 97 | return result; 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /SideComponents/SmartObjects.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// A Smart Object component is used to handle stateful objects in the BotPerception 7 | /// component. 8 | /// 9 | /// \author Davide Aversa 10 | /// \version 2.0 11 | /// \date 2013 12 | public class SmartObjects : MonoBehaviour { 13 | 14 | /// 15 | /// The list of observers. 16 | /// 17 | private List observers; 18 | 19 | // Use this for initialization 20 | void Awake () { 21 | observers = new List(); 22 | } 23 | 24 | /// 25 | /// Add an observer to the object. 26 | /// 27 | /// 28 | /// The observer perception component. 29 | /// 30 | public void AddObserver(BotPerception obs) 31 | { 32 | observers.Add(obs); 33 | } 34 | 35 | /// 36 | /// Remove an observer from the object. 37 | /// 38 | /// 39 | /// The observer perception component. 40 | /// 41 | public void RemoveObserver(BotPerception obs) 42 | { 43 | observers.Remove(obs); 44 | } 45 | 46 | /// 47 | /// Notifies the state change. 48 | /// 49 | public void NotifyStateChange() 50 | { 51 | foreach (BotPerception bp in observers) 52 | { 53 | bp.NotifyObjectChange(gameObject, gameObject.tag); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SideComponents/StateBook.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | /*! 7 | * StateBook stores informations about predicative conditions. 8 | * 9 | * For example StateBook can store predicates like `Location(2,3,bot1)` or 10 | * `Hold(gun)` usefull in deliberative algorithm and tecniques like planning 11 | * or FSM. 12 | * 13 | * Statebook class is easy to use. To add a conditions you just have to 14 | * 15 | * statebook["name:arg1 arg2 arg3"] = true; 16 | * 17 | * In the same way we can check the validity of a given conditions with 18 | * 19 | * statebook["name:arg1 arg2 arg3"]; 20 | * 21 | * Statebook use the Closed-World Assumption. 22 | */ 23 | public class StateBook : MonoBehaviour { 24 | 25 | private Dictionary> conditionsDB; 26 | private Dictionary predicatesArity; 27 | 28 | // Use this for initialization 29 | void Start () { 30 | conditionsDB = new Dictionary>(); 31 | predicatesArity = new Dictionary(); 32 | 33 | /* TEST */ 34 | this["bob:1 2 cane"] = true; 35 | this["bob:1 3 pollo"] = true; 36 | this["bob:3 5 cane"] = true; 37 | foreach (string[] ss in this.GetEnumerator("bob", "$1 $2 cane")) 38 | { 39 | Debug.Log(ss[0] + " " + ss[1]); 40 | } 41 | } 42 | 43 | // Update is called once per frame 44 | void Update () { 45 | 46 | } 47 | 48 | /*! 49 | * Query the database for the validity of a given expression. 50 | * 51 | * \param name The predicate name. 52 | * \param args The list of arguments of the predicate. 53 | * \retval true If the query is valid in the DB. 54 | * \retvale false Otherwise. 55 | */ 56 | public bool Query(string name, string[] args) 57 | { 58 | if (!conditionsDB.ContainsKey(name)) return false; 59 | return conditionsDB[name].Contains(new ArgsList(args)); 60 | } 61 | 62 | /*! 63 | * Query the database for the validity of a given expression. 64 | * 65 | * \param name The predicate name. 66 | * \param args The list of arguments of the predicate separated by spaces.7 67 | * \retval true If the query is valid in the DB. 68 | * \retvale false Otherwise. 69 | */ 70 | public bool Query(string name, string args) 71 | { 72 | if (!conditionsDB.ContainsKey(name)) return false; 73 | return conditionsDB[name].Contains(new ArgsList(args)); 74 | } 75 | 76 | /*! 77 | * Query the database for the validity of a given expression. 78 | * 79 | * \param query The query string. The format is "name:arg1 arg2 ...". 80 | * \retval true If the query is valid in the DB. 81 | * \retvale false Otherwise. 82 | */ 83 | public bool Query(string query) 84 | { 85 | string[] splitted = query.Split(':'); 86 | return this.Query(splitted[0], splitted[1]); 87 | } 88 | 89 | /*! 90 | * Set the given query with the given value. 91 | * 92 | * \param query The input query string. 93 | * \param value The desired value. 94 | */ 95 | public void SetValue(string query, bool value) 96 | { 97 | Debug.Log(query); 98 | string[] splitted = query.Split(':'); 99 | string theName = splitted[0]; 100 | string args = splitted[1]; 101 | int argsNum = args.Split(' ').Length; 102 | // If true add the arglist to the list. If false remove it. 103 | if (value == true) 104 | { 105 | // If the condition do not exist, create it. 106 | if (!conditionsDB.ContainsKey(theName)) 107 | { 108 | conditionsDB.Add(theName, new HashSet()); 109 | predicatesArity.Add(theName, argsNum); 110 | } 111 | // You cannot add an arglist with different number of arguments for 112 | // an existent condition. 113 | if (argsNum != predicatesArity[theName]) 114 | { 115 | throw new System.InvalidOperationException("Invalid arguments number!"); 116 | } 117 | conditionsDB[theName].Add(new ArgsList(args)); 118 | } 119 | else 120 | { 121 | // If you set a false condition you are removing a positive condition 122 | // in the DB (if any). 123 | if (!conditionsDB.ContainsKey(theName)) 124 | return; 125 | conditionsDB[theName].Remove(new ArgsList(args)); 126 | } 127 | } 128 | 129 | // ENUMERATORS 130 | /*! 131 | * Enumerator for the properties name in the database. 132 | */ 133 | public System.Collections.IEnumerable GetConditionsEnumerator() 134 | { 135 | foreach (KeyValuePair> entry in conditionsDB) 136 | { 137 | yield return entry.Key; 138 | } 139 | } 140 | 141 | /*! 142 | * Enumerator for a given property. 143 | * 144 | * This enumerator return all the argument tuple associated to a given 145 | * condition. 146 | * 147 | * /param name The condition name. 148 | */ 149 | public System.Collections.IEnumerable GetEnumerator(string name) 150 | { 151 | foreach (ArgsList al in conditionsDB[name]) 152 | { 153 | yield return al.ToStringArray(); 154 | } 155 | } 156 | 157 | /* 158 | * Enumerator for selective query. 159 | * 160 | * This enumerator return all the argument tuple associated to a given 161 | * condition that match the given template. 162 | * 163 | * A template is a string of the form "$var1 fixed1 fixed2 $var2 ..." where 164 | * the element starting with the $ symbol are variable and the fixed 165 | * ones must match the database argument. 166 | * 167 | * \param name The property name. 168 | * \param template The template string. * 169 | */ 170 | public System.Collections.IEnumerable GetEnumerator(string name, string template) 171 | { 172 | if (conditionsDB.ContainsKey(name)) 173 | { 174 | int unknow = 0; 175 | string[] splitted = template.Split(' '); 176 | foreach (string s in splitted) 177 | { 178 | if (s.StartsWith("$")) 179 | { 180 | unknow++; 181 | } 182 | } 183 | foreach (ArgsList al in conditionsDB[name]) 184 | { 185 | string[] result = new string[unknow]; 186 | int idx = 0; 187 | bool iPickThis = true; 188 | for (int i = 0; i < al.Length; ++i) 189 | { 190 | if (splitted[i].StartsWith("$")) 191 | { 192 | result[idx] = al[i]; 193 | idx++; 194 | } 195 | else 196 | { 197 | // If one item is 198 | iPickThis = iPickThis && (splitted[i] == al[i]); 199 | if (!iPickThis) break; 200 | } 201 | } 202 | if (iPickThis) yield return result; 203 | } 204 | } 205 | } 206 | 207 | // INDEXERS 208 | // Query string is in the form: "condition_name:arg1 arg2 arg3 ..." 209 | public bool this[string query] 210 | { 211 | get 212 | { 213 | return Query(query); 214 | } 215 | 216 | set 217 | { 218 | SetValue(query, value); 219 | } 220 | } 221 | } 222 | --------------------------------------------------------------------------------