public FieldTempNull(Obj parent_arg, string field_arg) { // A FieldTempNull is a placeholder object which exists so that the operation // of setting a new field value without manually calling add_field() can be accomplished. // // This works by putting a FieldTempNull in someObj[someFieldName] // when the .get on that is called for a not-present key, // then _replacing_ the FieldTempNull with a newly-create IObjField // of one of the "real" types when the .set is called on the FieldTempNull. // // As such, it needs to know the parent Obj and the field name by which it is accessed, unlike other IObjField types. // // The surprising thing is, how well this scheme seemed to work upon the first proper trial. // // TODO: Figure out whether there is some problem with this eccentric-but-seemingly-functional scheme... // This seems to approximately match the "lazy initialization" pattern, per http://en.wikipedia.org/wiki/Lazy_initialization // Is also seems partially similar to the "null object" pattern, per http://en.wikipedia.org/wiki/Null_Object_pattern // It also seems somewhat like the "state pattern" pattern, per http://en.wikipedia.org/wiki/State_pattern // parent_obj = parent_arg; field_name = field_arg; }
void warn_add_OBJ_field_mismatch_with_archetype_field(Obj obj, string field_name, FieldType obj_field_type) { warn_at(); Form1.stdout.print(" Field type mismatch with Archetype '{1}', ignoring OBJ field '{0}',\n" + " obj_field_type '{2}' != arch_field_type '{3}'\n", field_name, obj.archetype.tag, obj_field_type.ToString(), obj.archetype[field_name].type.ToString()); }
public void create_some_objs() { Archetype aa = new Archetype("first_test_archetype", 7); // W00t, it looks like the FieldTempNull approach got rid of the need for these manual add_field() calls //aa.add_field("int_field", FieldType.INT); //aa.add_field("string_field", FieldType.STRING); //aa.add_field("decimal_field", FieldType.DECIMAL); //aa.add_field("list_int_field", FieldType.LIST_INT); //aa.add_field("list_string_field", FieldType.LIST_STRING); //aa.add_field("list_decimal_field", FieldType.LIST_DECIMAL); Obj obj1 = new Obj(aa, "first_test_obj", 0); stdout.print("--- Scalar field types: ---\n"); int ii = obj1["int_field"].iv; obj1["int_field"].iv = 99; stdout.print("ii={0}, obj1[].iv={1}\n", ii, obj1["int_field"].iv); // expect ii=0, obj1[].iv=99 string ss = obj1["string_field"].sv; obj1["string_field"].sv = "def hij"; stdout.print("ss='{0}', obj1[].sv='{1}'\n", ss, obj1["string_field"].sv); // expect ss='', obj1[].sv="def hij" decimal dd = obj1["decimal_field"].dv; obj1["decimal_field"].dv = 123.456M; stdout.print("dd={0}, obj1[].dv={1}\n", dd, obj1["decimal_field"].dv); // expect dd=0, obj1[].dv=123.456 stdout.print("\n"); //if (false) { // // Debug output related to the inheritance-of-interface bug found in FieldID and FieldListID: // FieldID argh = new FieldID(); // stdout.print("FieldID : GetType={0}, type={1}\n", argh.GetType(), argh.type); // Returns FieldID and ID -- CORRECT // IObjField xzzy = new FieldID(); // stdout.print("IObjField, manual cast upon use : GetType={0}, type={1}\n", ((FieldID) xzzy).GetType(), ((FieldID) xzzy).type); // Returns FieldID and ID -- CORRECT, have to manually cast upon use??? In the name of all that is static typing, WHY??? // IObjField horg = (FieldID) new FieldID(); // stdout.print("IObjField, manual cast upon assignment: GetType={0}, type={1}\n", horg.GetType(), horg.type); // Returns FieldID and INT -- WRONG, why??? (cast did not make the difference) // IObjField slar = new FieldID(); // stdout.print("IObjField, no cast : GetType={0}, type={1}\n", slar.GetType(), slar.type); // Returns FieldID and INT -- WRONG, why??? // // (The why is due to "implementation of interfaces" not being inherited; // // thus FieldID and FieldListID needed to explicitly indicate that they implement IObjField // stdout.print("\n"); //} aa.add_field("ID_field_1", FieldType.ID); //stdout.print("aa[ID_field_1].type = {0}\n", aa["ID_field_1"].type); // Prints ID //stdout.print("obj1[ID_field_1].type = {0}\n", obj1["ID_field_1"].type); // Prints ID (but not if FieldID does not explicitly specify implmentation of IObjField) //stdout.print("\n"); obj1["ID_field_1"].iv = 7; // 7 is known valid ID int ID1 = obj1["ID_field_1"].iv; stdout.print("ID={0,3}, obj1[].iv={1}\n", ID1, obj1["ID_field_1"].iv); // Expect ID=7, obj1[].iv=7 stdout.print("\n"); stdout.print("--- List field types: ---\n"); obj1["list_int_field" ].ilist = new List<int> { 1, 2, 3 }; obj1["list_int_field" ].ilist.Add(42); obj1["list_string_field" ].slist = new List<string> { "abc", "def", "hij" }; obj1["list_string_field" ].slist.Add("xyz"); obj1["list_decimal_field"].dlist = new List<decimal> { 1.1M, 2.2M, 3.3M }; obj1["list_decimal_field"].dlist.Add(999.999M); stdout.print("list_int_field = {0}\n", obj1["list_int_field"].ToString()); stdout.print("list_string_field = {0}\n", obj1["list_string_field" ].ToString() ); stdout.print("list_decimal_field = {0}\n", obj1["list_decimal_field"].ToString() ); stdout.print("\n"); Archetype a1 = new Archetype("foo_arch", 100); Obj o1 = new Obj(a1, "foo_o1", 101); Archetype a2 = new Archetype("bar_arch", 200); Obj o2 = new Obj(a2, "bar_o2", 201); Obj o3 = new Obj(a2, "bar_o3", 202); Obj o4 = new Obj(a2, "bar_o4", 203); obj1["list_ID_field"].ilist = new List<int> { 100, 101, 200, 201 }; obj1["list_ID_field"].ilist.Add(a1.ID); // ID == 100, Dupe Archetype ID in list is valid obj1["list_ID_field"].ilist.Add(o3.ID); // ID == 202 obj1["list_ID_field"].ilist.Add(o1.ID); // ID == 101, Dupe Obj ID in list is valid obj1["list_ID_field"].ilist.Add(o4.ID); // ID == 203 stdout.print("list_ID_field = {0}\n", obj1["list_ID_field"].ToString() ); // Expect [100, 101, 200, 201, 100, 202, 101, 203] }
public void create_some_objs() { Archetype aa = new Archetype("first_test_archetype", 7); // W00t, it looks like the FieldTempNull approach got rid of the need for these manual add_field() calls //aa.add_field("int_field", FieldType.INT); //aa.add_field("string_field", FieldType.STRING); //aa.add_field("decimal_field", FieldType.DECIMAL); //aa.add_field("list_int_field", FieldType.LIST_INT); //aa.add_field("list_string_field", FieldType.LIST_STRING); //aa.add_field("list_decimal_field", FieldType.LIST_DECIMAL); Obj obj1 = new Obj(aa, "first_test_obj", 0); stdout.print("--- Scalar field types: ---\n"); int ii = obj1["int_field"].iv; obj1["int_field"].iv = 99; stdout.print("ii={0}, obj1[].iv={1}\n", ii, obj1["int_field"].iv); // expect ii=0, obj1[].iv=99 string ss = obj1["string_field"].sv; obj1["string_field"].sv = "def hij"; stdout.print("ss='{0}', obj1[].sv='{1}'\n", ss, obj1["string_field"].sv); // expect ss='', obj1[].sv="def hij" decimal dd = obj1["decimal_field"].dv; obj1["decimal_field"].dv = 123.456M; stdout.print("dd={0}, obj1[].dv={1}\n", dd, obj1["decimal_field"].dv); // expect dd=0, obj1[].dv=123.456 stdout.print("\n"); //if (false) { // // Debug output related to the inheritance-of-interface bug found in FieldID and FieldListID: // FieldID argh = new FieldID(); // stdout.print("FieldID : GetType={0}, type={1}\n", argh.GetType(), argh.type); // Returns FieldID and ID -- CORRECT // IObjField xzzy = new FieldID(); // stdout.print("IObjField, manual cast upon use : GetType={0}, type={1}\n", ((FieldID) xzzy).GetType(), ((FieldID) xzzy).type); // Returns FieldID and ID -- CORRECT, have to manually cast upon use??? In the name of all that is static typing, WHY??? // IObjField horg = (FieldID) new FieldID(); // stdout.print("IObjField, manual cast upon assignment: GetType={0}, type={1}\n", horg.GetType(), horg.type); // Returns FieldID and INT -- WRONG, why??? (cast did not make the difference) // IObjField slar = new FieldID(); // stdout.print("IObjField, no cast : GetType={0}, type={1}\n", slar.GetType(), slar.type); // Returns FieldID and INT -- WRONG, why??? // // (The why is due to "implementation of interfaces" not being inherited; // // thus FieldID and FieldListID needed to explicitly indicate that they implement IObjField // stdout.print("\n"); //} aa.add_field("ID_field_1", FieldType.ID); //stdout.print("aa[ID_field_1].type = {0}\n", aa["ID_field_1"].type); // Prints ID //stdout.print("obj1[ID_field_1].type = {0}\n", obj1["ID_field_1"].type); // Prints ID (but not if FieldID does not explicitly specify implmentation of IObjField) //stdout.print("\n"); obj1["ID_field_1"].iv = 7; // 7 is known valid ID int ID1 = obj1["ID_field_1"].iv; stdout.print("ID={0,3}, obj1[].iv={1}\n", ID1, obj1["ID_field_1"].iv); // Expect ID=7, obj1[].iv=7 stdout.print("\n"); stdout.print("--- List field types: ---\n"); obj1["list_int_field"].ilist = new List <int> { 1, 2, 3 }; obj1["list_int_field"].ilist.Add(42); obj1["list_string_field"].slist = new List <string> { "abc", "def", "hij" }; obj1["list_string_field"].slist.Add("xyz"); obj1["list_decimal_field"].dlist = new List <decimal> { 1.1M, 2.2M, 3.3M }; obj1["list_decimal_field"].dlist.Add(999.999M); stdout.print("list_int_field = {0}\n", obj1["list_int_field"].ToString()); stdout.print("list_string_field = {0}\n", obj1["list_string_field"].ToString()); stdout.print("list_decimal_field = {0}\n", obj1["list_decimal_field"].ToString()); stdout.print("\n"); Archetype a1 = new Archetype("foo_arch", 100); Obj o1 = new Obj(a1, "foo_o1", 101); Archetype a2 = new Archetype("bar_arch", 200); Obj o2 = new Obj(a2, "bar_o2", 201); Obj o3 = new Obj(a2, "bar_o3", 202); Obj o4 = new Obj(a2, "bar_o4", 203); obj1["list_ID_field"].ilist = new List <int> { 100, 101, 200, 201 }; obj1["list_ID_field"].ilist.Add(a1.ID); // ID == 100, Dupe Archetype ID in list is valid obj1["list_ID_field"].ilist.Add(o3.ID); // ID == 202 obj1["list_ID_field"].ilist.Add(o1.ID); // ID == 101, Dupe Obj ID in list is valid obj1["list_ID_field"].ilist.Add(o4.ID); // ID == 203 stdout.print("list_ID_field = {0}\n", obj1["list_ID_field"].ToString()); // Expect [100, 101, 200, 201, 100, 202, 101, 203] }
public void test_set_invalid_ID_values_in_Obj() { stdout.print("--- ID field types (set invalid ID values): ---\n"); Archetype bad_arch = new Archetype("arch_to_set_invalid_ID_fields", 0); bad_arch.add_field("invalid_ID", FieldType.ID); bad_arch.add_field("invalid_LIST_ID", FieldType.LIST_ID); Obj bad_obj = new Obj(bad_arch, "obj_to_get_invalid_ID_fields", 0); bad_obj["invalid_ID"].iv = 9999; // Known invalid ID -- should fail (does fail, OK) stdout.print("bad_obj[invalid_ID] = {0}\n", bad_obj["invalid_ID"].ToString()); bad_obj["invalid_LIST_ID"].ilist = new List<int> { 9999, 8888, 7777 }; // Known invalid IDs -- should fail (does fail, OK) stdout.print("bad_obj[invalid_LIST_ID] = {0}\n", bad_obj["invalid_LIST_ID"].ToString()); // The add_field() method is available to control the type which would otherwise be auto-vivified: bad_obj.add_field("obj_added_ID_field", FieldType.ID); // Without this, the field becomes an INT upon auto-vivify bad_obj["obj_added_ID_field"].iv = 999; // 999 is known-invalid ID -- this fails because we called add_field() earlier int ID = bad_obj["obj_added_ID_field"].iv; stdout.print("bad_obj[obj_added_ID_field].type = {0}\n", bad_obj["obj_added_ID_field"].type); // prints ID, would print INT if we had not done add_field() earlier stdout.print("ID={0,3}, bad_obj[].iv={1}\n", ID, bad_obj["obj_added_ID_field"].iv); // Expect ID=999, obj1[].iv=999 (if we had gotten this far) }