Click here to Skip to main content
12,252,903 members (60,901 online)
Click here to Skip to main content

Stats

80.9K views
1.4K downloads
159 bookmarked
Posted

A Flexible Plugin System

, 3 Sep 2008 LGPL3
A generic plugin system used to load and manage plugins
trunk
dlls
xunit.dll
Examples
Plugins
ExampleApplication.Shared
Properties
ExampleApplication
Properties
ExamplePlugin.Shared
Properties
ExamplePlugin
Properties
Fadd.Globalization.Yaml
fadd.snk
Properties
Tests
fadd
Commands
Net
Tests
fadd.snk
Globalization
Tests
Logging
Plugins
Properties
Tests
Validation
using System;
using System.IO;
using System.Timers;

namespace Fadd.Globalization.Yaml
{
    /// <summary>
    /// Class to subscribe to changeevents on a file and update a languagehierarchy every time the file changes.
    /// </summary>
    /// <remarks>
    /// If the file being watched is deleted or renamed no more changes will be made to the languagenode.
    /// Also be observant on the fact that the class cannot Remove entries from a LanguageNode, only add or modify entries.
    /// This restriction is needed because a single YamlWatcher only watches One file and a LanguageNode can be connected to several
    /// watchers and files, deleting anything could clear things for another file.
    /// </remarks>
    public class YamlWatcher : IDisposable
    {
        public static char PathSeparator = '\\';
        private readonly string _fileName;
        private readonly string _path;
        private readonly string _fullName;
        private FileSystemWatcher _watcher;
        private readonly LanguageNode _languageNode;
		
        /// <summary>A timer to use as delay if the file is currently in use</summary>
        private readonly Timer _readTimer = new Timer(500);

        /// <summary>
        /// Instantiates the class to listen to changes in a file, also reads the file and fills the languageNode with language data
        /// </summary>
        /// <param name="languageNode">LanguageNode to fill</param>
        /// <param name="filename">The filename to watch</param>
        public YamlWatcher(LanguageNode languageNode, string filename)
        {
            if (!File.Exists(filename))
                throw new FileNotFoundException(filename + " do not exist.", filename);

            _path = Path.GetDirectoryName(Path.GetFullPath(filename));
            _fileName = Path.GetFileName(filename);
            _fullName = _path + "\\" + _fileName;
            _languageNode = languageNode;

            LoadFile(filename, _languageNode);
            Watch();

            _readTimer.Elapsed += OnTryRead;
        }

        /// <summary>
        /// Callback for when the file should be read again
        /// </summary>
        void OnTryRead(object sender, ElapsedEventArgs e)
        {
            _readTimer.Stop();
            LoadFile();
        }

        /// <summary>
        /// Sets the class to watch the specified file
        /// </summary>
        private void Watch()
        {
            _watcher = new FileSystemWatcher(_path, _fileName);
            _watcher.EnableRaisingEvents = true;
            _watcher.Changed += OnFileChanged;
        }

        /// <summary>
        /// Callback for when the file changes
        /// </summary>
        private void OnFileChanged(object sender, FileSystemEventArgs e)
        {
            LoadFile();
        }

        /// <summary>
        /// Private loading of file to wrap read failure control
        /// </summary>
        private void LoadFile()
        {
            try
            {
                LoadFile(_fullName, _languageNode);
            }
            catch (IOException)
            {
                _readTimer.Start();
            }
        }

        /// <summary>
        /// Fill the rootNode with languages and categories from the specified file
        /// </summary>
        /// <param name="fullPath">Full file path</param>
        /// <param name="rootNode">The rootNode to fill</param>
        /// <exception cref="ArgumentException">If rootNode is of type Empty</exception>
        /// <exception cref="ArgumentNullException">If rootNode or fullPath is null</exception>
        public static void LoadFile(string fullPath, LanguageNode rootNode)
        {
            if (string.IsNullOrEmpty(fullPath))
                throw new ArgumentNullException("fullPath");

            if (rootNode == null)
                throw new ArgumentNullException("rootNode");

            if (rootNode is EmptyLanguageNode)
                throw new ArgumentException("rootNode must not be of type Empty", "rootNode");

            // Clear out any old information in the language nodes
            // rootNode.ClearHierarchy(); // todo, decide what to do with this

            //TextReader reader = new StreamReader(fullPath);
            using (Stream stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                TextReader reader = new StreamReader(stream);
                YamlLight yaml = YamlLight.Parse(reader);

                // go through all languages
                foreach (YamlLight language in yaml)
                {
                    int lcid;
                    if (int.TryParse(language.Name, out lcid))
                    {
                        // go through all textstrings / categories for each language
                        foreach (YamlLight childNode in language)
                        {
                            // if the entry contains more than one line of text it is a sub root node and not just an entry in the root
                            if (childNode.Count > 0)
                            {
                                // try to retrieve the node from the root in case it's been instantiated before otherwise create it then expand
                                LanguageNode languageNode = rootNode.GetNode(childNode.Name);
                                if (languageNode is EmptyLanguageNode)
                                {
                                    languageNode = rootNode.AddNode(childNode.Name);
                                }
                                foreach (YamlLight child in childNode)
                                    Expand(child, languageNode, lcid);

                                if (!string.IsNullOrEmpty(childNode.Value))
                                    rootNode.Set(childNode.Name, lcid, childNode.Value);
                            }
                            else
								rootNode.Set(childNode.Name, lcid, childNode.Value);
                        }

                    }
                }

                reader.Dispose();
            }
        }

        private static void Expand(YamlLight node, LanguageNode parent, int lcid)
        {
            if(node.Count > 0)
            {
                LanguageNode langNode = parent.GetNode(node.Name);
                if (langNode is EmptyLanguageNode)
                    langNode = parent.AddNode(node.Name);

                if (!string.IsNullOrEmpty(node.Value))
					parent.Set(node.Name, lcid, node.Value);

                foreach (YamlLight subNode in node)
                    Expand(subNode, langNode, lcid);
            }
            else
                parent.Set(node.Name, lcid, node.Value);
        }

        #region IDisposable members
        /// <summary>
        /// Function to stop the watcher
        /// </summary>
        public void Dispose()
        {
            if (_watcher != null)
            {
                _watcher.Dispose();
                _watcher = null;
            }
        }
        #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 GNU Lesser General Public License (LGPLv3)

Share

About the Author

jgauffin
Founder Gauffin Interactive AB
Sweden Sweden
Founder of OneTrueError, a .NET service which captures, analyzes and provide possible solutions for exceptions.

blog | twitter

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160426.1 | Last Updated 3 Sep 2008
Article Copyright 2008 by jgauffin
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid