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