1 /** 2 License: 3 Boost Software License - Version 1.0 - August 17th, 2003 4 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 27 Authors: 28 Alexandru Ermicioi 29 **/ 30 module aermicioi.aedi_property_reader.core.document; 31 32 import aermicioi.aedi : Container, AediAllocatorAware = AllocatorAware, component, autowired, AllocatorAwareMixin, AediNotFoundException = NotFoundException; 33 import aermicioi.aedi_property_reader.convertor.exception : ConvertorException; 34 import aermicioi.aedi_property_reader.convertor.convertor; 35 import aermicioi.aedi_property_reader.convertor.placeholder; 36 import aermicioi.aedi.storage.wrapper; 37 import std.meta; 38 import std.conv; 39 import std.experimental.allocator; 40 import std.exception : enforce; 41 import aermicioi.aedi_property_reader.convertor.accessor; 42 import aermicioi.aedi_property_reader.convertor.type_guesser; 43 import std.algorithm; 44 import std.array; 45 import std.experimental.logger; 46 import std.traits : Unqual; 47 48 /** 49 Locator for properties that weren't registered in container, however are present in the root document. 50 51 Since the type of the properties is not known at the runtime, 52 this locator will employ a type guesser to guess the type of element 53 it is requested from root document. If guessing fails, then raw type will 54 be returned per type guesser semantics. 55 **/ 56 @component 57 @safe class DocumentLocator(DocumentType) : Container, AediAllocatorAware!() 58 { 59 60 private { 61 Convertor convertor_; 62 PropertyAccessor!(DocumentType, Object) accessor_; 63 TypeGuesser!Object guesser_; 64 Object[string] components; 65 66 TypeWrapper document; 67 } 68 69 mixin AllocatorAwareMixin!(typeof(this)); 70 71 public { 72 73 /** 74 Constructor for a container for document. 75 76 Params: 77 document = document stored in container. 78 convertor = convertor storing any other one registered in document container. 79 **/ 80 @autowired 81 this(DocumentType document, RCIAllocator allocator = theAllocator) { 82 this.document = TypeWrapper(document); 83 this.allocator = allocator; 84 } 85 86 @property { 87 88 /** 89 Set convertor 90 91 Params: 92 convertor = combined convertor used in converting from type to another 93 94 Returns: 95 typeof(this) 96 **/ 97 @autowired 98 typeof(this) convertor(Convertor convertor) @safe nothrow pure { 99 this.convertor_ = convertor; 100 101 return this; 102 } 103 104 /** 105 Get convertor 106 107 Returns: 108 CombinedConvertor 109 **/ 110 inout(Convertor) convertor() @safe nothrow pure inout { 111 return this.convertor_; 112 } 113 114 /** 115 Set guesser 116 117 Params: 118 guesser = guesser uset to guess the D type of document. 119 120 Returns: 121 typeof(this) 122 **/ 123 @autowired 124 typeof(this) guesser(TypeGuesser!Object guesser) @safe nothrow pure { 125 this.guesser_ = guesser; 126 127 return this; 128 } 129 130 /** 131 Get guesser 132 133 Returns: 134 TypeGuesser!Object 135 **/ 136 inout(TypeGuesser!Object) guesser() @safe nothrow pure inout { 137 return this.guesser_; 138 } 139 140 /** 141 Set accessor 142 143 Params: 144 accessor = accessor used to navigate through document 145 Returns: 146 typeof(this) 147 **/ 148 @autowired 149 typeof(this) accessor(PropertyAccessor!(DocumentType, Object) accessor) @safe nothrow pure { 150 this.accessor_ = accessor; 151 152 return this; 153 } 154 155 /** 156 Get accessor 157 158 Returns: 159 PropertyAccessor!(DocumentType, Object) 160 **/ 161 inout(PropertyAccessor!(DocumentType, Object)) accessor() @safe nothrow pure inout { 162 return this.accessor_; 163 } 164 } 165 166 /** 167 Get a component that is associated with key. 168 169 Params: 170 identity = the element id. 171 172 Throws: 173 NotFoundException in case if the element wasn't found. 174 175 Returns: 176 Object element if it is available. 177 **/ 178 Object get(string identity) @trusted { 179 debug(trace) trace("Searching for \"", identity, '"'); 180 181 if (identity in components) { 182 debug(trace) trace("Found already converted component, supplying it."); 183 184 return components[identity]; 185 } 186 187 if (!this.accessor.has(this.document.payload, identity)) { 188 throw new AediNotFoundException( 189 text("Could not find ${identity} in document of type ", typeid(DocumentType)), identity 190 ); 191 } 192 193 Object document = this.accessor.access(this.document.payload, identity); 194 195 debug(trace) trace("Attempting to guess the desired type."); 196 TypeInfo guess = this.guesser.guess(document); 197 198 debug(trace) trace("Guessed ", guess, " type, commencing conversion"); 199 return convertor.convert(document, guess, this.allocator); 200 } 201 202 /** 203 Check if an element is present in Locator by key id. 204 205 Note: 206 This check should be done for elements that locator actually contains, and 207 not in chained locator. 208 Params: 209 identity = identity of element. 210 211 Returns: 212 bool true if an element by key is present in Locator. 213 **/ 214 bool has(string identity) inout @trusted { 215 return (identity in components) || this.accessor.has((cast(TypeWrapper) this.document).payload, identity); 216 } 217 218 /** 219 Sets up the internal state of container. 220 221 Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains). 222 **/ 223 typeof(this) instantiate() { 224 return this; 225 } 226 227 /** 228 Destruct all managed components. 229 230 Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components 231 by it. 232 **/ 233 typeof(this) terminate() @trusted { 234 foreach (id, component; this.components) { 235 this.convertor.destruct(null, component, allocator); 236 } 237 238 return this; 239 } 240 } 241 242 private { 243 /** 244 A dirty hack used to evade tagged algebraic or any other 245 custom implementation of cast while stripping out constness/immutability of the payload in has method 246 **/ 247 static struct TypeWrapper { 248 DocumentType payload; 249 } 250 } 251 }