Mercurial > projects > dynamin
diff dynamin/core/settings.d @ 0:aa4efef0f0b1
Initial commit of code.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 15 Jun 2009 22:10:48 -0500 |
parents | |
children | f8f5d6244795 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dynamin/core/settings.d Mon Jun 15 22:10:48 2009 -0500 @@ -0,0 +1,261 @@ +// Written in the D programming language +// www.digitalmars.com/d/ + +/* + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Dynamin library. + * + * The Initial Developer of the Original Code is Jordan Miner. + * Portions created by the Initial Developer are Copyright (C) 2007-2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jordan Miner <jminer7@gmail.com> + * + */ + +module dynamin.core.settings; + +import dynamin.core.string; +import tango.io.device.Conduit; +import tango.io.device.File; +import tango.io.device.Array; +import tango.io.stream.Text; +import tango.io.Stdout; +import tango.util.Convert; +import tango.core.Exception; + +// TODO: +align(1) +struct Pixel32 { +version(BigEndian) { + ubyte A; + ubyte R; + ubyte G; + ubyte B; +} else { + ubyte B; + ubyte G; + ubyte R; + ubyte A; +} +} +class Color2 { + Pixel32 ToPixel32() { + Pixel32 px; + return px; + } + string ToSetting() { + auto px = ToPixel32(); + return format("{}, {}, {}", px.R, px.G, px.B); + } + static Color2 FromSetting(string str) { + // allow "#AB00F2", "171, 0, 242" + return null; + } +} + +string test = r" +; Here is a comment +[Main] +UndoLevels=500 +# Here is another comment +CaretColor=255, 0, 0 +LineNumsVisible=true + +[RubyMode] +TabSize=4 +"; +unittest { + auto settings = new Settings; + settings.loadFromString(test); + assert(settings.get("UndoLevels") == "500"); + assert(settings.get("TabSize", "RubyMode") == "4"); + assert(getSetting!(int)(settings, "UndoLevels") == 500); + assert(getSetting!(bool)(settings, "LineNumsVisible") == true); + Stdout(settings.saveToString()).newline; +} + +/** + * This class will read from and write to what is basically a Windows INI file. + * Here is what a file would look like: + * Example: + * ----- + * ; Here is a comment + * [Main] + * UndoLevels=500 + * # Here is another comment + * CaretColor=255, 0, 0 + * LineNumsVisible=true + * + * [RubyMode] + * TabSize=4 + * ----- + */ +class Settings { +protected: + string[string][string] sections; + string[] comment; + void loadFromStream(InputStream stream) { + auto input = new TextInput(stream); + string section = MainSectionName; + bool inStartComment = true; + int lineNum = 0; + foreach(string line; input) { + lineNum++; + // check for a line with just whitespace + if(line.trim().length == 0) + continue; + + // check for a comment + if(line.trimLeft().startsWith(";") || + line.trimLeft().startsWith("#")) { + if(inStartComment) { + comment.length = comment.length + 1; + comment[$-1] = line.trimLeft()[1..$].dup; + } + continue; + } + inStartComment = false; + + // check for a section header + if(line.startsWith("[")) { + if(!line.endsWith("]") || line.length < 3) + throw new Exception("Invalid section on line " ~ to!(string)(lineNum)); + section = line[1..$-1].dup; + continue; + } + + // parse key=value line (quickly) + int eqIndex; + for(eqIndex = 0; eqIndex < line.length; ++eqIndex) + if(line[eqIndex] == '=') + break; + if(eqIndex == line.length) + throw new Exception("Invalid format on line " ~ to!(string)(lineNum)); + string value = line[eqIndex+1..$].unescape(); + sections[section][line[0..eqIndex].dup] = value; + } + } + void saveToStream(OutputStream stream) { + auto output = new TextOutput(stream); + foreach(line; comment) + output(';')(line).newline; + foreach(string sectionName, string[string] sectionData; sections) { + output('[')(sectionName)(']').newline;// TODO: newline before each section + foreach(key, value; sectionData) + output(key)('=')(value).newline; // TODO: escape? + } + } +public: + const string MainSectionName = "Main"; + /** + * Parses the specified file. + */ + void load(string file) { + scope f = new File(file); + loadFromStream(f); + } + void loadFromString(string str) { + scope a = new Array(str); + loadFromStream(a); + } + string saveToString() { + scope a = new Array(256, 80); + saveToStream(a); + return cast(string)a.slice(a.readable); + } + /** + * + * Examples: + * ----- + * settings.get("UndoLevels"); + * settings.get("TabSize", "RubyMode"); + * ----- + */ + string get(string name, string section = MainSectionName) { + auto sect = section in sections; + if(sect) { + auto val = name in *sect; + if(val) + return *val; + } + return ""; + } + + /** + * Examples: + * ----- + * settings.set("UndoLevels", "500") + * settings.set("TabSize", "4", "RubyMode"); + * ----- + */ + void set(string name, string value, string section = MainSectionName) { + if(section.contains('[') || section.contains(']')) + throw new IllegalArgumentException( + "Section name cannot contain a bracket"); + if(name.contains('=')) + throw new IllegalArgumentException( + "name cannot contain an equal sign"); + sections[section][name] = value; + } + /** + * Gets or sets the comment that appears at the beginning of the settings + * file. This is set when a file is read in. + */ + string[] commentLines() { + return comment; + } + /// ditto + void commentLines(string[] lines) { + comment = lines; + } +} + +/** + * Examples: + * ----- + * getSetting!(int)(settings, "UndoLevels"); + * getSetting!(int)(settings, "TabSize", "RubyMode"); + * ----- + */ +T getSetting(T)(Settings settings, + string name, string section = Settings.MainSectionName) { + string str = settings.get(name, section); + static if(is(T == bool)) + return str == "true" || str == "yes" || str == "on"; + else static if(is(T : long)) + return cast(T)to!(T)(str); + else + return T.FromSetting(str); +} + +/** + * Examples: + * ----- + * setSetting!(int)(settings, "UndoLevels", 500); + * setSetting!(int)(settings, "TabSize", 4, "RubyMode"); + * ----- + */ +void setSetting(T)(Settings settings, + string name, T value, string section = Settings.MainSectionName) { + string str; + static if(is(T == bool)) + str = value ? "true" : "false"; + else static if(is(T : long)) + str = to!(string)(value); + else + str = T.ToSetting(); + + settings.Set(name, str, section); +} +