Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / Win32

Fuzzy Ontology Framework

Rate me:
Please Sign up or sign in to vote.
4.92/5 (12 votes)
17 Mar 2012CPOL19 min read 66.8K   7.4K   37  
Integration of fuzzy OWL ontology modelling with .NET
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;
using System.Threading;
using System.ComponentModel;
using FuzzyFramework.Dimensions;
using FuzzyFramework.Sets;

namespace OntologyIntegration
{
    /// <summary>
    /// Main class in the OntologyIntegration assemly. Provides communication between business layer of an information system
    /// written in .NET on one side, and ontology stored in the RDF/XML format on the other side.
    /// </summary>
    public class OntologyIntegrator
    {

        #region private members
        
        /// <summary>
        /// Collection of all distinct classes identified when processing the UpdateIndividual method.
        /// </summary>
        private List<IndividualClass> _individualClasses = new List<IndividualClass>();
        
        /// <summary>
        /// Collection of all concepts found in the ontology within the UpdateOntology method.
        /// </summary>
        private List<Concept> _concepts = new List<Concept>(0);

        /// <summary>
        /// Collection of all individuals registered by the UpdateIndividual method.
        /// </summary>
        private Dictionary<string,Individual> _individuals = new Dictionary<string,Individual>(0);

        /// <summary>
        /// Queue of queries to execute
        /// </summary>
        //private Queue<Query> _queries = new Queue<Query>();

        ///Specifies that the ontology definition is invalid and has to be re-generated.
        private bool _ontologyDefinitionInvalid = true;
        private bool _ontologyIndividualsInvalid = false;
        private bool _reasonerIndividualsInvalid = false;

        protected string _ontologyDefinition;

       

        #endregion


        #region private methods

        protected static bool getBooleanFromConfigSetting(string key)
        {
            return ConfigurationManager.AppSettings[key].Equals("1") || ConfigurationManager.AppSettings[key].ToLower().Equals("true");
        }


        /// <summary>
        /// Executes qeuries specified in the input collection,
        /// setting the Result property for each member of the collection
        /// This method also automatically takes care of validity of cached ontology defnition, individual's definitions etc.
        /// </summary>
        /// <param name="queries">collection of queries to execute</param>
        private void ExecuteQueries (IEnumerable<Query> queries)
        {
            //if all queries in the collection of type ConceptIsaConcept, then only Check if ontology valid
            //else
            //Check if both ontology and all individuals valid

            bool _conceptIsaConceptOnly = true;
            foreach(Query query in queries)
            {
                if ( ! (query is SubsumptionQuery))
                {
                    _conceptIsaConceptOnly = false;
                    break;
                }
            }

            //individuals invalid -> for the invalid individuals, load the txt definition
            //i.e. invoke IReasonerCommunicator.UpdateReasonerIndividualDefinition

            if (!_conceptIsaConceptOnly)
            {
                if (this._reasonerIndividualsInvalid)
                {
                    foreach (Individual individual in this.Individuals)
                    {
                        if (individual.IsInvalid && !individual.Class.StoreInOntology)
                        {
                            //The definition gets actually cached in the Indiviual object
                            this._reasonerCommunicator.UpdateReasonerIndividualDefinition(individual);
                        }
                    }
                    this._reasonerIndividualsInvalid = false;
                }

                if (this._ontologyIndividualsInvalid)
                {
                    bool _changed = this.storeIndividualsInOntology();
                    if (_changed)
                        _ontologyDefinitionInvalid = true;
                    this._ontologyIndividualsInvalid = false;
                }
            }
            
            //ontology invalid -> generate new txt file from ontology via command line
            //i.e. invoke IReasonerCommunicator.GetReasonerOntologyDefinition

            if (this._ontologyDefinitionInvalid)
            {
                this._ontologyDefinition = _reasonerCommunicator.GetReasonerOntologyDefinition(this);
                this._ontologyDefinitionInvalid = false;
            }

            //Build the overall ontology for reasoning
            System.Text.StringBuilder sb = new StringBuilder( this._ontologyDefinition );
            sb.AppendLine();
            
            foreach(Individual ind in _individuals.Values)
                if (!ind.Class.StoreInOntology)
                    sb.AppendLine(ind.ReasonerDefinition);

            //and finally, execute the queries
            _reasonerCommunicator.ExecuteQueries(sb.ToString(), queries);
        }


        /// <summary>
        /// if necessary, store individuals previously specified by the UpdateIndividual method as to be stored to the ontology
        /// </summary>
        protected bool storeIndividualsInOntology()
        {
            return _ontologyCommunicator.StoreIndividualsInOntology(this);
        }

        /// <summary>
        /// To be invoked if someone wants to use the following properties:
        /// - :Individual.Concepts
        /// - :Concept.Members
        /// The method updates the appropriate collections if necessary
        /// </summary>
        internal void RefreshMembershipsIfNecessary()
        {
            AsyncOperation async = AsyncOperationManager.CreateOperation(null);
            
            
            if (this._ontologyIndividualsInvalid || this._reasonerIndividualsInvalid)
            {
                //Set the indices of the individuals in the INDIVIDUALS dimension.
                decimal index = 0;
                foreach (KeyValuePair<string, Individual> item in _individuals)
                    item.Value.ToDecimal = index++;
            }

            if (this._ontologyIndividualsInvalid || this._reasonerIndividualsInvalid || this._ontologyDefinitionInvalid)
            {
                List<InstanceQuery> queries = new List<InstanceQuery>( _individuals.Count * _concepts.Count);
                
                //Prepare queries
                bool firstRound = true;
                foreach (KeyValuePair<string, Individual> indItem in _individuals)
                {
                    indItem.Value.Concepts = new DiscreteSet(CONCEPTS, String.Format("Concepts of individual {0}", indItem.Value.Name));
                    
                    foreach (Concept concept in _concepts)
                    {
                        if (firstRound)
                            concept.Members = new DiscreteSet(INDIVIDUALS, String.Format("Members of concept {0}", concept.Name));

                        
                        queries.Add( new InstanceQuery(indItem.Value, concept));
                    }

                    firstRound = false;
                }

                //Execute queries
                this.ExecuteQueries(queries);

                //Process the results
                foreach(InstanceQuery query in queries)
                {
                    if (query.Result > 0)   //We will only insert members to the sets if the degree of membership is > 0
                    {
                        ((DiscreteSet)query.individual.Concepts).AddMember(query.concept, query.Result);
                        ((DiscreteSet)query.concept.Members).AddMember(query.individual, query.Result);
                    }

                }

            }

            async.OperationCompleted();
        }

        #endregion

        #region Progress Reporting
        public event EventHandler<UpdateOntologyProgressChangedEventArgs> UpdateOntologyProgressChanged;

        protected virtual void OnUpdateOntologyProgressChanged(UpdateOntologyProgressChangedEventArgs e)
        {
            if (UpdateOntologyProgressChanged != null)
                UpdateOntologyProgressChanged(this, e);
        }

        protected UpdateOntologyProgressChangedEventArgs _updateOntProgressEventArgs;

        object objUOLock = new object();

        internal void UOProgressChanged(int currentOperationIndex, int totalOperationCount, string currentOperationDesc)
        {
            _updateOntProgressEventArgs = new UpdateOntologyProgressChangedEventArgs(
                currentOperationIndex,
                totalOperationCount,
                currentOperationDesc
            );

            UpdateOntologyProgressChanged(this, _updateOntProgressEventArgs);
        }


        internal void UOProgressChanged(string currentOperationDesc)
        {
            _updateOntProgressEventArgs = new UpdateOntologyProgressChangedEventArgs(
                _updateOntProgressEventArgs.CurrentOperationIndex + 1,
                _updateOntProgressEventArgs.TotalOperationCount,
                currentOperationDesc
            );

            UpdateOntologyProgressChanged(this, _updateOntProgressEventArgs);

        }

   
        #endregion

        #region implementation of communicator interfaces

        /// <summary>
        /// Concrete class to communicate with ontology
        /// </summary>
        private IOntologyCommunicator _ontologyCommunicator = new RdfXmlOntologyCommunicator();


        private IReasonerCommunicator _reasonerCommunicator = new FuzzyDLCommunicator();

        #endregion

        #region Configuration settings are to be specified before any further work with OntologyInterface. Good way is to store them in the App.ini file.

        private Nullable<bool> _backupOntologyWhenModified;

        /// <summary>
        /// If entities polulated directly to the ontology (see the StoreInOntology attribute), specifies whether the source ontology file should be archived each time it has been changed by OntologyInterface. Default value is false.
        /// It is possible to specify the value in the App.config file, in the appSettings section, using key "BackupOntologyWhenModified"
        /// </summary>
        public bool BackupOntologyWhenModified
        {
            get 
            { 
                if (! _backupOntologyWhenModified.HasValue && ConfigurationManager.AppSettings.AllKeys.Contains( "BackupOntologyWhenModified" ))
                    _backupOntologyWhenModified = getBooleanFromConfigSetting("BackupOntologyWhenModified");

                if (! _backupOntologyWhenModified.HasValue )
                    BackupOntologyWhenModified = false;

                return _backupOntologyWhenModified.Value; 
            }
            set { _backupOntologyWhenModified = value; }
        }


        
        private string _pathToOntology = null;


        /// <summary>
        /// Full path to the ontology file. Example is C:\OntologyStore\Employees.owl.
        /// It is possible to specify the value in the App.config file, in the appSettings section, using key "PathToOntology"
        /// </summary>
        public string PathToOntology
        {
            get {
                if (_pathToOntology == null && ConfigurationManager.AppSettings.AllKeys.Contains("PathToOntology"))
                    _pathToOntology = ConfigurationManager.AppSettings["PathToOntology"];

                if (_pathToOntology == null)
                    throw new OntologyIntegratorException("The PathToOntology setting specified neither in code nor in the App.settings file.");
                
                return _pathToOntology; 
            
            }
 
            set { _pathToOntology = value; }
        }

        private string _individualsUri;
        //private string _individualsPrefix;
        protected static readonly string _defaultIri = "http://{0}/individuals.owl";
        //protected static readonly string _defaultPrefix = "fof"; ... put to FuzzyDLCommnunicator
        
        /// <summary>
        /// Specifies what IRI will be used for the individuals generated based on the UpdateIndividual method.
        /// Use key "IndividualsUriBase" to specify the value in the App.config file.
        /// Default value is "http://{machine name}/individuals.owl".
        /// </summary>
        public string IndividualsUriBase
        {
            get 
            {
                if (_individualsUri == null && ConfigurationManager.AppSettings.AllKeys.Contains("IndividualsUriBase"))
                    _individualsUri = ConfigurationManager.AppSettings["IndividualsUriBase"];

                if (_individualsUri == null)
                    _individualsUri = String.Format(_defaultIri, System.Environment.MachineName);

                return _individualsUri; 
            }
            set 
            {
                _individualsUri = value; 
            }
        }

        private Nullable<bool> _ignoreStringAttributes;

        /// <summary>
        /// Some reasoners like FuzzyOWL2 & FuzzyDL don't really support string values.
        /// If true, attributes type string won't be considered in the ontology, including the isClass propery.
        /// </summary>
        public bool IgnoreStringAttributes
        {
            get
            {
                if (!_ignoreStringAttributes.HasValue && ConfigurationManager.AppSettings.AllKeys.Contains("IgnoreStringAttributes"))
                    _ignoreStringAttributes = getBooleanFromConfigSetting("IgnoreStringAttributes");

                if (!_ignoreStringAttributes.HasValue)
                    _ignoreStringAttributes = false;
                return _ignoreStringAttributes.Value;
            }
            set
            {
                _ignoreStringAttributes = value;
            }
        }

        /// <summary>
        /// Namespace alias automatically generated from the IndividualsUriBase
        /// </summary>
        public string IndividualsNamespaceAlias
        {
            get
            {
                string baseStr = IndividualsUriBase;
                if (baseStr.ToLower().EndsWith(".owl"))
                    baseStr = baseStr.Substring(0, baseStr.Length - 4);
                int pos = baseStr.LastIndexOf("/");
                return System.Text.RegularExpressions.Regex.Replace(baseStr.Substring(pos + 1), "[^a-zA-Z]", "");
            }
        }

        private string _ontologyUriBase = null;

        /// <summary>
        /// Gets or sets the ontology uri base. Example is "http://www.mysystem.net/ontologies/MyOntology05.owl"
        /// </summary>
        public string OntologyUriBase
        {
            get
            {
                //todo
                //return "http://www.slavicek.net/ontologies/SalesPersons05.owl";

                if (_ontologyUriBase == null)
                    throw new OntologyIntegratorException("OntologyUriBase not set");

                return _ontologyUriBase;

            }

            set
            {
                _ontologyUriBase = value;
            }
        }

        public string DefaultNamespaceAlias
        {
            get
            {
                string baseStr = OntologyUriBase;
                if (baseStr.ToLower().EndsWith(".owl"))
                    baseStr = baseStr.Substring(0, baseStr.Length - 4);
                int pos = baseStr.LastIndexOf("/");
                return System.Text.RegularExpressions.Regex.Replace(baseStr.Substring(pos + 1), "[^a-zA-Z]", "");
            }
        }

        /*
        /// <summary>
        /// Prefix to use in the ontology definition for reasoner.
        /// The uri base of individuals might be too long or with unaccepted characters, hence this string will be used as a placeholder.
        /// Use key "IndividualsPrefix" to specify the value in the App.config file. Default value is "fof".
        /// </summary>
        public string InidivdualsPrefix
        {
            get
            {
                if (_individualsPrefix == null && ConfigurationManager.AppSettings.AllKeys.Contains("IndividualsPrefix"))
                    _individualsPrefix = ConfigurationManager.AppSettings["IndividualsPrefix"];

                if (_individualsPrefix == null)
                    _individualsPrefix = _defaultPrefix;

                return _individualsPrefix;
            }
            set
            {
                _individualsPrefix = value;
            }
        }
        */

        

        private string _backupFolder = null;


        /// <summary>
        /// Defines the backup folder. Default value is the folder with live ontology.
        /// It is also possible to specify the value in the App.config file, in the appSettings section, using key "BackupFolder"
        /// </summary>
        public string BackupFolder
        {
            get {
                if (_backupFolder == null && ConfigurationManager.AppSettings.AllKeys.Contains("BackupFolder"))
                    _backupFolder = ConfigurationManager.AppSettings["BackupFolder"];

                if (_backupFolder == null && _pathToOntology !=null)
                    _backupFolder = System.IO.Path.GetDirectoryName(_pathToOntology);
                
                if (_backupFolder == null)
                    throw new OntologyIntegratorException("The BackupFolder setting specified neither in code nor in the App.settings file.");
                
                return _backupFolder; 
            
            }
            set { _backupFolder = value; }
        }

        private string _temporaryFolder = null; //Environment.GetFolderPath(Environment.SpecialFolder....);


        /// <summary>
        /// Specifis a temporary folder to store text files when communication with a reasoner, for example. Default value is the folder with live ontology.
        /// It is also possible to specify the value in the App.config file, in the appSettings section, using key "TemporaryFolder"
        /// </summary>
        public string TemporaryFolder
        {
            get
            {
                if (_temporaryFolder == null && ConfigurationManager.AppSettings.AllKeys.Contains("TemporaryFolder"))
                    _temporaryFolder = ConfigurationManager.AppSettings["TemporaryFolder"];

                if (_temporaryFolder == null && _pathToOntology != null)
                    _temporaryFolder = System.IO.Path.GetDirectoryName(_pathToOntology);

                if (_temporaryFolder == null)
                    _temporaryFolder = Path.GetTempPath();
                
                if (_temporaryFolder == null)
                    throw new OntologyIntegratorException("The TemporaryFolder setting specified neither in code nor in the App.settings file.");

                return _temporaryFolder;

            }
            set { _temporaryFolder = value; }
        }


        
        #endregion      
        
        #region constructors

        /// <summary>
        /// Initialize new instance of OntologyIntegrator.
        /// Invoking UpdateOntology is part of this constructor, based on the path specified as the input parameter.
        /// </summary>
        public OntologyIntegrator(string pathToOntology) : base()
        {
            this.PathToOntology = pathToOntology;
            UpdateOntology();
        }

        /// <summary>
        /// Initialize new instance of OntologyIntegrator.
        /// Next steps are to set the settings (if not specified in an App.config file), and to invoke the UpdateOntology method.
        /// </summary>
        public OntologyIntegrator()
        {
        }

        #endregion

        #region public members

        /// <summary>
        /// Dimension which represents concepts found an ontology within the UpdateOntology method.
        /// </summary>
        public static readonly IDiscreteDimension CONCEPTS = new DiscreteDimension("concepts", "concepts found in the ontology");

        /// <summary>
        /// Dimensions which represents individuals being classified by the ontology
        /// </summary>
        public static readonly IDiscreteDimension INDIVIDUALS = new DiscreteDimension("individuals", "individuals classified by the ontology"); 

        /// <summary>
        /// Specifies format of the ontology backup file name. {0} stays for the original file name, {1} for extension "owl".
        /// Date and time format specifiers like yy, yyyy, MM, MMM, etc. are taken from the .NET Custom Date and Time Formatting.
        /// For the original filename of "myontology.owl", the default BACKUP_FORMAT returns a filename similar to "myontology_2012-04-15-22-58-59.018.owl"
        /// </summary>
        public const string BACKUP_FORMAT = "{0}_yyyy-MM-dd-HH-mm-ss.fff.{1}";


        /// <summary>
        /// The method is supposed to be invoked at the beginning of your code, after the OntologyIntegrator constructor. Furthermore, this method should be called each time the source ontology file has been modified from the outside world (i.e. redefined in an ontology editor, for instance).
        /// It performs the following actions:
        /// - Stores individuals which have to be stored, in the source ontololgy
        /// - Loads the ontology
        /// - Puts all concepts found in the ontology into collection Concepts
        /// - Uses the specified reasoner to load the fuzzy relations between concepts (in the asserted hierarchy), in order to build an image of this
        ///   hierarchy here in the C# code(in the Ancestors and Descendants properties of every Concept)
        ///   By other words, it is Cm isa Cn for m, n in 1..count of concepts.
        ///   It is worth to notice that this membership is infered based on the current knowledge about the concept. Should there come other statements later on with the UpdateIndividual methods
        ///   which would influence the membership, the UpdateOtology method has to be invoked once more after updating all the individuals.
        /// </summary>
        /// 
        public void UpdateOntology()
        {
            UpdateOntology(true, true);
        }

        /// <summary>
        /// The method is supposed to be invoked at the beginning of your code, after the OntologyIntegrator constructor. Furthermore, this method should be called each time the source ontology file has been modified from the outside world (i.e. redefined in an ontology editor, for instance).
        /// </summary>
        /// <param name="updateConceptSubsumptions">Specifies whether sets of ancestors and descendants will be defined for each concept</param>
        /// <param name="updateIndividualsMembership">Specifies whether set of members will be created for each concept, and in turn set of concepts for each individual</param>
        /// <param name="asyncResult">Current process of the operation (intended for asynchronous invocation of this method by means of BeginInvoke</param>
        public void UpdateOntology(bool updateConceptSubsumptions, bool updateIndividualsMembership)
        {
            lock (objUOLock)
            {

 
                UOProgressChanged(0, 100, "Updating ontology with individuals");

                _ontologyDefinitionInvalid = true;
                //Check the ontology file exists
                if (!File.Exists(this.PathToOntology))
                    throw new OntologyIntegratorException(String.Format("No ontology file exists on the following location:\n{0}", this.PathToOntology));

                //update ontology with individuals which have to be stored there.
                bool ontologyChanged = storeIndividualsInOntology();
                if (ontologyChanged)
                    this._ontologyDefinitionInvalid = true; //The definition has to be re-generated

                //Load the ontology
                UOProgressChanged(0, 100, "Loading concepts from the ontology");
                //Put all concepts in the ontology into collection Concepts
                _concepts = _ontologyCommunicator.LoadConcepts(this);

                int operCount = 2 +
                 (updateConceptSubsumptions ? Math.Max(_concepts.Count * (_concepts.Count), 0) : 0) +
                 (updateIndividualsMembership ? (_concepts.Count * _individuals.Count) : 0);



                UOProgressChanged(2, operCount,  "Building queries");


                #region Calculate isa cartesian product for all concepts


                Queue<Query> queries = new Queue<Query>();


                if (updateConceptSubsumptions)
                {
                    //Use the specified reasoner to load the fuzzy relations between concepts (in the asserted hierarchy), in order to build an image of this
                    //hierarchy here in the C# code(in the ParentConceps and ChildConcepts properties of every Concept)

                    foreach (Concept concept1 in _concepts)
                    {
                        concept1.Ancestors = new DiscreteSet(CONCEPTS, String.Format("ancestors of concept {0}", concept1.Name));
                        concept1.Descendants = new DiscreteSet(CONCEPTS, String.Format("descendants of concept {0}", concept1.Name));

                        foreach (Concept concept2 in _concepts)
                        {
                            Query query = new SubsumptionQuery(concept1, concept2);
                            queries.Enqueue(query);
                        }
                    }
                }

                #endregion
                #region Calculate membership of each individual for each concept
                if (updateIndividualsMembership)
                {
                    foreach (Concept concept in _concepts)
                    {
                        concept.Members = new DiscreteSet(INDIVIDUALS, String.Format("members of concept {0}", concept.Name));

                        foreach (Individual ind in _individuals.Values)
                        {
                            ind.Concepts = new DiscreteSet(CONCEPTS, String.Format("concepts of individual {0}", ind.Name));
                            Query query = new InstanceQuery(ind, concept);
                            queries.Enqueue(query);
                        }
                    }
                }
                #endregion

                this.ExecuteQueries(queries);

                #region Process query results

                UOProgressChanged("Processing query results");

                while (queries.Count > 0)
                {
                    Query generalQuery = queries.Dequeue();

                    if (generalQuery is SubsumptionQuery)
                    {
                        SubsumptionQuery query = (SubsumptionQuery)generalQuery;
                        DiscreteSet descendats = (DiscreteSet)query.concept1.Descendants;
                        DiscreteSet ancestors = (DiscreteSet)query.concept2.Ancestors;
                        descendats.AddMember(query.concept2, query.Result);
                        ancestors.AddMember(query.concept1, query.Result);
                    }
                    else if (generalQuery is InstanceQuery)
                    {
                        InstanceQuery query = (InstanceQuery)generalQuery;
                        DiscreteSet members = (DiscreteSet)query.Concept.Members;
                        DiscreteSet concepts = (DiscreteSet)query.Individual.Concepts;
                        members.AddMember(query.Individual, query.Result);
                        concepts.AddMember(query.Concept, query.Result);
                    }
                    else
                    {
                        throw new ApplicationException("Unsupported type of query");
                    }
                }

                #endregion

                UOProgressChanged("Finished");
            }

        }


        /// <summary>
        /// It should be invoked:
        ///-  At the beginning to register the object. The object is registered in an internal collection.
        ///-  Each time an important property of the object changes, i.e. within an event handler of a PropertyChange event. The object is updated in the internal collection.
        ///-  Once the object has been marked as inactive. Please use a property with attribute OntologyIndividualInactive. Once an individual is updated in this way, it is removed from the internal collection, as well as any reference to the object is removed, so that it can be eventually destroyed by the garbage collector.
        /// </summary>
        /// <param name="individual"></param>
        public void UpdateIndividual(object individual)
        {
            //Do we have the class of this individual already in the collection of classes?
            int indClassFound = _individualClasses.AsQueryable().Count<IndividualClass>(x => x.FullClassName.Equals(individual.GetType().FullName));
            
            IndividualClass indClass;

            if (indClassFound == 0)
            {   //...no => let's put it into our collection
                indClass = new IndividualClass(individual.GetType(), this);
                _individualClasses.Add(indClass);
            }else{
                indClass = _individualClasses.AsQueryable().First<IndividualClass>(x => x.FullClassName.Equals(individual.GetType().FullName));
            }

            if (!indClass.IsIndividual)
                return; //the individual will be ignored

            //You see we always create new instance of the individual, which is always invalid by default
            Individual indWrapper = new Individual(individual, indClass);

            if (indClass.StoreInOntology)
                this._ontologyIndividualsInvalid = true;
            else
                this._reasonerIndividualsInvalid = true;
            
            if (!indWrapper.Active && _individuals.ContainsKey( indWrapper.Name ))
            {
                _individuals.Remove( indWrapper.Name );
            }else if ( indWrapper.Active )
            {
                _individuals[ indWrapper.Name ] = indWrapper;
                //Index of the individual in the INDIVIDUALS dimension will be specified later in the RefreshMembershipsIfNecessary method.
            }
        }

        /// <summary>
        /// The method just iterates the specified collection, invoking UpdateIndividual for each member.
        /// </summary>
        /// <param name="individuals">Collection of individuals to update</param>
        public void UpdateIndividuals(IEnumerable<object> individuals)
        {
            foreach (object individual in individuals)
                UpdateIndividual(individual);
        }

        /// <summary>
        /// Concepts found in the ontology by the UpdateOntology method;
        /// </summary>
        public Concept[] Concepts
        {
            get
            {
                return _concepts.ToArray();
            }
        }

        /// <summary>
        /// Returns concept of the specified uri.
        /// </summary>
        /// <param name="Uri">Uri of the concept to return</param>
        /// <returns>Specified Concept.  Null is returned if no concept with such uri exists.</returns>
        public Concept GetConceptByUri(string Uri)
        {
            return _concepts.Find(t => t.Uri.Equals(Uri));
        }

        /// <summary>
        /// Individuals previously registered by the UpdateIndividual method
        /// </summary>
        public Individual[] Individuals
        {
            get { return _individuals.Values.ToArray(); }
        }

        /// <summary>
        /// Seaches for an individual which object is equal to the object specified as the input parameter.
        /// </summary>
        /// <param name="obj">Object whom wrapper has to be find</param>
        /// <returns>Individual, null if no individual found.</returns>
        public Individual FindIndividual(object obj)
        {
            if (_individuals.Values.Count(t => t.Object.Equals(t)) == 0)
                return null;
            return _individuals.Values.First<Individual>(t => t.Object.Equals(obj));
        }

        /// <summary>
        /// Returns an individual previously registered by the UpdateIndividual method.
        /// </summary>
        /// <param name="name">Name of the individual to return. Name is specified by a field/property of the indiviual. The IndividualNameAttribute
        /// specifies which field/property is used for this purpose.</param>
        /// <returns>Individual, or null if no indiviual of such name exists</returns>
        public Individual GetIndividualByName(string name)
        {
            if (!_individuals.ContainsKey(name))
                return null;

            return _individuals[name];
        }

        /// <summary>
        /// Returns a collection of individuals of the specified type. The inheritance hierarchy is also taken into account, hence the input parameter
        /// set to System.Object would return the complete list of individuals.
        /// </summary>
        /// <param name="type">Type of the individuals to return</param>
        /// <returns>Collection of individuals</returns>
        public Individual[] GetIndividualsByClass(Type type)
        {
            IEnumerable<Individual> result = new List<Individual>();
            foreach (IndividualClass indClass in _individualClasses)
            {
                if (indClass.FullClassNames.Contains<string>(type.FullName))
                {
                    IEnumerable<Individual> semiResult = _individuals.Values.Where(t => t.Class == indClass );
                    result.Concat(semiResult);
                }
            }

            return result.Distinct().ToArray();
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Czech Republic Czech Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions