Mercurial > projects > mde
diff mde/content/AStringContent.d @ 163:24d77c52243f
Provided sensible conversions for setting the value of one AStringContent from another, along with unittest.
Updated layout and Translation unittests to run.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 23 May 2009 15:47:32 +0200 |
parents | 2476790223b8 |
children | c13bded1bed3 |
line wrap: on
line diff
--- a/mde/content/AStringContent.d Fri May 22 19:59:22 2009 +0200 +++ b/mde/content/AStringContent.d Sat May 23 15:47:32 2009 +0200 @@ -18,9 +18,12 @@ module mde.content.AStringContent; public import mde.content.Content; +import Ascii = tango.text.Ascii; +import Util = tango.text.Util; //FIXME: efficient conversions? Need to dup result when formatting a string anyway? import Int = tango.text.convert.Integer; import Float = tango.text.convert.Float; +import Math = tango.math.Math; import derelict.sdl.keysym; import tango.util.log.Log : Log, Logger; @@ -29,6 +32,17 @@ logger = Log.getLogger ("mde.content.AStringContent"); } +/** Union of all content types here - for dynamic cast checking against several + * types. */ +union UnionContent { + AStringContent asc; + BoolContent bc; + StringContent sc; + IntContent ic; + DoubleContent dc; + EnumContent ec; +} + /** Base class for content containing a simple value editable as text. * * Derived classes should implement endEdit to convert sv and assign its value @@ -52,16 +66,11 @@ } /** Set the content via conversion to/from string. */ - override bool setContent (IContent c) { - AStringContent asc = cast (AStringContent) c; - if (asc !is null) { - try { - sv = asc.toString (0); - endEdit; - } catch (Exception) { // invalid conversion; just reject c - return false; - } - return true; + override bool set (IContent c) { + //AStringContent asc = cast (AStringContent) c; + if (c !is null) { + sv = c.toString (0).dup; + return endEdit; } return false; } @@ -81,10 +90,11 @@ sv = sv[0..pos] ~ sv[p..$]; } else { // insert character char[] tail = sv[pos..$]; + //NOTE: reallocating each keypress isn't optimal sv.length = sv.length + i.length; size_t npos = pos+i.length; if (tail) sv[npos..$] = tail.dup; // cannot assign with overlapping ranges - sv[pos..npos] = i; + sv[pos..npos] = i; pos = npos; } } else { // use sym; many keys output 0 @@ -136,11 +146,19 @@ } } - /** Call after editing a string; return new string (may be changed/reverted). */ - char[] endEdit (); + /** Call after editing a string. + * + * Returns: true if string successfully converted to value. + * + * Should never throw; should reset sv at least when returning false. */ + bool endEdit (); protected: - char[] sv; // string of value; updated on assignment for displaying and editing + /* String version of value (for toString(0) and editing). + * WARNING: This must point to mutable memory! + * (Actually this isn't usually required now, but after optimising will be.) + * TODO: provide a buffer, for use when editing sv. */ + char[] sv; size_t pos; // editing position; used by keyStroke } @@ -151,14 +169,14 @@ auto valp = symbol in changed.boolData; if (valp) v = *valp; - sv = v ? "true" : "false"; + sv = (v ? "true" : "false").dup; super (symbol); } // Assign without adding change to save changeset void assignNoCng (bool val) { v = val; - sv = v ? "true" : "false"; + sv = (v ? "true" : "false").dup; if (pos > sv.length) pos = sv.length; endEvent; } @@ -171,12 +189,24 @@ } alias opCall opCast; - override char[] endEdit () { - v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1'); - sv = v ? "true" : "false"; + override bool endEdit () { + try { + sv = Util.trim (Ascii.toLower (sv)); // NOTE: sv must be in mutable memory + if (sv == "false") + v = 0; + else if (sv == "true") + v = 1; + else // throws if can't convert to int: + v = (Int.toLong (sv) != 0); + } catch (Exception e) { + logger.error (e.msg); + sv = (v ? "true" : "false").dup; + return false; + } + sv = (v ? "true" : "false").dup; endEvent; endCng; - return sv; + return true; } // Add change to changeset @@ -212,10 +242,10 @@ } alias opCall opCast; - override char[] endEdit () { + override bool endEdit () { endEvent; endCng; - return sv; + return true; } void endCng () { @@ -238,15 +268,24 @@ super (symbol); } - // NOTE: the only point of this method is to avoid int->string->int conversions, - // and perhaps specialise (double->int rounding?) - override bool setContent (IContent c) { - IntContent ic = cast (IntContent) c; - if (ic !is null) { - this = ic(); + override bool set (IContent c) { + UnionContent uc; + uc.bc = cast (BoolContent) c; + if (uc.bc !is null) { + this = uc.bc(); return true; } - return super.setContent (c); + uc.ic = cast (IntContent) c; + if (uc.ic !is null) { + this = uc.ic(); + return true; + } + uc.dc = cast (DoubleContent) c; + if (uc.dc !is null) { + this = Math.rndint (uc.dc()); // round to nearest + return true; + } + return super.set (c); } void assignNoCng (int val) { @@ -264,16 +303,18 @@ } alias opCall opCast; - override char[] endEdit () { + override bool endEdit () { try { v = Int.toInt (sv); } catch (Exception e) { - logger.warn (e.msg); - } + logger.error (e.msg); + sv = Int.toString (v); + return false; + } sv = Int.toString (v); endEvent; endCng; - return sv; + return true; } void endCng () { @@ -284,7 +325,7 @@ int v; } -/** Double content. */ +/** Floating-point content. */ class DoubleContent : AStringContent { /** Create a content with _symbol name symbol. */ @@ -296,6 +337,26 @@ super (symbol); } + override bool set (IContent c) { + UnionContent uc; + uc.bc = cast (BoolContent) c; + if (uc.bc !is null) { + this = uc.bc(); + return true; + } + uc.ic = cast (IntContent) c; + if (uc.ic !is null) { + this = uc.ic(); + return true; + } + uc.dc = cast (DoubleContent) c; + if (uc.dc !is null) { + this = uc.dc(); + return true; + } + return super.set (c); + } + void assignNoCng (double val) { v = val; sv = Float.toString (v, 8, 4); @@ -311,16 +372,18 @@ } alias opCall opCast; - override char[] endEdit () { + override bool endEdit () { try { v = Float.toFloat (sv); } catch (Exception e) { - logger.warn (e.msg); - } + logger.error (e.msg); + sv = Float.toString (v, 8, 4); + return false; + } sv = Float.toString (v, 8, 4); endEvent; endCng; - return sv; + return true; } void endCng () { @@ -349,7 +412,7 @@ e = new EnumValueContent (this, i, symPeriod~enumSymbols[i]); } enums[v].assignFromParent (true); - sv = enums[v].name_; + sv = enums[v].name_.dup; // Re-set the value if a saved value is found: auto valp = symbol in changed.enumValData; if (valp) @@ -368,7 +431,7 @@ return; } } - logger.warn ("EnumContent {} assigned invalid enumeration: {}; valid: {}", symbol, enumSym, enumSymbols); + logger.error ("EnumContent {} assigned invalid enumeration: {}; valid: {}", symbol, enumSym, enumSymbols); } size_t opCall () { //debug logger.trace ("EnumContent {} returning value: {} ({})",symbol, enumSymbols[v], v); @@ -383,25 +446,26 @@ enums[v] .assignFromParent (false); enums[val].assignFromParent (true); v = val; - sv = enums[v].name_; + sv = enums[v].name_.dup; if (pos > sv.length) pos = sv.length; endEvent; } - override char[] endEdit () { + override bool endEdit () { foreach (i,e; enums) if (sv == e.name_) { assignNoCng (i); goto break1; } - sv = enums[v].name_; // sv was edited; revert + sv = enums[v].name_.dup; // sv was edited; revert logger.error ("EnumContent "~name_~" assigned invalid value; keeping value: "~sv); if (pos > sv.length) pos = sv.length; + return false; break1: endCng; - return sv; + return true; } void endCng () { @@ -452,10 +516,12 @@ super.assignNoCng (val); } - override char[] endEdit () { - v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1'); - parent.childAssign (i); - return sv; + override bool endEdit () { + if (super.endEdit) { + parent.childAssign (i); + return true; // value accepted by BoolContent, not necessarily by EnumContent + } + return false; } protected: @@ -463,3 +529,53 @@ size_t i; } } + +debug (mdeUnitTest) { + unittest { + bool throws (void delegate() dg) { + bool r = false; + try { + dg(); + } catch (Exception e) { + r = true; + } + return r; + } + + StringContent sc = new StringContent ("unittest.sc"); + IntContent ic = new IntContent ("unittest.ic"); + BoolContent bc = new BoolContent ("unittest.bc"); + DoubleContent dc = new DoubleContent ("unittest.dc"); + + logger.info ("You should see some \"invalid literal\" errors:"); + sc = "16"; + ic.set = sc; + assert (ic() == 16); + sc = "five"; // fails + ic.set = sc; + assert (ic.toString(0) == "16"); + + bc.set = ic; + assert (bc()); + sc = "fALse"; + bc.set = sc; + assert (!bc()); + + sc = "31.5"; + ic.set = sc; // parses as int which fails + assert (ic() == 16); + dc.set = sc; + ic.set = dc; // rounds to even + assert (ic() == 32); + dc = -1.5; + ic.set = dc; // rounds to even + assert (ic() == -2); + + bc.set = dc; // fails: not included conversion + assert (!bc()); + bc.set = ic; + assert (bc()); + + logger.info ("Unittest complete."); + } +} \ No newline at end of file