From e87188e460eaf172c11806bc85417facb9e4ca29 Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Sun, 15 Nov 2015 23:38:57 +0000 Subject: [PATCH] =?UTF-8?q?Utils=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CmdArgs.cs | 201 +++++++++++++++++++++++++++++++++++++ FileLogger.cs | 47 +++++++++ InIReader.cs | 198 ++++++++++++++++++++++++++++++++++++ OwnController.cs | 21 ++++ OwnModel.cs | 36 +++++++ OwnObject.cs | 72 +++++++++++++ OwnView.cs | 24 +++++ Properties/AssemblyInfo.cs | 36 +++++++ Utils.csproj | 60 +++++++++++ bin/Release/Utils.dll | Bin 0 -> 15872 bytes 10 files changed, 695 insertions(+) create mode 100644 CmdArgs.cs create mode 100644 FileLogger.cs create mode 100644 InIReader.cs create mode 100644 OwnController.cs create mode 100644 OwnModel.cs create mode 100644 OwnObject.cs create mode 100644 OwnView.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Utils.csproj create mode 100644 bin/Release/Utils.dll diff --git a/CmdArgs.cs b/CmdArgs.cs new file mode 100644 index 0000000..0c52719 --- /dev/null +++ b/CmdArgs.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BlubbFish.Utils +{ + public class CmdArgs + { + public enum ArgLength + { + Single, + Touple + } + #region Classes + public class VaildArguments + { + public VaildArguments(ArgLength length, bool required) + { + this.Required = required; + this.Length = length; + } + public VaildArguments(ArgLength length) + { + this.Required = false; + this.Length = length; + } + + public ArgLength Length { get; private set; } + public bool Required { get; private set; } + } + private class ArgTouple + { + public ArgTouple(string type, string data) + { + this.type = type; + this.data = data; + } + public ArgTouple(string type) + { + this.type = type; + } + public string type { get; private set; } + public string data { get; private set; } + + internal void setData(string data) + { + if (data != "") + this.data = data; + } + } + #endregion + private string[] args; + private List argList; + private Dictionary argsPosible = new Dictionary(); + private static CmdArgs instances = null; + private bool isSetArguments = false; + + private CmdArgs() + { + } + + /// + /// Gibt eine Instanz der Klasse zurück + /// + /// Klasse + public static CmdArgs getInstance() + { + if (instances == null) + { + instances = new CmdArgs(); + } + return instances; + } + + /// + /// Übernimmt die Argumente für die Klasse + /// + /// Mögliche Komandozeilenargumente + /// Tatsächliche Komandozeilenargumente + public void setArguments(Dictionary arguments, string[] args) + { + this.args = args; + if (!this.isSetArguments) + { + this.isSetArguments = true; + this.argsPosible = arguments; + this.Init(); + } + } + + private void Init() + { + this.argList = new List(); + for (int i = 0; i < this.args.Length; i++) + { + if (this.argsPosible.Keys.Contains(args[i])) + { + ArgTouple arg = new ArgTouple(args[i]); + if (argsPosible[args[i]].Length == ArgLength.Touple) + { + if (args.Length > i + 1) + { + arg.setData(args[++i]); + } + else + { + throw new ArgumentException(); + } + } + this.argList.Add(arg); + } + } + } + + /// + /// Menge der angegebenen Komandozeilen-Argumente + /// + /// Menge + public int GetArgsLength() + { + return this.argList.Count; + } + + /// + /// Gibt zurück ob ein Argument angegeben wurde + /// + /// Name des Arguments + /// true wenn angegeben + public bool HasArgumentType(string name) + { + foreach (ArgTouple t in this.argList) + { + if (t.type == name) + return true; + } + return false; + } + + /// + /// Gibt den Inhalt des angegeben Arguments zurück, nur bei zweiteiligen Argumenten möglich + /// + /// Name des Arguments + /// Inhalt des Arguments oder ArgumentNullException + public string GetArgumentData(string name) + { + foreach (ArgTouple t in this.argList) + { + if (t.type == name && t.data != null) + return t.data; + else + { + throw new ArgumentNullException(); + } + } + return null; + } + + public bool HasAllRequiredArguments() + { + foreach (KeyValuePair item in this.argsPosible) + { + if (item.Value.Required && !this.HasArgumentType(item.Key)) + { + return false; + } + } + return true; + } + + public string getUsageList(string name) + { + string ret = "Usage: "+name+" Parameter\nParameter:\n"; + string req =""; + string opt = ""; + foreach (KeyValuePair item in this.argsPosible) + { + if (item.Value.Required) + { + req += item.Key+" "+((item.Value.Length == ArgLength.Touple)?" [data]\n":"\n"); + } + } + if (req != "") + { + ret += "Benötigte Parameter:\n" + req; + } + foreach (KeyValuePair item in this.argsPosible) + { + if (!item.Value.Required) + { + opt += item.Key + " " + ((item.Value.Length == ArgLength.Touple) ? " [data]\n" : "\n"); + } + } + if (opt != "") + { + ret += "Optionale Parameter:\n" + opt; + } + return ret; + } + } +} diff --git a/FileLogger.cs b/FileLogger.cs new file mode 100644 index 0000000..6157d7d --- /dev/null +++ b/FileLogger.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace BlubbFish.Utils +{ + public class FileLogger + { + private static Dictionary instances = new Dictionary(); + private StreamWriter file; + private FileLogger(string filename, bool append) + { + if (!File.Exists(filename)) + { + string folder = Path.GetDirectoryName(Path.GetFullPath(filename)); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + } + this.file = new StreamWriter(filename, append, Encoding.UTF8); + this.file.AutoFlush = true; + } + public static FileLogger getInstance(string filename, bool append) + { + if (!instances.Keys.Contains(filename)) + { + instances.Add(filename, new FileLogger(filename, append)); + } + return instances[filename]; + } + + public void setArray(string[] text) + { + this.file.Write(String.Join(file.NewLine, text) + file.NewLine); + this.file.Flush(); + } + + public void setLine(string text) + { + this.file.WriteLine(text); + this.file.Flush(); + } + } +} diff --git a/InIReader.cs b/InIReader.cs new file mode 100644 index 0000000..fc87deb --- /dev/null +++ b/InIReader.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace BlubbFish.Utils +{ + public class InIReader + { + private Dictionary> cont; + private FileSystemWatcher k = new FileSystemWatcher(Directory.GetCurrentDirectory(), "*.ini"); + private string filename; + + private static Dictionary instances = new Dictionary(); + + private InIReader(string filename) + { + this.filename = filename; + k.Changed += new FileSystemEventHandler(this.readAgain); + loadFile(); + } + + public static InIReader getInstance(string filename) + { + if (!instances.Keys.Contains(filename)) + { + instances.Add(filename, new InIReader(filename)); + } + return instances[filename]; + } + + private void readAgain(object sender, EventArgs e) + { + this.loadFile(); + } + + private void loadFile() + { + this.cont = new Dictionary>(); + StreamReader file = new StreamReader(this.filename); + List buf = new List(); + string fline = ""; + while (fline != null) + { + fline = file.ReadLine(); + if (fline != null && fline.Length > 0 && fline.Substring(0,1) != ";") + buf.Add(fline); + } + file.Close(); + Dictionary sub = new Dictionary(); + string cap = ""; + foreach (string line in buf) + { + Match match = Regex.Match(line, @"^\[[a-zA-Z0-9\-_ ]+\]\w*$", RegexOptions.IgnoreCase); + if (match.Success) + { + if (sub.Count != 0 && cap != "") + { + this.cont.Add(cap, sub); + } + cap = line; + sub = new Dictionary(); + } + else + { + if (line != "" && cap != "") + { + string key = line.Substring(0, line.IndexOf('=')); + string value = line.Substring(line.IndexOf('=') + 1); + sub.Add(key, value); + } + } + } + if (sub.Count != 0 && cap != "") + { + this.cont.Add(cap, sub); + } + } + + public List getSections() + { + return cont.Keys.ToList(); + } + + public String getValue(String section, String key) + { + if (!section.StartsWith("[")) + { + section = "[" + section + "]"; + } + if (cont.Keys.Contains(section)) + { + if (cont[section].Keys.Contains(key)) + { + return cont[section][key]; + } + } + return null; + } + + + public void SetValue(string section, string key, string value) + { + if (!section.StartsWith("[")) + { + section = "[" + section + "]"; + } + if (cont.Keys.Contains(section)) + { + if (cont[section].Keys.Contains(key)) + { + cont[section][key] = value; + } + else + { + cont[section].Add(key, value); + } + } + else + { + Dictionary sub = new Dictionary(); + sub.Add(key, value); + cont.Add(section, sub); + } + this.changed(); + } + + private void changed() + { + k.Changed -= null; + saveSettings(); + loadFile(); + k.Changed += new FileSystemEventHandler(this.readAgain); + } + + private void saveSettings() + { + StreamWriter file = new StreamWriter(this.filename); + file.BaseStream.SetLength(0); + file.BaseStream.Flush(); + file.BaseStream.Seek(0, SeekOrigin.Begin); + foreach (KeyValuePair> cap in this.cont) + { + file.WriteLine(cap.Key); + foreach (KeyValuePair sub in cap.Value) + { + file.WriteLine(sub.Key + "=" + sub.Value); + } + file.WriteLine(); + } + file.Flush(); + file.Close(); + } + + /// + /// Fügt eine neue Sektion in der Ini-Datei ein. + /// + /// Sektionsname + /// true if added, false if error + public bool addSection(string name) + { + if (!name.StartsWith("[")) + { + name = "[" + name + "]"; + } + if (this.cont.Keys.Contains(name)) + { + return false; + } + this.cont.Add(name, new Dictionary()); + this.changed(); + return true; + } + + /// + /// Löscht eine Sektion inklusive Unterpunkte aus der Ini-Datei. + /// + /// Sektionsname + /// true if removed, false if error + public bool removeSection(string name) + { + if (!name.StartsWith("[")) + { + name = "[" + name + "]"; + } + if (!this.cont.Keys.Contains(name)) + { + return false; + } + this.cont.Remove(name); + this.changed(); + return false; + } + } +} diff --git a/OwnController.cs b/OwnController.cs new file mode 100644 index 0000000..9a5ac35 --- /dev/null +++ b/OwnController.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace BlubbFish.Utils +{ + public abstract class OwnController + { + /// + /// Führt den Controller aus. + /// + public void execute() + { + this.init(); + } + abstract protected void init(); + } +} diff --git a/OwnModel.cs b/OwnModel.cs new file mode 100644 index 0000000..42c2273 --- /dev/null +++ b/OwnModel.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BlubbFish.Utils +{ + public abstract class OwnModel where T : class + { + private static readonly Lazy _instance = new Lazy(() => CreateInstanceOfT()); + private List observer = new List(); + public static T Instance + { + get + { + return _instance.Value; + } + } + private static T CreateInstanceOfT() + { + return Activator.CreateInstance(typeof(T), true) as T; + } + + public void setObserver(OwnView tray) + { + this.observer.Add(tray); + tray.update(); + } + protected void update() + { + this.observer.ForEach(delegate(OwnView tray) { tray.update(); }); + } + abstract protected void init(); + } +} diff --git a/OwnObject.cs b/OwnObject.cs new file mode 100644 index 0000000..fc884b5 --- /dev/null +++ b/OwnObject.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BlubbFish.Utils { + abstract public class OwnObject { + private List> loglist = new List>(); + + public delegate void LogEvent(string location, string message, LogLevel level, DateTime date); + public enum LogLevel : int { + Debug = 1, + Notice = 2, + Info = 4, + Warn = 8, + Error = 16 + } + + public event LogEvent eventDebug; + public event LogEvent eventNotice; + public event LogEvent eventInfo; + public event LogEvent eventWarn; + public event LogEvent eventError; + public event LogEvent EventLog; + + /// + /// Get the Complete Log + /// + public List getLog(LogLevel level, bool classNames, bool timeStamps) { + List ret = new List(); + foreach(Tuple t in this.loglist) { + if(t.Item4 >= level) { + ret.Add(logToString(t.Item2, t.Item3, t.Item4, t.Item1, classNames, timeStamps)); + } + } + return ret; + } + + /// + /// Formates a LogMessage to a String + /// + public static string logToString(string location, string message, LogLevel level, DateTime date, bool classNames, bool timeStamps) { + return (timeStamps ? "[" + date.ToString("R") + "]: "+level.ToString()+" " : "") + (classNames ? location + ", " : "") + message; + } + + protected void addLog(string location, string message, LogLevel level) { + this.addLog(location, message, level, DateTime.Now); + } + + protected void addLog(string location, string message, LogLevel level, DateTime date) { + if(eventDebug != null && level >= LogLevel.Debug) { + eventDebug(location, message, level, date); + } + if(eventNotice != null && level >= LogLevel.Notice) { + eventNotice(location, message, level, date); + } + if(eventInfo != null && level >= LogLevel.Info) { + eventInfo(location, message, level, date); + } + if(eventWarn != null && level >= LogLevel.Warn) { + eventWarn(location, message, level, date); + } + if(eventError != null && level >= LogLevel.Error) { + eventError(location, message, level, date); + } + if(EventLog != null) { + EventLog(location, message, level, date); + } + this.loglist.Add(new Tuple(date, location, message, level)); + } + } +} diff --git a/OwnView.cs b/OwnView.cs new file mode 100644 index 0000000..4b39e48 --- /dev/null +++ b/OwnView.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BlubbFish.Utils +{ + public abstract class OwnView + { + /// + /// Called if the Oberver (Model) updates its View + /// + abstract public void update(); + /// + /// Called if view is viewed + /// + abstract protected void init(); + /// + /// Called if Form is Disposed + /// + abstract public void Dispose(); + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1ed8460 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("Utils")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Utils")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("6f20376a-5c71-4979-9932-13c105d1c6e6")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Utils.csproj b/Utils.csproj new file mode 100644 index 0000000..22e3a26 --- /dev/null +++ b/Utils.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098} + Library + Properties + BlubbFish.Utils + Utils + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bin/Release/Utils.dll b/bin/Release/Utils.dll new file mode 100644 index 0000000000000000000000000000000000000000..d11ce7ca29a8b9be6c71d09ddf621bb7fff8e761 GIT binary patch literal 15872 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 If4UL)zwC`n2mk;8 literal 0 HcmV?d00001