Переглянути джерело

Break the property mapper behaviour somwhat

Billy Barrow 1 місяць тому
батько
коміт
19e58d1fb4

+ 3 - 3
src/lib/Associative/Properties.vala

@@ -2,12 +2,12 @@
 namespace Invercargill {
 
     public interface Properties : Enumerable<KeyValuePair<string, Element>>, KeyValues<string, Element> {
-        public abstract void set_native<T>(string key, T value) throws ElementError;
+        public abstract void set_native<T>(string key, T value, bool? defined = null) throws ElementError;
     }
 
     public class PropertiesDictionary : Dictionary<string, Element>, Properties {
-        public void set_native<T>(string key, T value) throws ElementError {
-            this[key] = new NativeElement<T>(value);
+        public void set_native<T>(string key, T value, bool? defined = null) throws ElementError {
+            this[key] = new NativeElement<T>(value, defined);
         }
     }
 

+ 27 - 11
src/lib/Collections/Fifo.vala

@@ -2,15 +2,14 @@ namespace Invercargill {
 
     public class Fifo<T> : Enumerable<T> {
 
-        public bool marked_complete { get; private set; }
-        public bool is_complete { get; private set; }
+        public bool is_blocking { get; private set; default = true;}
 
         private FifoItem<T>? first_item = null;
         private FifoItem<T>? last_item = null;
 
-        public void complete() {
+        public void unblock() {
             mutex.lock();
-            marked_complete = true;
+            is_blocking = false;
             cond.broadcast();
             mutex.unlock();
         }
@@ -34,6 +33,28 @@ namespace Invercargill {
             mutex.unlock();
         }
 
+        public void push_all(Enumerable<T> items) {
+             mutex.lock();
+
+             foreach (var item in items) {
+                var fifo_item = new FifoItem<T>() {
+                    item = item,
+                };
+    
+                if(first_item == null) {
+                    first_item = fifo_item;
+                    last_item = fifo_item;
+                }
+                else {
+                    last_item.next_item = fifo_item;
+                    last_item = fifo_item;
+                }
+             }
+
+            cond.broadcast();
+            mutex.unlock();
+        }
+
         public void push_start(owned T item) {
             mutex.lock();
             var fifo_item = new FifoItem<T>() {
@@ -52,7 +73,7 @@ namespace Invercargill {
 
         private bool has_next() {
             mutex.lock();
-            while(first_item == null && !marked_complete){
+            while(is_blocking && first_item == null){
                 cond.wait(mutex);
             }
 
@@ -64,12 +85,7 @@ namespace Invercargill {
         private T get_next() {
             mutex.lock();
             var item = first_item;
-            first_item = item.next_item;
-
-            if(first_item == null && marked_complete) {
-                is_complete = true;
-            }
-            
+            first_item = item.next_item;            
             mutex.unlock();
             return item.item;
         }

+ 5 - 6
src/lib/Element.vala

@@ -92,9 +92,11 @@ namespace Invercargill {
     public class NativeElement<T> : Object, Element {
 
         private T object;
-
-        public NativeElement(T obj) {
+        private bool nullable;
+        
+        public NativeElement(T obj, bool nullable = false) {
             object = obj;
+            this.nullable = nullable;
         }
 
         public bool assignable_to_type(GLib.Type type) {
@@ -112,10 +114,7 @@ namespace Invercargill {
         }
 
         public bool is_null() {
-            if(type().is_a(typeof(bool))) {
-                return false;
-            }
-            return object == null;
+            return nullable && object == null;
         }
 
         public bool try_get_as<TOut>(out TOut result) {

+ 1 - 1
src/lib/Modifiers/Parallel.vala

@@ -59,7 +59,7 @@ namespace Invercargill {
             lock(remaining_workers) {
                 remaining_workers--;
                 if(remaining_workers == 0) {
-                    queue.complete();
+                    queue.unblock();
                 }
             }
             return true;

+ 118 - 37
src/lib/PropertyMapper.vala

@@ -1,7 +1,9 @@
 namespace Invercargill {
 
     public delegate TProp PropertyGetter<TClass, TProp>(TClass object);
+    public delegate bool PropertyPredicate<TClass>(TClass object);
     public delegate void PropertySetter<TClass, TProp>(TClass object, TProp value) throws Error;
+    public delegate void PropertyDefaultSetter<TClass>(TClass object);
     public delegate Tout PropertyGetterTransformer<Tin, Tout>(Tin object);
     public delegate Tout PropertySetterTransformer<Tin, Tout>(Tin object) throws Error;
     public delegate T ObjectConstructor<T>();
@@ -27,11 +29,16 @@ namespace Invercargill {
         public void map_into(T object, KeyValues<string, Element> properties) throws Error {
             foreach (var mapping in mappings) {
                 var exists = properties.has(mapping.name);
-                if(!mapping.mandatory && !exists) {
+                if(mapping.undefined_setter != null && !exists) {
+                    mapping.undefined_setter(object);
                     continue;
                 }
-                if(mapping.mandatory && !exists){
+                if(mapping.undefined_setter == null && !exists){
                     throw new IndexError.KEY_NOT_FOUND(@"Failed to map into $(typeof(T).name()): Mandatory property \"$(mapping.name)\" was not present in the properties list");
+                }
+                var element = properties[mapping.name];
+                if(element.is_null()) {
+
                 }
                 mapping.setter(object, properties[mapping.name]);
             }
@@ -46,10 +53,20 @@ namespace Invercargill {
         public override Properties map_from(T object) {
             var properties = new PropertiesDictionary();
             foreach (var mapping in mappings) {
-                var val = mapping.getter(object);
-                if(mapping.mandatory || !val.is_null()) {
-                    properties[mapping.name] = val;
+                // Check undefined
+                if(mapping.undefined_check != null && mapping.undefined_check(object)){
+                    continue;
+                }
+
+                // Check null
+                if(mapping.null_check != null && mapping.null_check(object)) {
+                    properties[mapping.name] = new NullElement();
+                    continue;
                 }
+
+                // Assign value
+                var val = mapping.getter(object);
+                properties[mapping.name] = val;
             }
             return properties;
         }
@@ -58,9 +75,12 @@ namespace Invercargill {
 
     private class PropertyMapping<T> {
         public string name;
-        public bool mandatory;
         public PropertyGetter<T, Element> getter;
         public PropertySetter<T, Element> setter;
+        public PropertyPredicate<T>? null_check;
+        public PropertyPredicate<T>? undefined_check;
+        public PropertyDefaultSetter<T>? null_setter;
+        public PropertyDefaultSetter<T>? undefined_setter;
     }
 
     public class PropertyMapperBuilder<T> {
@@ -73,58 +93,57 @@ namespace Invercargill {
             constructor = () => Object.new(typeof(T));
         }
 
-        public PropertyMapperBuilder<T> map<TProp>(string name, owned PropertyGetter<T, TProp> getter, owned PropertySetter<T, TProp> setter, bool mandatory = true) {
+        public virtual PropertyMappingBuilder<T> map<TProp>(string name, owned PropertyGetter<T, TProp> getter, owned PropertySetter<T, TProp> setter) {
+            PropertyMapping<T> mapping;
             if(typeof(TProp).is_a(typeof(Element))) {
-                add_mapping(new PropertyMapping<T>() {
+                mapping = new PropertyMapping<T>() {
                     name = name,
-                    mandatory = mandatory,
                     getter = (owned)getter,
-                    setter = (owned)setter
-                });
+                    setter = (owned)setter,
+                };
             }
             else {
-                add_mapping(new PropertyMapping<T>() {
+                mapping = new PropertyMapping<T>() {
                     name = name,
-                    mandatory = mandatory,
                     getter = (o) => new NativeElement<TProp>(getter(o)),
                     setter = (o,d) => setter(o, d.as<TProp>())
-                });
+                };
             }
-            return this;
+            add_mapping(mapping);
+            return new PropertyMappingBuilder<T>(this, mapping);
         }
 
-        public PropertyMapperBuilder<T> map_properties_with<TObj>(string name, owned PropertyGetter<T, TObj> getter, owned PropertySetter<T, TObj> setter, PropertyMapper<TObj> mapper, bool mandatory = true) {
-            return map_with<TObj, Properties>(name, getter, setter, mapper, mandatory);
+        public virtual PropertyMappingBuilder<T> map_properties_with<TObj>(string name, owned PropertyGetter<T, TObj> getter, owned PropertySetter<T, TObj> setter, PropertyMapper<TObj> mapper) {
+            return map_with<TObj, Properties>(name, getter, setter, mapper);
         }
 
-        public PropertyMapperBuilder<T> map_with<TNative, TElement>(string name, owned PropertyGetter<T, TNative> getter, owned PropertySetter<T, TNative> setter, Mapper<TNative, TElement> mapper, bool mandatory = true) {
-            add_mapping(new PropertyMapping<T>() {
+        public virtual PropertyMappingBuilder<T> map_with<TNative, TElement>(string name, owned PropertyGetter<T, TNative> getter, owned PropertySetter<T, TNative> setter, Mapper<TNative, TElement> mapper) {
+            var mapping = new PropertyMapping<T>() {
                 name = name,
-                mandatory = mandatory,
                 getter = (o) => new NativeElement<TElement>(mapper.map_from(getter(o))),
                 setter = (o,d) => setter(o, mapper.materialise(d.as<TElement>()))
-            });
-            return this;
+            };
+            add_mapping(mapping);
+            return new PropertyMappingBuilder<T>(this, mapping);
         }
 
-        public PropertyMapperBuilder<T> map_many<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter, bool mandatory = true) {
-            add_mapping(new PropertyMapping<T>() {
+        public virtual PropertyMappingBuilder<T> map_many<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter) {
+            var mapping = new PropertyMapping<T>() {
                 name = name,
-                mandatory = mandatory,
                 getter = (o) => new NativeElement<Elements>(getter(o).to_elements()),
                 setter = (o,d) => setter(o, d.as<Elements>().elements_as<TObj>())
-            });
-            return this;
+            };
+            add_mapping(mapping);
+            return new PropertyMappingBuilder<T>(this, mapping);;
         }
 
-        public PropertyMapperBuilder<T> map_property_groups_with<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter, PropertyMapper<TObj> mapper, bool mandatory = true) {
-            return map_many_with<TObj, Properties>(name, getter, setter, mapper, mandatory);
+        public virtual PropertyMappingBuilder<T> map_property_groups_with<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter, PropertyMapper<TObj> mapper) {
+            return map_many_with<TObj, Properties>(name, getter, setter, mapper);
         }
 
-        public PropertyMapperBuilder<T> map_many_with<TNative, TElement>(string name, owned PropertyGetter<T, Enumerable<TNative>> getter, owned PropertySetter<T, Enumerable<TNative>> setter, Mapper<TNative, TElement> mapper, bool mandatory = true) {
-            add_mapping(new PropertyMapping<T>() {
+        public virtual PropertyMappingBuilder<T> map_many_with<TNative, TElement>(string name, owned PropertyGetter<T, Enumerable<TNative>> getter, owned PropertySetter<T, Enumerable<TNative>> setter, Mapper<TNative, TElement> mapper) {
+            var mapping = new PropertyMapping<T>() {
                 name = name,
-                mandatory = mandatory,
                 getter = (o) => new NativeElement<Elements>(getter(o).select<TElement>(i => mapper.map_from(i)).to_elements()),
                 setter = (o,d) => {
                     var collection = new Series<TNative>();
@@ -133,16 +152,16 @@ namespace Invercargill {
                     }
                     setter(o, collection.seal());
                 }
-            });
-            return this;
+            };
+            return new PropertyMappingBuilder<T>(this, mapping);;
         }
 
-        public PropertyMapperBuilder<T> set_constructor(ObjectConstructor<T> constructor) {
+        public virtual PropertyMapperBuilder<T> set_constructor(ObjectConstructor<T> constructor) {
             this.constructor = constructor;
             return this;
         }
 
-        public PropertyMapper<T> build() {
+        public virtual PropertyMapper<T> build() {
             return new PropertyMapper<T>(mappings, constructor);
         }
 
@@ -151,4 +170,66 @@ namespace Invercargill {
         }
     }
 
-}
+
+    public class PropertyMappingBuilder<T> : PropertyMapperBuilder<T> {
+        private PropertyMapperBuilder<T> inner;
+        private PropertyMapping<T> mapping;
+        internal PropertyMappingBuilder(PropertyMapperBuilder<T> builder, PropertyMapping<T> mapping) {
+            inner = builder;
+            this.mapping = mapping;
+        }
+
+        public PropertyMappingBuilder<T> null_when(owned PropertyPredicate<T> predicate) {
+            mapping.null_check = predicate;
+            return this;
+        }
+
+        public PropertyMappingBuilder<T> undefined_when(owned PropertyPredicate<T> predicate) {
+            mapping.undefined_check = predicate;
+            return this;
+        }
+
+        public PropertyMappingBuilder<T> when_null(owned PropertyDefaultSetter<T> setter) {
+            mapping.null_setter = setter;
+            return this;
+        }
+        
+        public PropertyMappingBuilder<T> when_undefined(owned PropertyDefaultSetter<T> setter) {
+            mapping.undefined_setter = setter;
+            return this;
+        }
+
+        public override PropertyMappingBuilder<T> map<TProp>(string name, owned PropertyGetter<T, TProp> getter, owned PropertySetter<T, TProp> setter) {
+            return inner.map<TProp>(name, (owned)getter, (owned)setter);
+        }
+
+        public override PropertyMappingBuilder<T> map_properties_with<TObj>(string name, owned PropertyGetter<T, TObj> getter, owned PropertySetter<T, TObj> setter, PropertyMapper<TObj> mapper) {
+            return inner.map_properties_with<TObj>(name, (owned)getter, (owned)setter, mapper);
+        }
+
+        public override PropertyMappingBuilder<T> map_with<TNative, TElement>(string name, owned PropertyGetter<T, TNative> getter, owned PropertySetter<T, TNative> setter, Mapper<TNative, TElement> mapper) {
+            return inner.map_with<TNative, TElement>(name, (owned)getter, (owned)setter, mapper);
+        }
+
+        public override PropertyMappingBuilder<T> map_many<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter) {
+            return inner.map_many<TObj>(name, (owned)getter, (owned)setter);
+        }
+
+        public override PropertyMappingBuilder<T> map_property_groups_with<TObj>(string name, owned PropertyGetter<T, Enumerable<TObj>> getter, owned PropertySetter<T, Enumerable<TObj>> setter, PropertyMapper<TObj> mapper) {
+            return inner.map_property_groups_with<TObj>(name, getter, setter, mapper);
+        }
+
+        public override PropertyMappingBuilder<T> map_many_with<TNative, TElement>(string name, owned PropertyGetter<T, Enumerable<TNative>> getter, owned PropertySetter<T, Enumerable<TNative>> setter, Mapper<TNative, TElement> mapper) {
+            return inner.map_many_with<TNative, TElement>(name, (owned)getter, (owned)setter, mapper);
+        }
+
+        public override PropertyMapperBuilder<T> set_constructor(ObjectConstructor<T> constructor) {
+            return inner.set_constructor(constructor);
+        }
+
+        public override PropertyMapper<T> build() {
+            return inner.build();
+        }
+
+    }
+}

+ 61 - 0
src/tests/Integration/PropertyMapper.vala

@@ -34,6 +34,55 @@ void property_mapper_tests() {
 
     });
 
+    Test.add_func("/invercargill/property_mapper/map_nullable_primitaves", () => {
+        var mapper = new PropertyMapperBuilder<MappedClassWithNulls>()
+            .set_constructor(() => new MappedClassWithNulls())
+            .map<bool?>("boolean", c => c.boolean, (c, v) => c.boolean = v)
+                .null_when(c => c.boolean == null)
+                .when_null(c => c.boolean = null)
+            .map<int?>("number", c => c.number, (c, v) => c.number = v)
+                .null_when(c => c.number == null)
+                .when_null(c => c.number = null)
+            .map<bool>("r-boolean", c => c.required_bool, (c, v) => c.required_bool = v)
+            .map<int>("r-number", c => c.required_number, (c, v) => c.required_number = v)
+            .map<bool?>("o-boolean", c => c.optional_bool, (c, v) => c.optional_bool = v)
+                .undefined_when(c => c.optional_bool == null)
+                .when_undefined(c => c.optional_bool = null)
+            .map<int?>("o-number", c => c.optional_number, (c, v) => c.optional_number = v)
+                .undefined_when(c => c.optional_number == null)
+                .when_undefined(c => c.optional_number = null)
+            .build();
+
+        var original = new MappedClassWithNulls();
+        original.number = null;
+        original.boolean = null;
+        original.required_bool = false;
+        original.required_number = 0;
+        original.optional_bool = null;
+        original.optional_number = null;
+
+        var properties = mapper.map_from(original);
+
+        try {
+            var copy = mapper.materialise(properties);
+    
+            assert(copy.number == null && copy.number != 0);
+            assert(copy.boolean == null && copy.boolean != false);
+            assert(copy.required_bool == false);
+            assert(copy.required_number == 0);
+
+            assert(properties["boolean"].is_null());
+            assert(properties["number"].is_null());
+            assert(!properties["r-boolean"].is_null());
+            assert(!properties["r-number"].is_null());
+            assert(!properties.has("o-boolean"));
+            assert(!properties.has("o-number"));
+        }
+        catch(Error e) {
+            assert_no_error(e);
+        }
+    });
+
 }
 
 private class MappedClass {
@@ -42,4 +91,16 @@ private class MappedClass {
     public string name { get; set; }
     public string not_mapped { get; set; }
 
+}
+
+
+private class MappedClassWithNulls {
+
+    public int? number { get; set; }   
+    public bool? boolean { get; set; }
+    public int required_number { get; set; }
+    public bool required_bool { get; set; }
+    public int? optional_number { get; set; }
+    public bool? optional_bool { get; set; }
+
 }

+ 1 - 1
src/tests/Speed/Fifo.vala

@@ -2,5 +2,5 @@ using Invercargill;
 
 void fifo_speed_test() {
     var fifo = new Fifo<int>();
-    speed_test_runner_int("Fifo", fifo, i => fifo.push(i), i => { i.iterate(j => fifo.push(j)); fifo.complete(); });
+    speed_test_runner_int("Fifo", fifo, i => fifo.push(i), i => { i.iterate(j => fifo.push(j)); fifo.unblock(); });
 }