1 /*! \file mdobject.cpp
2 * \brief Implementation of classes that define metadata objects
3 *
4 * Class MDObject holds info about a specific metadata object
5 *<br><br>
6 * Class MDOType holds the definition of MDObjects derived from
7 * the XML dictionary.
8 *
9 * \version $Id: mdobject.cpp,v 1.25 2007/10/10 15:45:35 matt-beard Exp $
10 *
11 */
12 /*
13 * Copyright (c) 2003, Matt Beard
14 *
15 * This software is provided 'as-is', without any express or implied warranty.
16 * In no event will the authors be held liable for any damages arising from
17 * the use of this software.
18 *
19 * Permission is granted to anyone to use this software for any purpose,
20 * including commercial applications, and to alter it and redistribute it
21 * freely, subject to the following restrictions:
22 *
23 * 1. The origin of this software must not be misrepresented; you must
24 * not claim that you wrote the original software. If you use this
25 * software in a product, an acknowledgment in the product
26 * documentation would be appreciated but is not required.
27 *
28 * 2. Altered source versions must be plainly marked as such, and must
29 * not be misrepresented as being the original software.
30 *
31 * 3. This notice may not be removed or altered from any source
32 * distribution.
33 */
34
35 #include <mxflib/mxflib.h>
36
37 #include <stdarg.h>
38
39 using namespace mxflib;
40
41
42 //! Static flag to say if dark metadata sets that appear to be valid KLV 2x2 sets should be parsed
43 bool MDObject::ParseDark = false;
44
45 //! Static primer to use for index tables
46 PrimerPtr MDOType::StaticPrimer;
47
48 //! List of all existing symbol spaces to allow full searching
49 SymbolSpaceMap SymbolSpace::AllSymbolSpaces;
50
51 //! Global SymbolSpace for all MXFLib's normal symbols
52 SymbolSpacePtr mxflib::MXFLibSymbols = new SymbolSpace("http://www.freemxf.org/MXFLibSymbols");
53
54 //! Translator function to translate unknown ULs to object names
55 MDObject::ULTranslator MDObject::UL2NameFunc = NULL;
56
57
58 //! Build a Primer object for the current dictionary
59 /*! This primer has the mappings of tag to UL from the dictionary
60 * \param SetStatic - If true the StaticPrimer will be set to this new primer
61 */
MakePrimer(bool SetStatic)62 PrimerPtr MDOType::MakePrimer(bool SetStatic /*=false*/)
63 {
64 PrimerPtr Ret = new Primer;
65
66 // DRAGONS: Some debug!
67 MDOTypeList::iterator it = AllTypes.begin();
68 while(it != AllTypes.end())
69 {
70 if((*it)->Key.Size == 2)
71 {
72 Tag ThisTag = (*it)->Key.Data[1] + ((*it)->Key.Data[0] << 8);
73
74 // Don't barf if the dictionary entry is invalid!
75 if((*it)->GlobalKey.Size != 16)
76 {
77 error("Dictionary entry for \"%s\" has a 2-byte tag, but doesn't have a 16-byte UL\n", (*it)->FullName().c_str());
78 }
79 else
80 {
81 mxflib::UL ThisUL((*it)->GlobalKey.Data);
82 Ret->insert(Primer::value_type(ThisTag, ThisUL));
83 }
84 }
85
86 it++;
87 }
88
89 // Replace existing StaticPrimer if requested
90 if(SetStatic) StaticPrimer = Ret;
91
92 return Ret;
93 }
94
95
96 //! Builds an MDOType
97 /*! This constructor is private so the ONLY way to create
98 * new MDOTypes from outside this class is via member methods
99 */
MDOType(void)100 MDOType::MDOType(void)
101 {
102 // Initialise dictionary data
103 KeyFormat = DICT_KEY_NONE;
104 LenFormat = DICT_LEN_BER;
105 minLength = 0;
106 maxLength = (unsigned int)-1;
107 Use = DICT_USE_NONE;
108 RefType = DICT_REF_NONE;
109
110 // Initially assume not a container
111 ContainerType = NONE;
112 }
113
114
115 //! Find the MDOType object that defines a named type
116 /*! \return Pointer to the object
117 * \return NULL if there is no type of that name
118 * \note If BaseType contains a qualified name of the format "symbolspace::name" then only
119 * the specified symbolspace is searched
120 */
Find(std::string BaseType,SymbolSpacePtr & SymSpace,bool SearchAll)121 MDOTypePtr MDOType::Find(std::string BaseType, SymbolSpacePtr &SymSpace, bool SearchAll /*=false*/)
122 {
123 // Check for a symbol space given in the name
124 size_type Pos = BaseType.find("::");
125
126 if(Pos != std::string::npos)
127 {
128 SymbolSpacePtr Sym;
129
130 // DRAGONS: A zero length namespace represents the default namespace
131 if(Pos == 0) Sym = MXFLibSymbols;
132 else Sym = SymbolSpace::FindSymbolSpace(BaseType.substr(0, Pos));
133
134 if(Sym)
135 {
136 ULPtr ThisUL = Sym->Find(BaseType.substr(Pos+2), false);
137 if(ThisUL) return MDOType::Find(ThisUL);
138 }
139 }
140 else
141 {
142 ULPtr ThisUL = SymSpace->Find(BaseType, SearchAll);
143 if(ThisUL) return MDOType::Find(ThisUL);
144 }
145
146 return NULL;
147 }
148
149
150 //! Find the MDOType object that defines a type with a specified UL
151 /*! \return Pointer to the object
152 * \return NULL if there is no type with that UL
153 */
Find(const UL & BaseUL)154 MDOTypePtr MDOType::Find(const UL& BaseUL)
155 {
156 MDOTypePtr theType;
157
158 std::map<UL, MDOTypePtr>::iterator it = ULLookup.find(BaseUL);
159
160 if(it != ULLookup.end())
161 {
162 theType = (*it).second;
163 }
164 else
165 {
166 // If the exact match is not found try a version-less lookup by changing the version number to 1
167 if((BaseUL.GetValue()[0] == 0x06) && (BaseUL.GetValue()[1] == 0x0e) && (BaseUL.GetValue()[2] == 0x2b) && (BaseUL.GetValue()[3] == 0x34))
168 {
169 UL Ver1UL = BaseUL;
170 Ver1UL.Set(7, 1);
171
172 std::map<UL, MDOTypePtr>::iterator it2 = ULLookupVer1.find(Ver1UL);
173 if(it2 != ULLookupVer1.end())
174 {
175 theType = (*it2).second;
176 }
177 }
178 }
179
180 return theType;
181 }
182
183
184 //! Find the MDOType object that defines a type with a specified Tag
185 /*! The tag is looked up in the supplied primer
186 * \note if BasePrimer is NULL then the static primer is searched
187 * \return Pointer to the object
188 * \return NULL if there is no type with that tag
189 */
Find(Tag BaseTag,PrimerPtr BasePrimer)190 MDOTypePtr MDOType::Find(Tag BaseTag, PrimerPtr BasePrimer)
191 {
192 MDOTypePtr theType;
193
194 // Search the static primer by default
195 if(!BasePrimer) BasePrimer = GetStaticPrimer();
196
197 // Search the primer
198 Primer::iterator it = BasePrimer->find(BaseTag);
199
200 // Return NULL if not in the primer
201 if(it == BasePrimer->end()) return NULL;
202
203 // Now search on the located UL
204 return MDOType::Find((*it).second);
205 }
206
207
208
209 //! MDObject named constructor
210 /*! Builds a "blank" metadata object of a named type
211 * \note packs are built with defaut values
212 */
MDObject(std::string BaseType,SymbolSpacePtr & SymSpace)213 MDObject::MDObject(std::string BaseType, SymbolSpacePtr &SymSpace /*=MXFLibSymbols*/ )
214 {
215 ULPtr ThisUL = SymSpace->Find(BaseType);
216 if(ThisUL) Type = MDOType::Find(ThisUL);
217 if(Type)
218 {
219 ObjectName = Type->Name();
220 }
221 else
222 {
223 error("Metadata object type \"%s\" doesn't exist\n", BaseType.c_str());
224
225 Type = MDOType::Find("Unknown");
226
227 ASSERT(Type);
228
229 ObjectName = "Unknown " + BaseType;
230 }
231
232 IsConstructed = true;
233 ParentOffset = 0;
234 KLSize = 0;
235 Parent = NULL;
236 ParentFile = NULL;
237 TheUL = Type->GetUL();
238 TheTag = 0;
239
240 Outer = NULL;
241
242 // Initialise the new object
243 Init();
244 }
245
246
247 //! MDObject typed constructor
248 /*! Builds a "blank" metadata object of a specified type
249 * \note packs are built with defaut values
250 */
MDObject(MDOTypePtr BaseType)251 MDObject::MDObject(MDOTypePtr BaseType) : Type(BaseType)
252 {
253 ObjectName = BaseType->Name();
254
255 IsConstructed = true;
256 ParentOffset = 0;
257 KLSize = 0;
258 Parent = NULL;
259 ParentFile = NULL;
260 TheUL = Type->GetUL();
261 TheTag = 0;
262
263 Outer = NULL;
264
265 // Initialise the new object
266 Init();
267 }
268
269
270 //! MDObject UL based constructor body
271 /*! Builds a "blank" metadata object of a specified type
272 * \note packs are built with default values
273 *
274 * \note TheUL must be set before calling
275 */
ULCtor(void)276 void MDObject::ULCtor(void)
277 {
278 Type = MDOType::Find(TheUL);
279 if(Type)
280 {
281 ObjectName = Type->Name();
282 }
283 else
284 {
285 Type = MDOType::Find("Unknown");
286
287 if(UL2NameFunc)
288 {
289 ObjectName = UL2NameFunc(TheUL,NULL);
290 }
291 else
292 {
293 // TODO: Needs to have a more complete name
294 ObjectName = "Unknown " + TheUL->GetString();
295 }
296
297 ASSERT(Type);
298
299 // Shall we try and parse this?
300 if(ParseDark)
301 {
302 const UInt8 Set2x2[6] = { 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x53 };
303 if(memcmp(Set2x2, TheUL->GetValue(), 6) == 0)
304 {
305 MDOTypePtr DefaultType = MDOType::Find("DefaultObject");
306 if(DefaultType) Type = DefaultType;
307 }
308 }
309 }
310
311 IsConstructed = true;
312 ParentOffset = 0;
313 KLSize = 0;
314 Parent = NULL;
315 ParentFile = NULL;
316 TheTag = 0;
317
318 Outer = NULL;
319
320 // Initialise the new object
321 Init();
322 }
323
324
325 //! MDObject typed constructor
326 /*! Builds a "blank" metadata object of a specified type
327 * \note packs are built with defaut values
328 */
MDObject(Tag BaseTag,PrimerPtr BasePrimer)329 MDObject::MDObject(Tag BaseTag, PrimerPtr BasePrimer)
330 {
331 // Try and find the tag in the primer
332 if(BasePrimer)
333 {
334 Primer::iterator it = BasePrimer->find(BaseTag);
335
336 // Didn't find it!!
337 if(it == BasePrimer->end())
338 {
339 error("Metadata object with Tag \"%s\" doesn't exist in specified Primer\n", Tag2String(BaseTag).c_str());
340
341 // See if we know this tag anyway
342 Type = MDOType::Find(BaseTag, NULL);
343
344 // If it is a "known" static then use it (but still give the error)
345 if(Type) TheUL = Type->GetTypeUL();
346 }
347 else
348 {
349 // It was found in the primer, so lookup the type from the UL
350 TheUL = new UL((*it).second);
351 Type = MDOType::Find(TheUL);
352 }
353 }
354 else
355 {
356 // No primer supplied - see if we know this tag anyway
357 Type = MDOType::Find(BaseTag, NULL);
358 if(Type) TheUL = Type->GetTypeUL();
359 }
360
361 // If it was unknown build an "Unknown" and set a meaningful name
362 if(!Type)
363 {
364 Type = MDOType::Find("Unknown");
365
366 ASSERT(Type);
367
368 if(UL2NameFunc)
369 {
370 ObjectName = UL2NameFunc(TheUL, &BaseTag);
371 }
372 else
373 {
374 if(TheUL)
375 {
376 // Tag found, but UL unknown
377 // FIXME: Needs to have a more complete name
378 ObjectName = "Unknown " + Tag2String(BaseTag) + std::string(" ") + TheUL->GetString();
379 }
380 else
381 {
382 // Tag not found, build a blank UL
383 // FIXME: Needs to have a more complete name
384 ObjectName = "Unknown " + Tag2String(BaseTag);
385
386 // We will need a UL, so try just using the generic "Unknown" UL
387 TheUL = Type->GetTypeUL();
388 }
389 }
390 }
391 else
392 {
393 ObjectName = Type->Name();
394 }
395
396 IsConstructed = true;
397 ParentOffset = 0;
398 KLSize = 0;
399 Parent = NULL;
400 ParentFile = NULL;
401 TheTag = BaseTag;
402
403 Outer = NULL;
404
405 // Initialise the new object
406 Init();
407 }
408
409
410 //! Second part of MDObject constructors
411 /*! Builds a "blank" metadata object
412 * \note packs are built with defaut values
413 */
Init(void)414 void MDObject::Init(void)
415 {
416 SetModified(true);
417
418 switch(Type->GetContainerType())
419 {
420 case NONE:
421 // If it isn't a container build the basic item
422 {
423 // If we are trying to build a type that is not defined, build it as an "Unknown"
424 MDTypePtr ValType = Type->GetValueType();
425 if(!ValType) ValType = MDType::Find("UnknownType");
426 ASSERT(ValType);
427
428 Value = new MDValue(ValType);
429
430 if(ValType->EffectiveClass() == TYPEARRAY)
431 {
432 // Build the minimum size array
433 Value->Resize(Type->GetMinLength());
434 }
435 }
436
437 break;
438
439 case PACK:
440 // If it's a pack build all items
441 {
442 Value = NULL;
443
444 MDOTypeList::const_iterator it = Type->GetChildList().begin();
445 while(it != Type->GetChildList().end())
446 {
447 MDObjectPtr NewItem = new MDObject(*it);
448 NewItem->SetDefault();
449 insert(NewItem);
450
451 it++;
452 }
453 }
454 break;
455
456 case BATCH:
457 case ARRAY:
458 default:
459 {
460 Value = NULL;
461 }
462 }
463 }
464
465
466 #if 0 // WORK IN PROGRESS...
467 //! Add an empty numbered child to an MDObject continer and return a pointer to it
468 /*! \return NULL if this object does not take numbered children
469 * \note If a child with this index already exists it is returned rather than adding a new one
470 */
471 MDObjectPtr MDObject::AddChild(UInt32 ChildIndex)
472 {
473 MDObjectPtr Ret;
474
475 SetModified(true);
476
477 // Try and find an existing child (if replacing)
478 Ret = Child(ChildIndex);
479
480 // Only add a new one if we didn't find it
481 if(!Ret)
482 {
483 // Find the child definition
484 MDOType::iterator it = Type->begin();
485
486 // Return NULL if not found
487 if(it == Type->end()) return Ret;
488
489 // Build a new item of the correct type
490 Ret = new MDObject((*it).second);
491
492 // Add the new child
493 AddChildInternal(Ret, false);
494 }
495
496 // Return smart pointer to the new object
497 return Ret;
498 }
499 #endif // 0
500
501
502 //! Add an empty named child to an MDObject continer and return a pointer to it
503 /*! If Replace is true (or not supplied) and a child of this name already
504 * exists a pointer to that child is returned but the value is not changed.
505 * ChildName is a symbol to be located in the given SymbolSpace - if no SymbolSpace is specifed the default MXFLib space is used
506 * \return NULL if it is not a valid child to add to this type of container
507 * \note If you want to add an child that is non-standard (i.e. not listed
508 * as a child in the container's MDOType then you must build the child
509 * then add it with AddChild(MDObjectPtr)
510 */
AddChild(std::string ChildName,SymbolSpacePtr & SymSpace,bool Replace)511 MDObjectPtr MDObject::AddChild(std::string ChildName, SymbolSpacePtr &SymSpace /*=MXFLibSymbols*/, bool Replace /*=true*/)
512 {
513 MDObjectPtr Ret;
514
515 SetModified(true);
516
517 // Try and find an existing child (if replacing)
518 if(Replace) Ret = Child(ChildName); else Ret = NULL;
519
520 // Only add a new one if we didn't find it
521 if(!Ret)
522 {
523 // Find the child definition
524 MDOTypePtr ChildType = Type->Child(ChildName);
525
526 // If not a known child of this type try and add the named item anyway
527 if(!ChildType)
528 {
529 ULPtr ChildUL = SymSpace->Find(ChildName, false);
530
531 // If not a known name return NULL
532 if(!ChildUL) return Ret;
533
534 // Insert a new item of the correct type
535 Ret = new MDObject(ChildUL);
536 }
537 else
538 {
539 // Insert a new item of the correct type
540 Ret = new MDObject(ChildType);
541 }
542
543 if(Ret) insert(Ret);
544 }
545
546 // Return smart pointer to the new object
547 return Ret;
548 }
549
550
551 //! Add an empty child of a specified type to an MDObject continer and return a pointer to it
552 /*! If Replace is true (or not supplied) and a child of this type already
553 * exists a pointer to that child is returned but the value is not changed.
554 * \return NULL if it is not a valid child to add to this type of container
555 * \note If you want to add an child that is non-standard (i.e. not listed
556 * as a child in the container's MDOType then you must build the child
557 * then add it with AddChild(MDObjectPtr)
558 */
AddChild(MDOTypePtr ChildType,bool Replace)559 MDObjectPtr MDObject::AddChild(MDOTypePtr ChildType, bool Replace /*=true*/)
560 {
561 MDObjectPtr Ret;
562
563 SetModified(true);
564
565 // Try and find an existing child (if replacing)
566 if(Replace) Ret = Child(ChildType); else Ret = NULL;
567
568 // Only add a new one if we didn't find it
569 if(!Ret)
570 {
571 // Insert a new item of the correct type
572 Ret = new MDObject(ChildType);
573 insert(Ret);
574 }
575
576 // Return smart pointer to the new object
577 return Ret;
578 }
579
580
581 //! Add a new child MDObject of the specified type
582 /*! DRAGONS: This code searches for child entries that match rather than simply doing a full UL-lookup as there
583 * may be cases where the same UL is used somewhere else but is not compatible (such as in a pack definition
584 * whereas are adding to a local set to need a valid local tag)
585 */
AddChild(const UL & ChildType,bool Replace)586 MDObjectPtr MDObject::AddChild(const UL &ChildType, bool Replace /*=true*/)
587 {
588 MDObjectPtr Ret;
589
590 SetModified(true);
591
592 // Try and find an existing child (if replacing)
593 if(Replace) Ret = Child(ChildType); else Ret = NULL;
594
595 // Only add a new one if we didn't find it
596 if(!Ret)
597 {
598 // Find the child definition
599 MDOType::iterator it = Type->begin();
600 while(it != Type->end())
601 {
602 if(((*it).second->GetUL()) && (*((*it).second->GetUL()) == ChildType)) break;
603 it++;
604 }
605
606 // If not a known child of this type try and add the specified item anyway
607 if(it == Type->end())
608 {
609 // Insert a new item of the correct type
610 Ret = new MDObject(ChildType);
611 }
612 else
613 {
614 // Insert a new item of the correct type
615 Ret = new MDObject((*it).second);
616 }
617
618 if(Ret) insert(Ret);
619 }
620
621 // Return smart pointer to the new object
622 return Ret;
623 }
624
625 //! Add a new child MDObject to a vector
626 /*! \note The type of the object added is automatic.
627 * If the vector is of multiple members the next type will be chosen by the number of members currently
628 * in the array, so if there are 3 sub member types the 7th entry will be type 1 [ 7 = (2*3) + 1 ]
629 *
630 * \note This version of AddChild will <b>not</b> replace duplicates, it always appends
631 */
AddChild(void)632 MDObjectPtr MDObject::AddChild(void)
633 {
634 // Number of sub items types declares
635 int SubCount = (int)Type->size();
636
637 if(!SubCount)
638 {
639 error("Attempted to AddChild() to %s which has no child types declared\n", FullName().c_str());
640 return NULL;
641 }
642
643 // Number sub items currently existing
644 int ItemCount = (int)size();
645
646 // The sub-type number (zero based) for the next item to add
647 int ItemType = ItemCount % SubCount;
648
649 // Get an iterator that points at the required type
650 MDOType::iterator it = Type->begin();
651 while(ItemType--) it++;
652
653 // Add a child of this type
654 return AddChild((*it).second, false);
655 }
656
657 //! Add a given MDObject to an MDObject continer
658 /*! \return pointer to the object added (for compatibility with other ADDChild funcs)
659 * \return NULL if there was an error
660 * \note If there is already a child of this type it is replaces if Replace = true
661 */
AddChild(MDObjectPtr & ChildObject,bool Replace)662 MDObjectPtr MDObject::AddChild(MDObjectPtr &ChildObject, bool Replace /* = false */)
663 {
664 SetModified(true);
665
666 return AddChildInternal(ChildObject, Replace);
667 }
668
669
670 //! Same as AddChild(), but does not set "Modified"
671 /*! \return pointer to the object added (for compatibility with other ADDChild funcs)
672 * \return NULL if there was an error
673 * This function is used when reading an objects children
674 * \note If there is already a child of this type it is replaces if Replace = true
675 */
AddChildInternal(MDObjectPtr ChildObject,bool Replace)676 MDObjectPtr MDObject::AddChildInternal(MDObjectPtr ChildObject, bool Replace /* = false */)
677 {
678 // If replacing, remove any existing children of this type
679 if (Replace) RemoveChild(ChildObject->Type);
680
681 // Insert the new item at the end
682 insert(ChildObject);
683
684 return ChildObject;
685 }
686
687
688 //! Remove any children with a specified name from an MDObject continer
RemoveChild(std::string ChildName)689 void MDObject::RemoveChild(std::string ChildName)
690 {
691 SetModified(true);
692
693 MDObjectULList::iterator it = begin();
694 while(it != end())
695 {
696 if((*it).second->Name() == ChildName)
697 {
698 erase(it);
699
700 // Restart the scan - DRAGONS: Not very efficient
701 it = begin();
702 }
703 else
704 {
705 it++;
706 }
707 }
708 }
709
710
711 //! Remove any children of a specified type from an MDObject continer
RemoveChild(MDOTypePtr & ChildType)712 void MDObject::RemoveChild(MDOTypePtr &ChildType)
713 {
714 // Note that we cannot rely on removing by name as names are changeable
715
716 SetModified(true);
717
718 MDObjectULList::iterator it = begin();
719 while(it != end())
720 {
721 if((*it).second->Type == ChildType)
722 {
723 erase(it);
724
725 // Restart the scan - DRAGONS: Not very efficient
726 it = begin();
727 }
728 else
729 {
730 it++;
731 }
732 }
733 }
734
735
736 //! Remove a specified object from an MDObject continers children lists
737 /*! If the object is not an child of the container nothing is done
738 */
RemoveChild(MDObjectPtr ChildObject)739 void MDObject::RemoveChild(MDObjectPtr ChildObject)
740 {
741 SetModified(true);
742
743 MDObjectULList::iterator it = begin();
744 while(it != end())
745 {
746 if((*it).second == ChildObject)
747 {
748 erase(it);
749 return;
750 }
751 it++;
752 }
753 }
754
755
756 //! MDObject destructor
757 /*! Frees the data of the object if it exists
758 */
~MDObject()759 MDObject::~MDObject()
760 {
761 // Free any memory used
762 }
763
764
765
766 //! Access named sub-item within a compound MDObject
767 /*! If the child does not exist in this item then NULL is returned
768 * even if it is a valid child to have in this type of container
769 *
770 * DRAGONS: This doesn't work well with SmartPtrs
771 * so member function Child() is also available
772 */
operator [](std::string ChildName)773 MDObjectPtr MDObject::operator[](std::string ChildName)
774 {
775 MDObjectULList::iterator it = begin();
776 while(it != end())
777 {
778 if((*it).second->Name() == ChildName)
779 {
780 return (*it).second;
781 }
782 it++;
783 }
784
785 return NULL;
786 }
787
788
789 //! Access sub-item of the specified type within a compound MDObject
790 /*! If the child does not exist in this item then NULL is returned
791 * even if it is a valid child to have in this type of container
792 *
793 * DRAGONS: This doesn't work well with SmartPtrs
794 * so member function Child() is also available
795 */
operator [](MDOTypePtr ChildType)796 MDObjectPtr MDObject::operator[](MDOTypePtr ChildType)
797 {
798 MDObjectULList::iterator it = begin();
799 while(it != end())
800 {
801 if((*it).second->Type == ChildType)
802 {
803 return (*it).second;
804 }
805 it++;
806 }
807
808 return NULL;
809 }
810
811
812 //! Access sub-item of the specified type within a compound MDObject
813 /*! If the child does not exist in this item then NULL is returned
814 * even if it is a valid child to have in this type of container
815 *
816 * DRAGONS: This doesn't work well with SmartPtrs
817 * so member function Child() is also available
818 */
operator [](const UL & ChildType)819 MDObjectPtr MDObject::operator[](const UL &ChildType)
820 {
821 MDObjectULList::iterator it = begin();
822 while(it != end())
823 {
824 if((*it).first == ChildType)
825 {
826 return (*it).second;
827 }
828 it++;
829 }
830
831 return NULL;
832 }
833
834
835 //! Find all sub-items within a compound MDObject of a named type
ChildList(std::string ChildName)836 MDObjectListPtr MDObject::ChildList(std::string ChildName)
837 {
838 MDObjectListPtr Ret = new MDObjectList;
839 MDObjectULList::iterator it = begin();
840
841 while(it != end())
842 {
843 if((*it).second->Name() == ChildName)
844 {
845 // Add this object to the list
846 Ret->push_back((*it).second);
847 }
848 it++;
849 }
850
851 return Ret;
852 }
853
854
855 //! Find all sub-items within a compound MDObject of a named type
ChildList(MDOTypePtr ChildType)856 MDObjectListPtr MDObject::ChildList(MDOTypePtr ChildType)
857 {
858 MDObjectListPtr Ret = new MDObjectList;
859 MDObjectULList::iterator it = begin();
860
861 while(it != end())
862 {
863 if((*it).second->Type == ChildType)
864 {
865 // Add this object to the list
866 Ret->push_back((*it).second);
867 }
868 it++;
869 }
870
871 return Ret;
872 }
873
874
875 //! Find all sub-items within a compound MDObject of a specified type
ChildList(const UL & ChildType)876 MDObjectListPtr MDObject::ChildList(const UL &ChildType)
877 {
878 MDObjectListPtr Ret = new MDObjectList;
879 MDObjectULList::iterator it = begin();
880
881 while(it != end())
882 {
883 if((*it).first == ChildType)
884 {
885 // Add this object to the list
886 Ret->push_back((*it).second);
887 }
888 it++;
889 }
890
891 return Ret;
892 }
893
894
895 //! Read value from a buffer
896 /*! Note that collection headers are handled here rather than in the MDValue
897 * because MDValue objects don't differentiate. A primer must be supplied for reading sets
898 *
899 * DRAGONS: This function is bloated and should be split up
900 *
901 * \return Number of bytes read
902 */
ReadValue(const UInt8 * Buffer,size_t Size,PrimerPtr UsePrimer)903 size_t MDObject::ReadValue(const UInt8 *Buffer, size_t Size, PrimerPtr UsePrimer /*=NULL*/)
904 {
905 size_t Bytes = 0;
906 size_t Count = 0;
907 UInt32 ItemSize = 0;
908
909 SetModified(false);
910
911 switch(Type->GetContainerType())
912 {
913 case NONE:
914 return Value->ReadValue(Buffer, Size);
915
916 case BATCH:
917 {
918 if( Size >= 4 )
919 {
920 Count = GetU32(Buffer);
921 Buffer += 4;
922 Bytes = 4;
923 Size -= 4;
924 }
925 else
926 {
927 error("Malformed batch found in %s at 0x%s in %s - total bytes = %u\n",
928 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str(),
929 Size);
930
931 Count = 0;
932 Bytes = Size;
933 Size = 0;
934 }
935
936 if( Size >= 4 )
937 {
938 ItemSize = GetU32(Buffer);
939 Buffer += 4;
940 Bytes += 4;
941 Size -= 4;
942 }
943 else
944 {
945 error("Malformed batch found in %s at 0x%s in %s - total bytes = %u\n",
946 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str(),
947 Bytes + Size);
948
949 ItemSize = 0;
950 Bytes += Size;
951 Size = 0;
952 }
953
954 if((ItemSize*Count) != Size)
955 {
956 error("Malformed batch found in %s at 0x%s in %s - item size = %u, count = %u, but bytes = %u\n",
957 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str(),
958 ItemSize, Count, Size);
959
960 // Prevent us reading off the end of the buffer
961 if(Size < (ItemSize*Count)) Count = Size / ItemSize;
962 }
963
964 if(Count) Size = ItemSize; else Size = 0;
965
966 // Don't try and read an empty batch
967 if(Count == 0) return Bytes;
968 }
969 // Fall through and process as an array
970
971 case ARRAY:
972 {
973 if(Type->empty())
974 {
975 error("Object %s at 0x%s in %s is a multiple, but has no contained types\n", FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
976 return Bytes;
977 }
978
979 // Start with no children
980 clear();
981
982 // Find the first (or only) child type
983 MDOTypeList::const_iterator it = Type->GetChildList().begin();
984
985 size_t ChildCount = Type->size();
986 while(Size || Count)
987 {
988 MDObjectPtr NewItem = new MDObject(*it);
989
990 ASSERT(NewItem);
991
992 NewItem->Parent = this;
993 NewItem->ParentOffset = Bytes;
994 NewItem->KLSize = 0;
995
996 size_t ThisBytes = NewItem->ReadValue(Buffer, Size);
997
998 Bytes += ThisBytes;
999 Buffer += ThisBytes;
1000 if(ThisBytes > Size) Size = 0; else Size -= ThisBytes;
1001 insert(NewItem);
1002
1003
1004 bool ItemStart = true;
1005
1006 // If this array has multiple children, get the next type
1007 if(ChildCount > 1)
1008 {
1009 it++;
1010
1011 if(it == Type->GetChildList().end()) it = Type->GetChildList().begin();
1012 else ItemStart = false;
1013 }
1014
1015 // If processing a batch, set up for the next item
1016 if(ItemStart && (Count != 0))
1017 {
1018 if(--Count) Size = ItemSize; else break;
1019 }
1020 }
1021
1022 if((ChildCount > 1) && (it != Type->GetChildList().begin()))
1023 {
1024 error("Multiple %s at 0x%s in %s does not contain an integer number of sub-items\n",
1025 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1026 }
1027
1028 return Bytes;
1029 }
1030
1031 case PACK:
1032 {
1033 debug("Reading pack at 0x%s\n", Int64toHexString(GetLocation(), 8).c_str());
1034
1035 if( Type->GetLenFormat() == DICT_LEN_NONE )
1036 {
1037 // Fixed pack - items know their own length
1038 MDObjectULList::iterator it = begin();
1039 if(Size) for(;;)
1040 {
1041 // If we are already at the end of the list, we have too many bytes!
1042 if(it == end())
1043 {
1044 warning("Extra bytes found parsing buffer in MDObject::ReadValue()\n");
1045 break;
1046 }
1047
1048 (*it).second->Parent = this;
1049 (*it).second->ParentOffset = Bytes;
1050 (*it).second->KLSize = 0;
1051
1052 // DRAGONS: Array length calculation fudge!
1053 // If an array exists in a pack there is no easy way to determine the size
1054 // of the array unless it is the last item in the pack. Unfortunately there
1055 // are some cases where MXF packs have arrays that are not the last entry
1056 // This section deals with each in turn (Nasty!!)
1057
1058 size_t ValueSize = Size;
1059 if((*it).second->Type->GetContainerType() == ARRAY)
1060 {
1061 // FIXME: Shouldn't we do UL lookups here?
1062 std::string FullName = (*it).second->FullName();
1063 if(FullName == "IndexTableSegment/IndexEntryArray/SliceOffsetArray")
1064 {
1065 // DRAGONS: Does this ever get called these days?
1066 // Number of entries in SliceOffsetArray is in IndexTableSegment/SliceCount
1067 // Each entry is 4 bytes long
1068 ValueSize = Parent->GetInt(SliceCount_UL) * 4;
1069 }
1070 else if(FullName == "RandomIndexMetadata/PartitionArray")
1071 {
1072 // RandomIndexMetadata/PartitionArray is followed by a UInt32
1073 if(ValueSize >4) ValueSize = ValueSize - 4; else ValueSize = 0;
1074 }
1075 }
1076
1077 size_t ThisBytes = (*it).second->ReadValue(Buffer, ValueSize);
1078
1079 // debug(" at 0x%s Pack item %s = %s\n", Int64toHexString((*it).second->GetLocation(), 8).c_str(),
1080 // (*it).first.c_str(), (*it).second->GetString().c_str());
1081
1082 Bytes += ThisBytes;
1083
1084 it++;
1085
1086 if(ThisBytes >= Size) break;
1087
1088 Buffer += ThisBytes;
1089 Size -= ThisBytes;
1090 }
1091
1092 if(it != end())
1093 {
1094 warning("Not enough bytes in buffer for %s at 0x%s in %s\n",
1095 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1096 }
1097
1098 return Bytes;
1099 }
1100 else
1101 {
1102 // Variable pack - each item has a length
1103 MDObjectULList::iterator it = begin();
1104 if(Size) for(;;)
1105 {
1106 // If we are already at the end of the list, we have too many bytes!
1107 if(it == end())
1108 {
1109 warning("Extra bytes found parsing buffer in MDObject::ReadValue()\n");
1110 break;
1111 }
1112
1113 (*it).second->Parent = this;
1114 (*it).second->ParentOffset = Bytes;
1115 (*it).second->KLSize = 0;
1116
1117 // Read Length
1118 Length Length;
1119 size_t ThisBytes = ReadLength(Type->GetLenFormat(), Size, Buffer, Length);
1120
1121 // Advance counters and pointers past Length
1122 Size -= ThisBytes;
1123 Buffer += ThisBytes;
1124 Bytes += ThisBytes;
1125
1126 // Sanity check the length of this value
1127 if((sizeof(size_t) < 8) && (Length > 0xffffffff))
1128 {
1129 error("Tried to read %s at 0x%s in %s which is > 4GBytes, but this platform can only handle <= 4GByte chunks\n", FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1130 return 0;
1131 }
1132
1133 if(Length)
1134 {
1135 if(Size < static_cast<size_t>(Length))
1136 {
1137 error("Not enough bytes for value for %s at 0x%s in %s\n",
1138 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1139
1140 // Read what we can!
1141 Length = Size;
1142 }
1143
1144 ThisBytes = (*it).second->ReadValue(Buffer, static_cast<size_t>(Length));
1145
1146 // debug(" at 0x%s Variable Pack item %s = %s\n", Int64toHexString((*it).second->GetLocation(), 8).c_str(),
1147 // (*it).first.c_str(), (*it).second->GetString().c_str());
1148
1149 Bytes += ThisBytes;
1150 }
1151 else
1152 {
1153 // Length == 0, so skip this item
1154 //(*it).second->ReadValue(Buffer, 0);
1155 (*it).second->ClearModified();
1156 ThisBytes = 0;
1157 }
1158
1159 it++;
1160
1161 if(ThisBytes >= Size) break;
1162
1163 Buffer += ThisBytes;
1164 Size -= ThisBytes;
1165 }
1166
1167 if(it != end())
1168 {
1169 warning("Not enough bytes in buffer for %s at 0x%s in %s\n",
1170 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1171 }
1172
1173 return Bytes;
1174 }
1175 }
1176
1177 case SET:
1178 {
1179 debug("Reading set at 0x%s\n", Int64toHexString(GetLocation(), 8).c_str());
1180
1181 // Start with an empty list
1182 clear();
1183
1184 // Scan until out of data
1185 while(Size)
1186 {
1187 size_t BytesAtItemStart = Bytes;
1188
1189 DataChunk Key;
1190 size_t ThisBytes = ReadKey(Type->GetKeyFormat(), Size, Buffer, Key);
1191
1192 // Abort if we can't read the key
1193 // this prevents us looping for ever if we
1194 // come across invalid data
1195 if(ThisBytes == 0) break;
1196
1197 // Advance counters and pointers passed key
1198 Size -= ThisBytes;
1199 Buffer += ThisBytes;
1200 Bytes += ThisBytes;
1201
1202 Length Length;
1203 ThisBytes = ReadLength(Type->GetLenFormat(), Size, Buffer, Length);
1204
1205 // Advance counters and pointers passed Length
1206 Size -= ThisBytes;
1207 Buffer += ThisBytes;
1208 Bytes += ThisBytes;
1209
1210 // Sanity check the length of this value
1211 if((sizeof(size_t) < 8) && (Length > 0xffffffff))
1212 {
1213 error("Tried to read %s at 0x%s in %s which is > 4GBytes, but this platform can only handle <= 4GByte chunks\n", FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1214 return 0;
1215 }
1216
1217 if(Length)
1218 {
1219 if(Size < Length)
1220 {
1221 error("Not enough bytes for value for %s at 0x%s in %s\n",
1222 FullName().c_str(), Int64toHexString(GetLocation(), 8).c_str(), GetSource().c_str());
1223
1224 // Read what we can!
1225 Length = Size;
1226 }
1227
1228 MDObjectPtr NewItem;
1229 if(Type->GetKeyFormat() == DICT_KEY_2_BYTE)
1230 {
1231 ASSERT(Key.Size == 2);
1232 Tag ThisKey = GetU16(Key.Data);
1233
1234 NewItem = new MDObject(ThisKey, UsePrimer);
1235 }
1236 else if(Type->GetKeyFormat() == DICT_KEY_AUTO)
1237 {
1238 ASSERT(Key.Size == 16);
1239 ULPtr ThisUL = new UL(Key.Data);
1240
1241 NewItem = new MDObject(ThisUL);
1242 }
1243 else
1244 {
1245 // Only 2-byte and 16-byte keys are supported at present
1246 ASSERT(0);
1247 return 0;
1248 }
1249
1250 NewItem->Parent = this;
1251 NewItem->ParentOffset = BytesAtItemStart;
1252 NewItem->KLSize = static_cast<UInt32>(Bytes - BytesAtItemStart);
1253
1254 // Handle cases where a batch has burst the 2-byte length (non-standard)
1255 if((Length == 0xffff) && (Type->GetLenFormat() == DICT_LEN_2_BYTE) && (NewItem->Type->GetContainerType() == BATCH))
1256 {
1257 ThisBytes = NewItem->ReadValue(Buffer, Size);
1258 }
1259 else
1260 {
1261 ThisBytes = NewItem->ReadValue(Buffer, static_cast<size_t>(Length));
1262
1263 // debug(" at 0x%s Set item (%s) %s = %s\n", Int64toHexString(NewItem->GetLocation(), 8).c_str(), Key.GetString().c_str(), NewItem->Name().c_str(), NewItem->GetString().c_str());
1264
1265 if(ThisBytes != Length)
1266 {
1267 error("Failed to read complete %s value at 0x%s in %s - specified length=%d, read=%d\n",
1268 NewItem->FullName().c_str(), Int64toHexString(NewItem->GetLocation(), 8).c_str(),
1269 NewItem->GetSource().c_str(), Length, ThisBytes);
1270
1271 // Skip anything left over
1272 if(Length > ThisBytes) ThisBytes = static_cast<size_t>(Length);
1273 }
1274 }
1275
1276 Size -= ThisBytes;
1277 Buffer += ThisBytes;
1278 Bytes += ThisBytes;
1279
1280 AddChildInternal(NewItem);
1281 }
1282 }
1283
1284 return Bytes;
1285 }
1286
1287 default:
1288 ASSERT(0);
1289 return 0;
1290 }
1291 }
1292
1293
1294 //! Has this object (including any child objects) been modified?
IsModified(void)1295 bool MDObject::IsModified(void)
1296 {
1297 if(Modified) return true;
1298
1299 if(!empty())
1300 {
1301 MDObjectULList::iterator it = begin();
1302
1303 while(it != end())
1304 {
1305 if((*it).second->IsModified()) return true;
1306 it++;
1307 }
1308 }
1309
1310 return false;
1311 }
1312
1313
1314 //! Clear the modified flag on this object and any contained objects
ClearModified(void)1315 void MDObject::ClearModified(void)
1316 {
1317 Modified = false;
1318
1319 if(!empty())
1320 {
1321 MDObjectULList::iterator it = begin();
1322
1323 while(it != end())
1324 {
1325 (*it).second->ClearModified();
1326 it++;
1327 }
1328 }
1329 }
1330
1331
1332 //! Set the GenerationUID of an object iff it has been modified
1333 /*! \return true if the GenerationUID has been set, otherwise false
1334 * \note If the object does not have a GenerationUID property false is returned!
1335 */
SetGenerationUID(UUIDPtr NewGen)1336 bool MDObject::SetGenerationUID(UUIDPtr NewGen)
1337 {
1338 if(!IsModified()) return false;
1339
1340 // Can't have a GenerationUID if not a set or pack
1341 MDContainerType CType = Type->GetContainerType();
1342 if((CType != SET) && (CType != PACK)) return false;
1343
1344 // Find (or add) the GenerationUID property
1345 MDObjectPtr GenUID = Child("GenerationUID");
1346 if(!GenUID)
1347 {
1348 // If we don't currenly have a GenerationUID, forst check if this is because we shouldn't have one
1349 if(!IsA(GenerationInterchangeObject_UL)) return false;
1350
1351 // Otherwise go ahead and add one
1352 GenUID = AddChild(GenerationUID_UL);
1353 }
1354
1355 ASSERT(GenUID);
1356
1357 // Set the actual UID
1358 GenUID->Value->ReadValue(NewGen->GetValue(), NewGen->Size());
1359
1360 return true;
1361 }
1362
1363
1364 //! Read a key from a memory buffer
ReadKey(DictKeyFormat Format,size_t Size,const UInt8 * Buffer,DataChunk & Key)1365 UInt32 MDObject::ReadKey(DictKeyFormat Format, size_t Size, const UInt8 *Buffer, DataChunk& Key)
1366 {
1367 UInt32 KeySize;
1368
1369 switch(Format)
1370 {
1371 default:
1372 // Unsupported key types!
1373 case DICT_KEY_NONE:
1374 case DICT_KEY_AUTO: // DRAGONS: Should probably make this work at some point!
1375 ASSERT(0);
1376 Key.Resize(0);
1377 return 0;
1378
1379 case DICT_KEY_1_BYTE: KeySize = 1; break;
1380 case DICT_KEY_2_BYTE: KeySize = 2; break;
1381 case DICT_KEY_4_BYTE: KeySize = 4; break;
1382 }
1383
1384 if(Size < KeySize)
1385 {
1386 error("Not enough bytes for required key type in MDObject::ReadKey()\n");
1387 Key.Resize(0);
1388 return 0;
1389 }
1390
1391 Key.Resize(KeySize);
1392 Key.Set(KeySize, Buffer);
1393
1394 return KeySize;
1395 }
1396
1397
1398 //! Read a length field from a memory buffer
ReadLength(DictLenFormat Format,size_t Size,const UInt8 * Buffer,Length & Length)1399 UInt32 MDObject::ReadLength(DictLenFormat Format, size_t Size, const UInt8 *Buffer, Length& Length)
1400 {
1401 // int LenSize;
1402
1403 switch(Format)
1404 {
1405 default:
1406 // Unsupported key types!
1407 case DICT_LEN_NONE:
1408 ASSERT(0); // Cause debug sessions to show this error
1409 Length = 0;
1410 return 0;
1411
1412 case DICT_LEN_BER:
1413 {
1414 if( Size <1 ) break;
1415 UInt8 LenLen = GetU8(Buffer);
1416
1417 if( LenLen < 0x80 )
1418 {
1419 // Short form
1420 Length = LenLen;
1421 return 1;
1422 }
1423 else
1424 {
1425 // Long form
1426 LenLen &= 0x7f;
1427
1428 UInt32 RetLen = LenLen + 1;
1429 if( RetLen > Size) break;
1430
1431 // DRAGONS: ReadLength should return UInt64, BER length up to 7
1432 if(LenLen > 8)
1433 {
1434 error("Excessive BER length field in MDObject::ReadLength()\n");
1435 Length = 0;
1436 return 0;
1437 }
1438
1439 if(LenLen > 4)
1440 {
1441 // It is valid to have BER length > 4 bytes long however we don't
1442 // support metadata values > 4Gb in size so we ensure they are
1443 // not that big by reading the length and validating
1444
1445 UInt64 Length64 = 0;
1446 const UInt8* LenBuff = (Buffer+1);
1447 while(LenLen--) Length64 = (Length64<<8) + *LenBuff++;
1448
1449 if((Length64 >> 32) != 0)
1450 {
1451 error("Excessive BER length field in MDObject::ReadLength() - Metadata objects are limited to 4Gb each\n");
1452 Length = 0;
1453 return 0;
1454 }
1455
1456 Length =(UInt32) Length64;
1457 }
1458 else
1459 {
1460 // Handle sane sized BER lengths
1461 Length = 0;
1462 const UInt8* LenBuff = (Buffer+1);
1463 while(LenLen--) Length = (Length<<8) + *LenBuff++;
1464 }
1465
1466 return RetLen;
1467 }
1468 }
1469
1470 case DICT_LEN_1_BYTE:
1471 {
1472 if(Size >= 1)
1473 {
1474 Length = GetU8(Buffer);
1475 return 1;
1476 }
1477
1478 // Else we drop through to error handler
1479 break;
1480 }
1481
1482 case DICT_LEN_2_BYTE:// { LenSize = 2; Length = GetU16(Buffer); };
1483 {
1484 if(Size >= 2)
1485 {
1486 Length = GetU16(Buffer);
1487 return 2;
1488 }
1489
1490 // Else we drop through to error handler
1491 break;
1492 }
1493
1494 case DICT_LEN_4_BYTE:// { LenSize = 4; Length = GetU32(Buffer); };
1495 {
1496 if(Size >= 4)
1497 {
1498 Length = GetU32(Buffer);
1499 return 4;
1500 }
1501
1502 // Else we drop through to error handler
1503 break;
1504 }
1505 }
1506
1507 error("Not enough bytes for required length field in MDObject::ReadLength()\n");
1508 Length = 0;
1509 return 0;
1510 }
1511
1512
1513 //! Get the location within the ultimate parent
GetLocation(void)1514 UInt64 mxflib::MDObject::GetLocation(void)
1515 {
1516 UInt64 Ret = ParentOffset;
1517
1518 if(Parent) Ret += Parent->KLSize + Parent->GetLocation();
1519
1520 return Ret;
1521 }
1522
1523 //! Get text that describes where this item came from
GetSource(void)1524 std::string mxflib::MDObject::GetSource(void)
1525 {
1526 if(Parent) return Parent->GetSource();
1527 if(ParentFile) return std::string("file \"") + ParentFile->Name + std::string("\"");
1528
1529 return std::string("memory buffer");
1530 }
1531
1532
1533 //! Build a data chunk with all this item's data (including child data)
PutData(PrimerPtr UsePrimer)1534 const DataChunkPtr MDObject::PutData(PrimerPtr UsePrimer /* =NULL */)
1535 {
1536 if(Value) return Value->PutData();
1537
1538 // DRAGONS: Pre-allocating a buffer could speed things up
1539 DataChunkPtr Ret = new DataChunk;
1540
1541 MDObjectULList::iterator it = begin();
1542
1543 while(it != end())
1544 {
1545 (*it).second->WriteObject(Ret, this, UsePrimer);
1546 it++;
1547 }
1548
1549 return Ret;
1550 }
1551
1552
1553 //! Write this object, and any strongly linked sub-objects, to a memory buffer
1554 /*! The object must be at the outer or top KLV level.
1555 * The objects are appended to the buffer
1556 * \return The number of bytes written
1557 */
WriteLinkedObjects(DataChunkPtr & Buffer,PrimerPtr UsePrimer)1558 size_t MDObject::WriteLinkedObjects(DataChunkPtr &Buffer, PrimerPtr UsePrimer /*=NULL*/)
1559 {
1560 size_t Bytes = 0;
1561
1562 Bytes = WriteObject(Buffer, NULL, UsePrimer);
1563
1564 MDObjectULList::iterator it = begin();
1565 while(it != end())
1566 {
1567 if((*it).second->Link)
1568 {
1569 if((*it).second->GetRefType() == DICT_REF_STRONG) Bytes += (*it).second->Link->WriteLinkedObjects(Buffer, UsePrimer);
1570 }
1571 else if(!((*it).second->empty()))
1572 {
1573 MDObjectULList::iterator it2 = (*it).second->begin();
1574 MDObjectULList::iterator itend2 = (*it).second->end();
1575 while(it2 != itend2)
1576 {
1577 if((*it2).second->Link)
1578 {
1579 if((*it2).second->GetRefType() == DICT_REF_STRONG)
1580 {
1581 Bytes += (*it2).second->Link->WriteLinkedObjects(Buffer, UsePrimer);
1582 }
1583 }
1584 else if(!((*it2).second->empty()))
1585 {
1586 error("Internal error for object %s - Cannot process nesting > 2 in WriteLinkedObjects()\n",
1587 (*it2).second->FullName().c_str());
1588 }
1589 it2++;
1590 }
1591 }
1592 it++;
1593 }
1594
1595 return Bytes;
1596 }
1597
1598
1599 //! Write this object to a memory buffer
1600 /*! The object is appended to the buffer
1601 * \return The number of bytes written
1602 */
1603 #define DEBUG_WRITEOBJECT(x)
1604 //#define DEBUG_WRITEOBJECT(x) x
WriteObject(DataChunkPtr & Buffer,MDObjectPtr ParentObject,PrimerPtr UsePrimer,UInt32 BERSize)1605 size_t MDObject::WriteObject(DataChunkPtr &Buffer, MDObjectPtr ParentObject, PrimerPtr UsePrimer /*=NULL*/, UInt32 BERSize /*=0*/)
1606 {
1607 size_t Bytes = 0;
1608
1609 DictLenFormat LenFormat;
1610
1611 DEBUG_WRITEOBJECT( debug("WriteObject(%s) ", FullName().c_str()); )
1612
1613 // Write the key (and determine the length format)
1614 if(!ParentObject)
1615 {
1616 DEBUG_WRITEOBJECT( debug("no parent\n"); )
1617 Bytes += WriteKey(Buffer, DICT_KEY_AUTO, UsePrimer);
1618 LenFormat = DICT_LEN_BER;
1619 }
1620 else
1621 {
1622 DEBUG_WRITEOBJECT( debug("Parent %s, ", ParentObject->FullName().c_str()); )
1623
1624 // Only sets need keys
1625 if(ParentObject->Type->GetContainerType() == SET)
1626 {
1627 Bytes = WriteKey(Buffer, ParentObject->Type->GetKeyFormat(), UsePrimer);
1628
1629 DEBUG_WRITEOBJECT( DataChunk Key; Key.Set(Buffer, Buffer->Size - Bytes); )
1630 DEBUG_WRITEOBJECT( debug("Key = %s, ", Key.GetString().c_str()); )
1631 }
1632
1633 if((ParentObject->Type->GetContainerType() == BATCH) || (ParentObject->Type->GetContainerType() == ARRAY))
1634 {
1635 LenFormat = DICT_LEN_NONE;
1636 }
1637 else
1638 {
1639 LenFormat = ParentObject->Type->GetLenFormat();
1640 }
1641
1642 DEBUG_WRITEOBJECT( debug("ParentObject->ContainerType = %d, ", ParentObject->Type->GetContainerType()); )
1643 DEBUG_WRITEOBJECT( if(LenFormat == DICT_LEN_BER) debug("Length = BER\n"); )
1644 DEBUG_WRITEOBJECT( else debug("Length = %d-byte\n", (int)LenFormat); )
1645 }
1646
1647 // The rest depends on the container type
1648 MDContainerType CType = Type->GetContainerType();
1649
1650 // Build value
1651 if(CType == BATCH || CType == ARRAY)
1652 {
1653 UInt32 Count = 0;
1654 UInt32 Size = 0;
1655
1656 // DRAGONS: Pre-allocating a buffer could speed things up
1657 DataChunkPtr Val = new DataChunk();
1658
1659 // Work out how many sub-items per child
1660 // DRAGONS: We assume < 4 billion
1661 UInt32 SubCount = static_cast<UInt32>(Type->GetChildOrder().size());
1662
1663 // Count of remaining subs for this item
1664 UInt32 Subs = 0;
1665
1666 MDObjectULList::iterator it = begin();
1667 while(it != end())
1668 {
1669 // Start of an item
1670 if(Subs == 0)
1671 {
1672 Subs = SubCount;
1673 Size = 0;
1674 Count++;
1675 }
1676 // DRAGONS: do NOT force embedded objects to inherit BERSize
1677 UInt32 ThisBytes = static_cast<UInt32>((*it).second->WriteObject(Val, this, UsePrimer));
1678 //Bytes += ThisBytes;
1679 Size += ThisBytes;
1680
1681 Subs--;
1682 it++;
1683 }
1684
1685 // Determine item size if batch is empty
1686 // May not be strictly required, but 0 items of 0 size is a little dubious
1687 if(Count == 0)
1688 {
1689 DataChunkPtr Temp = new DataChunk();
1690
1691 MDOTypeList::const_iterator it = Type->GetChildList().begin();
1692 while(it != Type->GetChildList().end())
1693 {
1694 MDObjectPtr Ptr = new MDObject(*it);
1695 Ptr->WriteObject(Temp, this, UsePrimer, BERSize);
1696 it++;
1697 }
1698 Size = static_cast<UInt32>(Temp->Size);
1699 }
1700
1701 if(CType == BATCH)
1702 {
1703 // Write the length and batch header
1704 Bytes += WriteLength(Buffer, Val->Size+8, LenFormat, BERSize);
1705 UInt8 Buff[4];
1706 PutU32(Count, Buff);
1707 Buffer->Append(4, Buff);
1708 PutU32(Size, Buff);
1709 Buffer->Append(4, Buff);
1710 Bytes += 8;
1711 }
1712 else
1713 {
1714 Bytes += WriteLength(Buffer, Val->Size, LenFormat, BERSize);
1715 }
1716
1717 // Append this data
1718 Buffer->Append(Val);
1719 Bytes += Val->Size;
1720
1721 DEBUG_WRITEOBJECT( debug(" > %s\n", Val->GetString().c_str()); )
1722 }
1723 else if(CType == PACK)
1724 {
1725 DEBUG_WRITEOBJECT( debug(" *PACK*\n"); )
1726
1727 // DRAGONS: Pre-allocating a buffer could speed things up
1728 DataChunkPtr Val = new DataChunk;
1729
1730 // Ensure we write the pack out in order
1731 StringList::const_iterator it = Type->GetChildOrder().begin();
1732
1733 while(it != Type->GetChildOrder().end())
1734 {
1735 MDObjectPtr Ptr = Child(*it);
1736 if(!Ptr)
1737 {
1738 error("Pack %s is missing sub-item %s\n", FullName().c_str(), (*it).c_str());
1739 }
1740 else
1741 {
1742 Bytes += Ptr->WriteObject(Val, this, UsePrimer, BERSize);
1743 }
1744 it++;
1745 }
1746
1747 // Write the length of the value
1748 // DRAGONS: do NOT force embedded objects to inherit BERSize
1749 Bytes += WriteLength(Buffer, Val->Size, LenFormat);
1750
1751 // Append this data
1752 Buffer->Append(Val);
1753 Bytes += Val->Size;
1754
1755 DEBUG_WRITEOBJECT( debug(" > %s\n", Val->GetString().c_str()); )
1756 }
1757 else if(!empty())
1758 {
1759 DEBUG_WRITEOBJECT( debug(" *Not Empty*\n"); )
1760
1761 // DRAGONS: Pre-allocating a buffer could speed things up
1762 DataChunkPtr Val = new DataChunk;
1763
1764 MDObjectULList::iterator it = begin();
1765 while(it != end())
1766 {
1767 // DRAGONS: do NOT force embedded objects to inherit BERSize
1768 // don't double-count the bytes!
1769 (*it).second->WriteObject(Val, this, UsePrimer);
1770 it++;
1771 }
1772
1773 // Write the length of the value
1774 Bytes += WriteLength(Buffer, Val->Size, LenFormat, BERSize);
1775
1776 // Append this data
1777 Buffer->Append(Val);
1778 Bytes += Val->Size;
1779
1780 DEBUG_WRITEOBJECT( debug(" > %s\n", Val->GetString().c_str()); )
1781 }
1782 else if(Value)
1783 {
1784 DEBUG_WRITEOBJECT( debug(" *Value*\n"); )
1785
1786 DataChunkPtr Val = Value->PutData();
1787 Bytes += WriteLength(Buffer, Val->Size, LenFormat, BERSize);
1788 Buffer->Append(Val);
1789 Bytes += Val->Size;
1790
1791 DEBUG_WRITEOBJECT( debug(" > %s\n", Val->GetString().c_str()); )
1792 }
1793 else
1794 {
1795 DEBUG_WRITEOBJECT( debug(" *Empty!*\n"); )
1796
1797 Bytes += WriteLength(Buffer, 0, LenFormat, BERSize);
1798 }
1799
1800 return Bytes;
1801 }
1802
1803
1804 //! Write a length field to a memory buffer
1805 /*! The length is <b>appended</b> to the specified buffer
1806 * \param Buffer The buffer to receive the length
1807 * \param Length The length to be written
1808 * \param Format The format to use for the length
1809 * \param Size The total number of bytes to write for a BER length (or 0 for auto)
1810 * \return Number of bytes written
1811 * \note If the format is BER and a size is specified it will be overridden for
1812 * lengths that will not fit. However an error message will be produced.
1813 */
WriteLength(DataChunkPtr & Buffer,Length Length,DictLenFormat Format,UInt32 Size)1814 UInt32 MDObject::WriteLength(DataChunkPtr &Buffer, Length Length, DictLenFormat Format, UInt32 Size /*=0*/)
1815 {
1816 switch(Format)
1817 {
1818 default:
1819 case DICT_LEN_NONE:
1820 return 0;
1821
1822 case DICT_LEN_BER:
1823 {
1824 DataChunkPtr BER = MakeBER(Length, Size);
1825 Buffer->Append(*BER);
1826 return static_cast<UInt32>(BER->Size);
1827 }
1828
1829 case DICT_LEN_1_BYTE:
1830 {
1831 UInt8 Buff;
1832 UInt8 Len8 = Length <= 0xff ? (UInt8)Length : 0xff;
1833 PutU8(Len8, &Buff);
1834
1835 Buffer->Append(1, &Buff);
1836 return 1;
1837 }
1838
1839 case DICT_LEN_2_BYTE:
1840 {
1841 UInt8 Buff[2];
1842 UInt16 Len16 = Length <= 0xffff ? (UInt16)Length : 0xffff;
1843 PutU16(Len16, Buff);
1844
1845 Buffer->Append(2, Buff);
1846 return 2;
1847 }
1848
1849 case DICT_LEN_4_BYTE:
1850 {
1851 UInt8 Buff[4];
1852 UInt32 Len32 = Length <= 0xffffffff ? (UInt32)Length : 0xffffffff;
1853 PutU32(Len32, Buff);
1854
1855 Buffer->Append(4, Buff);
1856 return 4;
1857 }
1858 }
1859 }
1860
1861
1862 //! Write an objects key
1863 /*! The key is <b>appended</b> to the specified buffer
1864 * \return Number of bytes written
1865 * \note If the object has no parent the full UL will be written, otherwise
1866 * the parent will be examined to determine the type of key to write.
1867 * \note If a 2-byte local tag is used the primer UsePrimer is used to determine
1868 * the correct tag. UsePrimer will be updated if it doesn't yet incude the tag
1869 */
WriteKey(DataChunkPtr & Buffer,DictKeyFormat Format,PrimerPtr UsePrimer)1870 UInt32 MDObject::WriteKey(DataChunkPtr &Buffer, DictKeyFormat Format, PrimerPtr UsePrimer /*=NULL*/)
1871 {
1872 switch(Format)
1873 {
1874 default:
1875 case DICT_KEY_NONE:
1876 return 0;
1877
1878 case DICT_KEY_AUTO:
1879 {
1880 if(!TheUL)
1881 {
1882 error("Call to WriteKey() for %s, but the UL is not known\n", FullName().c_str());
1883 return 0;
1884 }
1885
1886 Buffer->Append(16, TheUL->GetValue());
1887 return 16;
1888 }
1889
1890 case DICT_KEY_2_BYTE:
1891 {
1892 if(!TheUL)
1893 {
1894 error("Call to WriteKey() for %s, but the UL is not known\n", FullName().c_str());
1895 return 0;
1896 }
1897
1898
1899 Tag UseTag;
1900 if(UsePrimer) UseTag = UsePrimer->Lookup(TheUL, TheTag);
1901 else UseTag = MDOType::GetStaticPrimer()->Lookup(TheUL, TheTag);
1902
1903 UInt8 Buff[2];
1904 PutU16(UseTag, Buff);
1905
1906 Buffer->Append(2, Buff);
1907 return 2;
1908 }
1909
1910 case DICT_KEY_1_BYTE:
1911 case DICT_KEY_4_BYTE:
1912 {
1913 ASSERT(0);
1914 error("Call to WriteKey() for %s, but 1 and 4 byte tags not currently supported\n", FullName().c_str());
1915 return 0;
1916 }
1917 }
1918 }
1919
1920
1921 //! Make a link from this reference source to the specified target set
1922 /*! If the target set already has an instanceUID it will be used, otherwise
1923 * one will be added.
1924 * \param TargetSet A pointer to the set to be the target of this reference
1925 * \param ForceLink True if the link is to be made even if not a valid reference source/target pair
1926 * \return true on success, else false
1927 * \note The link will be made from the source <b>property</b> to the target <b>set</b>
1928 * so be aware that "this" must be a reference source property and "TargetSet"
1929 * must be a set (or pack) containing an InstanceUID property which is a
1930 * reference target
1931 */
MakeRef(MDObjectPtr & TargetSet,bool ForceLink)1932 bool MDObject::MakeRef(MDObjectPtr &TargetSet, bool ForceLink /*=false*/)
1933 {
1934 UInt8 TheUID[16];
1935
1936 // Does the target set already have an InstanceUID?
1937 MDObjectPtr InstanceUID = TargetSet[InstanceUID_UL];
1938
1939 // If not add one
1940 if(!InstanceUID)
1941 {
1942 InstanceUID = TargetSet->AddChild(InstanceUID_UL);
1943
1944 // If this failed then chances are the set is not a reference target
1945 if(!InstanceUID)
1946 {
1947 error("Attempt to reference %s from %s failed\n", FullName().c_str(), TargetSet->FullName().c_str());
1948 return false;
1949 }
1950
1951 MakeUUID(TheUID);
1952 InstanceUID->ReadValue(TheUID, 16);
1953 }
1954 else
1955 {
1956 DataChunkPtr Data = InstanceUID->Value->PutData();
1957 ASSERT(Data->Size == 16);
1958 memcpy(TheUID, Data->Data, 16);
1959 }
1960
1961 // Validate that we are a reference source
1962 // Note: The link will be attempted even if an error is produced
1963 // This is intentional as it may be valid in a later file spec
1964 ClassRef RType = Type->GetRefType();
1965 if((RType != ClassRefStrong) && (RType != ClassRefWeak) && (RType != ClassRefGlobal))
1966 {
1967 if(!ForceLink)
1968 {
1969 error("Attempting to reference %s from %s (which is not a reference source)\n",
1970 TargetSet->FullName().c_str(), FullName().c_str() );
1971 }
1972 }
1973
1974 // Make the link
1975 ReadValue(TheUID, 16);
1976 Link = TargetSet;
1977
1978 return true;
1979 }
1980
1981 //! Set an object to its distinguished value
1982 /*! \return true if distinguished value set, else false */
SetDValue(void)1983 bool MDObject::SetDValue(void)
1984 {
1985 if(Type->GetDValue().Size == 0) return false;
1986
1987 SetModified(true);
1988 ReadValue(Type->GetDValue());
1989
1990 return true;
1991 }
1992
1993
1994 //! Is an object set to its distinguished value?
1995 /*! \return true if distinguished value set, else false */
IsDValue(void)1996 bool MDObject::IsDValue(void)
1997 {
1998 if(Type->GetDValue().Size == 0) return false;
1999
2000 DataChunkPtr DVal = PutData();
2001 if(DVal->Size != Type->GetDValue().Size) return false;
2002
2003 if(memcmp(DVal->Data, Type->GetDValue().Data, DVal->Size) == 0) return true;
2004
2005 return false;
2006 }
2007
2008
2009 //! Make a copy of this object
MakeCopy(void)2010 MDObjectPtr MDObject::MakeCopy(void)
2011 {
2012 MDObjectPtr Ret = new MDObject(Type);
2013
2014 MDObjectULList::iterator it = begin();
2015 while(it != end())
2016 {
2017 Ret->insert((*it).second->MakeCopy());
2018 it++;
2019 }
2020
2021 if(Value)
2022 {
2023 Ret->Value = new MDValue(Value->GetType());
2024 Ret->Value->ReadValue(Value->PutData());
2025 }
2026
2027 // Somewhat dangerous!!
2028 if(Link)
2029 {
2030 Ret->Link = Link;
2031 if(GetRefType() == DICT_REF_STRONG)
2032 {
2033 warning("Copy made of %s which is a StrongRef!\n", FullName().c_str());
2034 }
2035 }
2036
2037 // Copy any properties that are safe to copy
2038 Ret->TheUL = TheUL;
2039 Ret->TheTag = TheTag;
2040
2041 SetModified(true);
2042
2043 return Ret;
2044 }
2045
2046
2047 //! Derive this new entry from a base entry
2048 /*! \note It is important that DictName is set before calling
2049 * \note Don't attempt to call this function on objects that are not freshly created
2050 */
Derive(MDOTypePtr & BaseEntry)2051 void MDOType::Derive(MDOTypePtr &BaseEntry)
2052 {
2053 // Default to using the base entry keys
2054 Key.Set(BaseEntry->Key);
2055 GlobalKey.Set(BaseEntry->GlobalKey);
2056
2057 // Copy the base root name, note that we can't copy the name as this must be unique
2058 RootName = BaseEntry->RootName;
2059
2060 // Copy the container type
2061 ContainerType = BaseEntry->ContainerType;
2062
2063 // Copy the base detail
2064 Detail = BaseEntry->Detail;
2065
2066 // Copy the type info
2067 ValueType = BaseEntry->ValueType;
2068 TypeName = BaseEntry->TypeName;
2069
2070 //VectorType = BaseEntry->VectorType;
2071
2072 // Copy the data relating to content format
2073 KeyFormat = BaseEntry->KeyFormat;
2074 LenFormat = BaseEntry->LenFormat;
2075 minLength = BaseEntry->minLength;
2076 maxLength = BaseEntry->maxLength;
2077
2078 // Copy the usage
2079 Use = BaseEntry->Use;
2080
2081 // Copy the default and the destinguished value
2082 Default.Set(BaseEntry->Default);
2083 DValue.Set(BaseEntry->DValue);
2084
2085 // Copy the reference type
2086 RefType = BaseEntry->RefType;
2087
2088 // Copy the parentage
2089 Parent = BaseEntry->Parent;
2090
2091 // Tie to the base class
2092 Base = BaseEntry;
2093
2094 // Remove any of our existing children
2095 clear();
2096
2097 // Add children from base class
2098 MDOTypeList::iterator it = BaseEntry->ChildList.begin();
2099 while(it != BaseEntry->ChildList.end())
2100 {
2101 // Add the base types children
2102 insert(*it);
2103
2104 NameLookup[RootName + DictName + "/" + (*it)->Name()] = *it;
2105 it++;
2106 }
2107
2108 // Copy the base type's ref target setting
2109 RefTarget = Base->RefTarget;
2110 RefTargetName = Base->RefTargetName;
2111 }
2112
2113
2114 //! Re-Derive sub-items from a base entry
2115 /*! Used when the base entry is being extended.
2116 * We scan the list of sub items until a new sub-item is found, then insert any remaining items at
2117 * that point (before any items that were added to the derived type and were not part of the base type)
2118 */
ReDerive(MDOTypePtr & BaseEntry)2119 void MDOType::ReDerive(MDOTypePtr &BaseEntry)
2120 {
2121 /* Build iterators that show the insertion point for child lists */
2122 MDOTypeList::iterator ChildListIt = ChildList.begin();
2123 std::list<std::string>::iterator ChildOrderIt = ChildOrder.begin();
2124
2125 // Set true once we find new items that we need to insert
2126 bool Inserting = false;
2127
2128 MDOTypeList::iterator it = BaseEntry->ChildList.begin();
2129 while(it != BaseEntry->ChildList.end())
2130 {
2131 if(!Inserting)
2132 {
2133 // If we are scanning and have have found a mismatch, this is the insertion point.
2134 if((*it) != (*ChildListIt)) Inserting = true;
2135 }
2136
2137 if(Inserting)
2138 {
2139 Inserting = true;
2140
2141 std::string NewName = (*it)->Name();
2142 std::pair<iterator, bool> Ret = MDOTypeMap::insert(MDOTypeMap::value_type(NewName, (*it)));
2143
2144 ChildList.insert(ChildListIt, *it);
2145 ChildOrder.insert(ChildOrderIt, NewName);
2146 }
2147
2148 // Increment the insertion points (unless already at the end of the lists)
2149 if(ChildListIt != ChildList.end()) ChildListIt++;
2150 if(ChildOrderIt != ChildOrder.end()) ChildOrderIt++;
2151
2152 if(!Inserting)
2153 {
2154 // If we hit the end of the list we insert at the end (both lists should end at the same point!)
2155 if((ChildListIt == ChildList.end()) || (ChildOrderIt == ChildOrder.end())) Inserting = true;
2156 }
2157
2158 it++;
2159 }
2160 }
2161
2162
2163 //! Determine if this object is derived from a specified type (directly or indirectly)
IsA(std::string BaseType)2164 bool MDObject::IsA(std::string BaseType)
2165 {
2166 MDOTypePtr TestType = Type;
2167
2168 while(TestType)
2169 {
2170 if(TestType->Name() == BaseType) return true;
2171 TestType = TestType->Base;
2172 }
2173
2174 return false;
2175 }
2176
2177
2178 //! Determine if this object is derived from a specified type (directly or indirectly)
IsA(MDOTypePtr & BaseType)2179 bool MDObject::IsA(MDOTypePtr &BaseType)
2180 {
2181 MDOTypePtr TestType = Type;
2182
2183 while(TestType)
2184 {
2185 if(TestType == BaseType) return true;
2186 TestType = TestType->Base;
2187 }
2188
2189 return false;
2190 }
2191
2192
2193 //! Determine if this object is derived from a specified type (directly or indirectly)
IsA(const UL & BaseType)2194 bool MDObject::IsA(const UL &BaseType)
2195 {
2196 MDOTypePtr TestType = Type;
2197
2198 while(TestType)
2199 {
2200 const ULPtr &TestUL = TestType->GetTypeUL();
2201 if((*TestUL).Matches(BaseType)) return true;
2202 TestType = TestType->Base;
2203 }
2204
2205 return false;
2206 }
2207
2208
2209 //! Determine if this type is derived from a specified type (directly or indirectly)
IsA(std::string BaseType)2210 bool MDOType::IsA(std::string BaseType)
2211 {
2212 MDOTypePtr TestType = this;
2213
2214 while(TestType)
2215 {
2216 if(TestType->Name() == BaseType) return true;
2217 TestType = TestType->Base;
2218 }
2219
2220 return false;
2221 }
2222
2223
2224 //! Determine if this type is derived from a specified type (directly or indirectly)
IsA(MDOTypePtr & BaseType)2225 bool MDOType::IsA(MDOTypePtr &BaseType)
2226 {
2227 MDOTypePtr TestType = this;
2228
2229 while(TestType)
2230 {
2231 if(TestType == BaseType) return true;
2232 TestType = TestType->Base;
2233 }
2234
2235 return false;
2236 }
2237
2238
2239 //! Determine if this type is derived from a specified type (directly or indirectly)
IsA(const UL & BaseType)2240 bool MDOType::IsA(const UL &BaseType)
2241 {
2242 MDOTypePtr TestType = this;
2243
2244 while(TestType)
2245 {
2246 const ULPtr &TestUL = TestType->GetTypeUL();
2247 if((*TestUL).Matches(BaseType)) return true;
2248 TestType = TestType->Base;
2249 }
2250
2251 return false;
2252 }
2253
2254
2255
2256
2257
2258 /*
2259 ** XML_warning() - Handle warnings during XML parsing
2260 */
XML_warning(void * user_data,const char * msg,...)2261 void MDOType::XML_warning(void *user_data, const char *msg, ...)
2262 {
2263 va_list args;
2264
2265 va_start(args, msg);
2266
2267 // DRAGONS: How do we prevent bursting?
2268 char Buffer[10240];
2269 vsprintf(Buffer, msg, args);
2270
2271 // DRAGONS: This should end up on stderr or similar, not in any XML output file!
2272 // If there is any reason to send warnings to an XML file that should be done in
2273 // the implementation of wraning(), not each message.
2274 // warning("<!-- XML WARNING: %s -->\n", Buffer);
2275
2276 warning("XML WARNING: %s\n", Buffer);
2277
2278 va_end(args);
2279 }
2280
2281 /*
2282 ** XML_error() - Handle errors during XML parsing
2283 */
XML_error(void * user_data,const char * msg,...)2284 void MDOType::XML_error(void *user_data, const char *msg, ...)
2285 {
2286 va_list args;
2287
2288 va_start(args, msg);
2289
2290 // DRAGONS: How do we prevent bursting?
2291 char Buffer[10240];
2292 vsprintf(Buffer, msg, args);
2293 error("XML ERROR: %s\n", Buffer);
2294
2295 va_end(args);
2296 }
2297
2298 /*
2299 ** XML_fatalError() - Handle fatal erros during XML parsing
2300 */
XML_fatalError(void * user_data,const char * msg,...)2301 void MDOType::XML_fatalError(void *user_data, const char *msg, ...)
2302 {
2303 va_list args;
2304
2305 va_start(args, msg);
2306
2307 // DRAGONS: How do we prevent bursting?
2308 char Buffer[1024];
2309 vsprintf(Buffer, msg, args);
2310 error("XML FATAL ERROR: %s\n", Buffer);
2311
2312 va_end(args);
2313 }
2314
2315
2316
2317
2318 //! Locate reference target types for any types not yet located
LocateRefTypes(void)2319 void mxflib::MDOType::LocateRefTypes(void)
2320 {
2321 MDOTypeList::iterator it = MDOType::AllTypes.begin();
2322 while(it != MDOType::AllTypes.end())
2323 {
2324 // Locate the reference target if the name exists, but not the type
2325 if((*it)->RefTargetName.size() && !(*it)->RefTarget)
2326 {
2327 MDOTypeMap::iterator it2 = NameLookup.find((*it)->RefTargetName);
2328
2329 if(it2 == NameLookup.end())
2330 {
2331 error("Type %s specifies an unknown reference target type of %s\n", (*it)->Name().c_str(), (*it)->RefTargetName.c_str());
2332 }
2333 else
2334 {
2335 (*it)->RefTarget = (*it2).second;
2336 }
2337 }
2338
2339 it++;
2340 }
2341 }
2342
2343
2344 //** Static Instantiations for MDOType class **
2345 //*********************************************
2346
2347 MDOTypeList MDOType::AllTypes; //!< All types managed by the MDOType class
2348 MDOTypeList MDOType::TopTypes; //!< The top-level types managed by the MDOType class
2349
2350 //! Map for UL lookups
2351 std::map<UL, MDOTypePtr> MDOType::ULLookup;
2352
2353 //! Map for UL version-less lookups
2354 std::map<UL, MDOTypePtr> MDOType::ULLookupVer1;
2355
2356 //! Map for reverse lookups based on type name
2357 std::map<std::string, MDOTypePtr> MDOType::NameLookup;
2358
2359
2360 //! Redefine a sub-item in a container
ReDefine(std::string NewDetail,std::string NewBase,unsigned int NewMinSize,unsigned int NewMaxSize)2361 void MDOType::ReDefine(std::string NewDetail, std::string NewBase, unsigned int NewMinSize, unsigned int NewMaxSize)
2362 {
2363 if(NewDetail.length()) Detail = NewDetail;
2364
2365 if(NewBase.length())
2366 {
2367 MDTypePtr Type = MDType::Find(NewBase);
2368 if(!Type)
2369 error("Attempt to redefine %s to be of type %s which is not known\n", FullName().c_str(), NewBase.c_str());
2370 else
2371 ValueType = Type;
2372 }
2373
2374 if(NewMinSize != 0) minLength = NewMinSize;
2375 if(NewMaxSize != 0) maxLength = NewMaxSize;
2376 }
2377
2378
2379