1 /* 2 Copyright (c) 2013 Andrey Penechko 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license the "Software" to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 // Provides optimized ways to interact with FlexibleObject descendants properties, which have some 30 // built in properties. 31 // 32 // If property name is statically specified and there is built in property with that name then it will be bound to it without looking into property array, 33 // otherwise it will look into property array. This will speed up binding of properties which is accessible at compile time. 34 module anchovy.utils.flexibleobject.flexibleaccess; 35 36 import std.traits : hasMember; 37 import std.variant : Variant; 38 import anchovy.utils.flexibleobject.flexibleobject : FlexibleObject; 39 import anchovy.utils.flexibleobject.flexibleproperty : IProperty; 40 41 42 private template hasStaticProperty(T, string property) 43 { 44 enum hasStaticProperty = __traits(hasMember, T, property); 45 } 46 47 // ditto 48 static Variant getProperty(string propname, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 49 { 50 static if(hasStaticProperty!(FlexibleObjectType, propname)) 51 { 52 return mixin("w."~propname); 53 } 54 else 55 { 56 return w[propname]; 57 } 58 } 59 60 // 61 // 62 // 63 static T getPropertyAs(T)(string propname, FlexibleObject w) 64 { 65 return w[propname].get!T; 66 } 67 68 static T getPropertyAs(T)(string propname, FlexibleObject w, T defaultValue) 69 { 70 if (auto property = propname in (cast(FlexibleObject)w).properties) 71 { 72 return property.get!T; 73 } 74 else 75 { 76 return defaultValue; 77 } 78 } 79 80 // ditto 81 static T getPropertyAs(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 82 { 83 static if(hasStaticProperty!(FlexibleObjectType, propname)) 84 { 85 return mixin("w."~propname~".value.get!T"); 86 } 87 else 88 { 89 return w[propname].get!T; 90 } 91 } 92 93 // ditto 94 static T getPropertyAs(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w, T defaultValue) 95 { 96 static if(hasStaticProperty!(FlexibleObjectType, propname)) 97 { 98 return mixin("w."~propname~".value.get!T"); 99 } 100 else 101 { 102 if (auto property = propname in (cast(FlexibleObject)w).properties) 103 { 104 return property.get!T; 105 } 106 else 107 { 108 return defaultValue; 109 } 110 } 111 } 112 113 // 114 // 115 // 116 static T getPropertyAsBase(T)(string propname, FlexibleObject w) 117 { 118 auto property = w[propname]; 119 120 if (property.convertsTo!T) 121 return property.get!T; 122 else 123 return null; 124 } 125 126 // ditto 127 static T getPropertyAsBase(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 128 { 129 static if(hasStaticProperty!(FlexibleObjectType, propname)) 130 { 131 auto property = mixin("w."~propname~".value"); 132 133 if (property.convertsTo!T) 134 return property.get!T; 135 else 136 return null; 137 } 138 else 139 { 140 auto property = w[propname]; 141 142 if (property.convertsTo!T) 143 return property.get!T; 144 else 145 return null; 146 } 147 } 148 149 150 // Peek property value 151 static T* peekPropertyAs(T)(string propname, FlexibleObject w) 152 { 153 return w[propname].peek!T; 154 } 155 156 // ditto 157 static T* peekPropertyAs(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 158 { 159 static if(hasStaticProperty!(FlexibleObjectType, propname)) 160 { 161 return mixin("w."~propname~".value.peek!T"); 162 } 163 else 164 { 165 return w[propname].peek!T; 166 } 167 } 168 169 // 170 // 171 // 172 static T coercePropertyAs(T)(FlexibleObject w, string propname) 173 { 174 return w[propname].coerce!T; 175 } 176 177 static T coercePropertyAs(T)(FlexibleObject w, string propname, T defaultValue) 178 { 179 if (auto property = propname in (cast(FlexibleObject)w).properties) 180 { 181 return property.value.coerce!T; 182 } 183 else 184 { 185 return defaultValue; 186 } 187 } 188 189 // ditto 190 static T coercePropertyAs(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 191 { 192 static if(hasStaticProperty!(FlexibleObjectType, propname)) 193 { 194 return mixin("w."~propname~".value.coerce!T"); 195 } 196 else 197 { 198 return w[propname].coerce!T; 199 } 200 } 201 202 // ditto 203 static T coercePropertyAs(string propname, T, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w, T defaultValue) 204 { 205 static if(hasStaticProperty!(FlexibleObjectType, propname)) 206 { 207 return mixin("w."~propname~".value.coerce!T"); 208 } 209 else 210 { 211 if (auto property = propname in (cast(FlexibleObject)w).properties) 212 { 213 return (*property).value.coerce!T; 214 } 215 else 216 { 217 return defaultValue; 218 } 219 } 220 } 221 222 bool hasProperty(string propname, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w) 223 { 224 return (propname in (cast(FlexibleObject)w).properties) !is null; 225 } 226 227 bool hasProperty(FlexibleObjectType : FlexibleObject)(FlexibleObjectType w, string propname) 228 { 229 return (propname in (cast(FlexibleObject)w).properties) !is null; 230 } 231 232 // 233 // 234 // 235 static void setProperty(ValueType)(FlexibleObject w, string propname, ValueType value) 236 { 237 w[propname] = value; 238 } 239 240 // ditto 241 static void setProperty(string propname, ValueType, FlexibleObjectType : FlexibleObject)(FlexibleObjectType w, ValueType value) 242 { 243 static if(hasStaticProperty!(FlexibleObjectType, propname)) 244 { 245 auto property = mixin("w."~propname); 246 247 static if (is(ValueType:Variant)) 248 { 249 property.value = value; 250 } 251 else 252 { 253 property.value = Variant(value); 254 } 255 } 256 else 257 { 258 static if (is(ValueType:Variant)) 259 { 260 w[propname] = value; 261 } 262 else 263 { 264 w[propname] = Variant(value); 265 } 266 } 267 } 268 269 /// 270 void bindTo(IProperty source, IProperty dest, Variant delegate(Variant) converter) 271 { 272 auto handler = (FlexibleObject obj, Variant value){ 273 dest.value = converter(value); 274 }; 275 276 dest.value = converter(source.value); 277 278 source.valueChanged.connect(handler); 279 } 280 281 template isVariant(T) 282 { 283 enum isVariant = is(T == Variant); 284 } 285 286 import std.conv; 287 alias ExtractValues(Properties...) = ExtractValuesImpl!(0, Properties); 288 289 template ExtractValuesImpl(uint index, Properties...) 290 { 291 static if(Properties.length > 1) 292 enum ExtractValuesImpl = "sources[" ~ to!string(index) ~ "].value, " ~ ExtractValuesImpl!(index + 1, Properties[1..$]); 293 else 294 enum ExtractValuesImpl = "sources[" ~ to!string(index) ~ "].value"; 295 } 296 297 import std.algorithm; 298 import std.range; 299 import std.traits; 300 import std.typetuple; 301 /// creates one-way binding from multiple to one property 302 /// destination.pipeFrom((Variant a, Variant b)=> a+b, num1, num2); 303 void pipeFrom(ConverterType, P ...)(IProperty dest, ConverterType converter, P sources) 304 if (is(ReturnType!ConverterType == Variant) && 305 (ParameterTypeTuple!ConverterType).length == P.length && 306 is(NoDuplicates!(ParameterTypeTuple!ConverterType) == TypeTuple!Variant) && 307 is(NoDuplicates!P == TypeTuple!IProperty)) 308 { 309 auto handler = (FlexibleObject obj, Variant value){ 310 dest.value = mixin("converter("~ExtractValues!sources~")"); 311 }; 312 313 foreach(index, IProperty source; sources) 314 { 315 source.valueChanged.connect(handler); 316 } 317 318 dest.value = mixin("converter("~ExtractValues!sources~")"); 319 }