summaryrefslogtreecommitdiff
path: root/XMPCore/source/NodeImpl.cpp
blob: be93781122360b90aed4f5885800f09a869c6138 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
// =================================================================================================
// Copyright Adobe
// Copyright 2014 Adobe
// All Rights Reserved
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it. If you have received this file from a source other 
// than Adobe, then your use, modification, or distribution of it requires the prior written permission
// of Adobe.
// =================================================================================================

#define IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED 1
	#include "XMPCore/ImplHeaders/NodeImpl.h"
#undef IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED

#include "XMPCommon/Interfaces/IError_I.h"
#include "XMPCore/XMPCoreErrorCodes.h"
#include "XMPCommon/Interfaces/IUTF8String_I.h"
#include "XMPCommon/Utilities/AutoSharedLock.h"
#include "XMPCommon/Utilities/TSmartPointers_I.h"
#include "XMPCore/Interfaces/IPath.h"
#include "XMPCore/Interfaces/IPathSegment_I.h"
#include "XMPCore/Interfaces/IStructureNode_I.h"

#include "source/XMP_LibUtils.hpp"
#include "source/UnicodeInlines.incl_cpp"
#include <cstring>
#include <algorithm>

namespace AdobeXMPCore_Int {

	// All virtual functions
	static bool VerifyNameSpace( const char * nameSpace, sizet nameSpaceLength ) {
		if ( ( nameSpace == NULL ) || ( nameSpaceLength == 0 )  || ( nameSpaceLength == AdobeXMPCommon::npos && strlen( nameSpace ) == 0 ) ) {
			NOTIFY_ERROR( IError_v1::kEDGeneral, kGECParametersNotAsExpected,
				"nameSpace can't be null or empty", IError_v1::kESOperationFatal,
				true, ( void * ) nameSpace, true, nameSpaceLength, nameSpace, nameSpace );
			return false;
		}
		return true;
	}

	static bool VerifyName( const char * name, sizet nameLength ) {
		if ( ( name == NULL ) || ( nameLength == 0 ) || ( nameLength == AdobeXMPCommon::npos && strlen( name ) == 0 ) ) {
			NOTIFY_ERROR( IError_v1::kEDGeneral, kGECParametersNotAsExpected,
				"name can't be null or empty", IError_v1::kESOperationFatal,
				true, ( void * ) name, true, nameLength, name, name );
			return false;
		}
		if ( nameLength == AdobeXMPCommon::npos ) nameLength = strlen( name );
		try {
			if ( nameLength != 2 && strncmp( name, "[]", 2 ) != 0 )
				VerifySimpleXMLName( name, name + nameLength );
		} catch ( ... ) {
			NOTIFY_ERROR( IError_v1::kEDDataModel, kDMECBadXPath,
				"name is not a valid XML Name", IError_base::kESOperationFatal,
				true, name );
			return false;
		}
		return true;
	}

	static spcIPathSegment CreatePathSegmentToParent( pcINode node ) {
		if ( node->IsQualifierNode() ) {
			return IPathSegment_I::CreateQualifierPathSegment( node->GetNameSpace(), node->GetName() );
		}
		if ( node->IsArrayItem() ) {
			return IPathSegment_I::CreateArrayIndexPathSegment( node->GetNameSpace(), node->GetIndex() );
		}
		return IPathSegment_I::CreatePropertyPathSegment( node->GetNameSpace(), node->GetName() );
	}

	static const char * kQualifierNodeNameSpace( "http://qualifiers" );
	static const AdobeXMPCommon::sizet kQualifiersNodeNameSpaceLength( 17 );
	static const char * kQualifierNodeLocalName( "_qualifiers_" );
	static const AdobeXMPCommon::sizet kQualifierNodeLocalNameLength( 12 );

	NodeImpl::NodeImpl( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength )
		: mNameSpace( IUTF8String_I::CreateUTF8String( nameSpace, nameSpaceLength ) )
		, mName( IUTF8String_I::CreateUTF8String( name, nameLength ) )
		, mIndex( 0 )
		, mpParent( NULL )
		, mspParent()
		, mChangeCount( 0 )
		, mIsQualifierNode( false )
	{
		VerifyNameSpace( nameSpace, nameSpaceLength );
		VerifyName( name, nameLength );
	}

	void APICALL NodeImpl::ChangeParent( pINode parent ) {
		AutoSharedLock lock( mSharedMutex, true );
		if ( mspParent ) {
			if ( mspParent.get() == parent ) {
				return;
			} else {
				mspParent.reset();
				if ( mChangeCount > 1 ) {
					mpParent->GetINode_I()->UnRegisterChange();
					if ( parent ) parent->GetINode_I()->RegisterChange();
				}
				mpParent = parent;
				updateParentSharedPointer();
			}
		} else {
			if ( mChangeCount > 1 ) {
				if ( mpParent ) mpParent->GetINode_I()->UnRegisterChange();
				if ( parent ) parent->GetINode_I()->RegisterChange();
			}
			if ( parent ) parent->GetINode_I()->RegisterChange();
			mpParent = parent;
			updateParentSharedPointer();
		}

		if ( !mpParent ) {
			mIsQualifierNode = false;
			mIndex = 0;
		}
	}

	pINode APICALL NodeImpl::GetRawParentPointer() {
		AutoSharedLock lock( mSharedMutex );
		return mpParent;
	}

	spINode APICALL NodeImpl::GetParent() {
		AutoSharedLock lock( mSharedMutex );
		if ( mpParent ) {
			if ( mIsQualifierNode )
				return MakeUncheckedSharedPointer( mpParent->GetINode_I()->GetRawParentPointer(), __FILE__, __LINE__ );
			else
				return MakeUncheckedSharedPointer( mpParent, __FILE__, __LINE__ );
		}
		else
			return spINode();
	}

	void APICALL NodeImpl::SetName( const char * name, sizet nameLength ) {
		if ( VerifyName( name, nameLength ) ) {
			spIUTF8String newName = IUTF8String_I::CreateUTF8String( name, nameLength );
			if ( mName->compare( newName ) == 0 ) return;
			if ( mpParent ) {
				if ( mpParent->GetINode_I()->ValidateNameOrNameSpaceChangeForAChild( mNameSpace, mName, mNameSpace, newName ) ) {
					AutoSharedLock( mSharedMutex, true );
					mName = newName;
					RegisterChange();
				} else {
					NOTIFY_ERROR( IError_v1::kEDDataModel, kDMECNodeAlreadyExists,
						"One of the sibling have same name and nameSpace combination", IError_v1::kESOperationFatal,
						true, mNameSpace->c_str(), true, mName->c_str(), true, mNameSpace->c_str(), true, newName->c_str() );
				}
			} else {
				AutoSharedLock( mSharedMutex, true );
				mName = newName;
				RegisterChange();
			}
		}
	}

	spcIUTF8String APICALL NodeImpl::GetName() const {
		if ( this->IsArrayItem() )
			return mpParent->GetName();
		AutoSharedLock lock( mSharedMutex );
		return mName;
	}

	void APICALL NodeImpl::SetNameSpace( const char * nameSpace, sizet nameSpaceLength ) {
		if ( VerifyNameSpace( nameSpace, nameSpaceLength ) ) {
			spIUTF8String newNameSpace = IUTF8String_I::CreateUTF8String( nameSpace, nameSpaceLength );
			if ( mNameSpace->compare( newNameSpace ) == 0 ) return;
			if ( mpParent ) {
				if ( mpParent->GetINode_I()->ValidateNameOrNameSpaceChangeForAChild( mNameSpace, mName, newNameSpace, mName ) ) {
					AutoSharedLock( mSharedMutex, true );
					mNameSpace = newNameSpace;
					RegisterChange();
				} else {
					NOTIFY_ERROR( IError_v1::kEDDataModel, kDMECNodeAlreadyExists,
						"One of the sibling have same name and nameSpace combination", IError_v1::kESOperationFatal,
						true, mNameSpace->c_str(), true, mName->c_str(), true, newNameSpace->c_str(), true, mName->c_str() );
				}
			} else {
				AutoSharedLock( mSharedMutex, true );
				mNameSpace = newNameSpace;
				RegisterChange();
			}
		}
	}

	spcIUTF8String APICALL NodeImpl::GetNameSpace() const {
		if ( this->IsArrayItem() )
			return( mpParent->GetNameSpace() );
		AutoSharedLock lock( mSharedMutex );
		return mNameSpace;
	}

	spIPath APICALL NodeImpl::GetPath() const {
		typedef std::vector< pcINode, TAllocator< pcINode > > pcINodeList;
		pcINodeList pathToParent;

		pcINode node = this;

		while ( node != NULL ) {
			pcINode_I node_I = node->GetINode_I();
		
			pathToParent.push_back( node );
			node = node_I->GetRawParentPointer();
		}
		
		// reverse the array
		std::reverse( pathToParent.begin(), pathToParent.end() );
		spIPath xmpPath = IPath::CreatePath();
		pcINode parent = NULL;

		sizet nElements = pathToParent.size();
		for ( sizet i = 0; i < nElements; i++ ) {
			if ( pathToParent[ i ]->GetINode_I()->GetRawParentPointer() == NULL ) {
				continue;
			}

			spcIPathSegment segment = CreatePathSegmentToParent( pathToParent[ i ] );

			if ( segment ) {
				xmpPath->AppendPathSegment( segment );
			}

			parent = pathToParent[ i ];
		}
		return xmpPath;
	}

	spINode APICALL NodeImpl::GetQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return spINode();
		}
		return mQualifiers->GetNode( nameSpace, nameSpaceLength, name, nameLength );
	}

	spINode APICALL NodeImpl::GetQualifier( const spcIUTF8String & nameSpace, const spcIUTF8String & name ) {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return spINode();
		}
		return mQualifiers->GetIStructureNode_I()->GetNode( nameSpace, name );
	}

	void APICALL NodeImpl::InsertQualifier( const spINode & node ) {
		CreateQualifierNode();
		mQualifiers->InsertNode( node );
		node->GetINode_I()->SetIsQualifierNode( true );
	}

	spINode APICALL NodeImpl::ReplaceQualifier( const spINode & node ) {
		CreateQualifierNode();
		auto retValue = mQualifiers->ReplaceNode( node );
		node->GetINode_I()->SetIsQualifierNode( true );
		return retValue;
	}

	spINode APICALL NodeImpl::RemoveQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) {
		CreateQualifierNode();
		return mQualifiers->RemoveNode( nameSpace, nameSpaceLength, name, nameLength );
	}

	spINode APICALL NodeImpl::RemoveQualifier( const spcIUTF8String & nameSpace, const spcIUTF8String & name ) {
		CreateQualifierNode();
		return mQualifiers->GetIStructureNode_I()->RemoveQualifier( nameSpace, name );
	}

	bool APICALL NodeImpl::IsArrayItem() const {
		AutoSharedLock lock( mSharedMutex );
		return mIndex > 0;
	}

	bool APICALL NodeImpl::HasQualifiers() const {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return false;
		}
		return mQualifiers->ChildCount() > 0;
	}

	bool APICALL NodeImpl::IsEmpty() const {
		return !HasContent() && !HasQualifiers();
	}

	bool APICALL NodeImpl::HasChanged() const {
		return mChangeCount != 0;
	}

	void APICALL NodeImpl::AcknowledgeChanges() const __NOTHROW__ {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( mChangeCount > 1 && mpParent )
				mpParent->GetINode_I()->UnRegisterChange();
		}
		
		resetChangesForChildren();

		{
			AutoSharedLock lock( mSharedMutex );
			if ( mQualifiers )
				mQualifiers->AcknowledgeChanges();
		}
		AutoSharedLock lock( mSharedMutex, true );
		mChangeCount = 0;
	}

	void APICALL NodeImpl::Clear( bool contents, bool qualifiers ) {
		if ( qualifiers && mQualifiers ) {
			mQualifiers->Clear();
		}
		if ( contents ) {
			ClearContents();
			RegisterChange();
		}
	}

	spINode APICALL NodeImpl::Clone( bool ignoreEmptyNodes, bool ignoreNodesWithOnlyQualifiers ) const {
		AutoSharedLock lock( mSharedMutex );

		sizet qualifiersCount( mQualifiers ? mQualifiers->ChildCount() : 0 );
		
		spIStructureNode qualifierNode;
		// copy the qualifiers
		if ( qualifiersCount > 0 ) {
			auto node = mQualifiers->Clone( ignoreEmptyNodes, ignoreNodesWithOnlyQualifiers );
			if ( node ) qualifierNode = node->ConvertToStructureNode();
			qualifiersCount = qualifierNode ? qualifierNode->ChildCount() : 0;
		}
		
		spINode newNode = CloneContents( ignoreEmptyNodes, ignoreNodesWithOnlyQualifiers, qualifiersCount );

		if ( newNode )
			newNode->GetINode_I()->SetIsQualifierNode( newNode->IsQualifierNode() );

		// set the qualifier
		if ( newNode && qualifierNode && qualifiersCount > 0 ) {
			newNode->GetINode_I()->SetQualifiers( qualifierNode );
		}

		return newNode;
	}

	void NodeImpl::updateParentSharedPointer( bool calledFromRelease ) {
		if ( !calledFromRelease ) {
			if ( !mspParent && mRefCount > 1 && mpParent )
				mspParent = MakeUncheckedSharedPointer( mpParent, __FILE__, __LINE__, true );
		} else {
			if ( mspParent && mRefCount <= 2 )
				mspParent.reset();
		}
	}

	void NodeImpl::CreateQualifierNode() {
		AutoSharedLock( mSharedMutex, true );
		if ( !mQualifiers ) {
			mQualifiers = IStructureNode::CreateStructureNode( kQualifierNodeNameSpace, kQualifierNodeLocalNameLength, kQualifierNodeLocalName, kQualifierNodeLocalNameLength );
			mQualifiers->GetINode_I()->ChangeParent( this );
		}
	}

	void NodeImpl::SetQualifiers( const spIStructureNode & node ) {
		AutoSharedLock( mSharedMutex, true );
		mQualifiers = node;
	}

	sizet APICALL NodeImpl::QualifiersCount() const __NOTHROW__ {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return 0;
		}
		return mQualifiers->ChildCount();
	}

	spINodeIterator APICALL NodeImpl::QualifiersIterator() {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return spINodeIterator();
		}
		return mQualifiers->Iterator();
	}

	bool NodeImpl::ValidateNameOrNameSpaceChangeForAChild( const spcIUTF8String & currentNameSpace, const spcIUTF8String & currentName, const spcIUTF8String & newNameSpace, const spcIUTF8String & newName ) {
		return true;
	}

	INode_v1::eNodeType APICALL NodeImpl::GetParentNodeType() const {
		if ( mpParent )
			return mpParent->GetNodeType();
		return INode_v1::kNTNone;
	}

	INode_v1::eNodeType APICALL NodeImpl::GetQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const {
		{
			AutoSharedLock lock( mSharedMutex );
			if ( !mQualifiers ) return INode::kNTNone;
		}
		return mQualifiers->GetChildNodeType( nameSpace, nameSpaceLength, name, nameLength );
	}

	spISimpleNode APICALL NodeImpl::ConvertToSimpleNode() {
		return spISimpleNode();
	}

	spIStructureNode APICALL NodeImpl::ConvertToStructureNode() {
		return spIStructureNode();
	}

	spIArrayNode APICALL NodeImpl::ConvertToArrayNode() {
		return spIArrayNode();
	}

	spIMetadata APICALL NodeImpl::ConvertToMetadata() {
		return spIMetadata();
	}

	void NodeImpl::UnRegisterChange() {
		if ( mChangeCount > 0 )
			mChangeCount--;
		if ( mChangeCount == 0 && mpParent ) {
			mpParent->GetINode_I()->UnRegisterChange();
		}
	}

	void APICALL NodeImpl::Acquire() const __NOTHROW__ {
		SharedObjectImpl::Acquire();
		AutoSharedLock lock( mSharedMutex, true );
		const_cast< NodeImpl * >( this )->updateParentSharedPointer();
	}

	void APICALL NodeImpl::Release() const __NOTHROW__ {
		AutoSharedLock lock( mSharedMutex, true );
		const_cast< NodeImpl * >( this )->updateParentSharedPointer( true );
		SharedObjectImpl::Release();
	}

	void APICALL NodeImpl::AcquireInternal() const __NOTHROW__ {
		SharedObjectImpl::AcquireInternal();
		AutoSharedLock lock( mSharedMutex, true );
		const_cast< NodeImpl * >( this )->updateParentSharedPointer();
	}

	void NodeImpl::RegisterChange() {
		mChangeCount++;
		if ( mChangeCount == 1 ) {
			auto parent = GetRawParentPointer();
			if ( parent ) parent->GetINode_I()->RegisterChange();
		}
	}

	void NodeImpl::SetIndex( sizet currentIndex ) {
		mIndex = currentIndex;
	}

	void NodeImpl::SetIsQualifierNode( bool isQualifierNode ) {
		mIsQualifierNode = isQualifierNode;
	}

	bool APICALL NodeImpl::IsQualifierNode() const {
		return mIsQualifierNode;
	}

	sizet APICALL NodeImpl::GetIndex() const {
		return mIndex;
	}

}

namespace AdobeXMPCore {
	using namespace AdobeXMPCore_Int;
	spINode INode_v1::MakeShared( pINode_base ptr ) {
		if ( !ptr ) return spINode();
		pINode p = INode::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< INode >() : ptr;
		return MakeUncheckedSharedPointer( p, __FILE__, __LINE__, false );
	}

}