SemanticData   F
last analyzed

Complexity

Total Complexity 80

Size/Duplication

Total Lines 700
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 91.53%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 700
ccs 162
cts 177
cp 0.9153
rs 3.4285
wmc 80
lcom 1
cbo 12

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A __sleep() 0 3 1
A getSubject() 0 3 1
A getProperties() 0 4 1
A hasProperty() 0 3 2
A getPropertyValues() 0 11 3
A getOption() 0 8 3
A setOption() 0 8 2
A getErrors() 0 3 1
A addError() 0 3 1
A getHash() 0 8 2
A getSubSemanticData() 0 14 2
A clearSubSemanticData() 0 8 3
A hasVisibleProperties() 0 3 1
A hasVisibleSpecialProperties() 0 3 1
D addPropertyObjectValue() 0 46 12
B addPropertyValue() 0 22 4
A addDataValue() 0 21 3
A addSubobject() 0 6 1
D removePropertyObjectValue() 0 35 9
C removeProperty() 0 26 7
A clear() 0 10 1
A isEmpty() 0 3 2
C importDataFrom() 0 35 8
A removeDataFrom() 0 13 4
A hasSubSemanticData() 0 3 1
A findSubSemanticData() 0 3 1
A addSubSemanticData() 0 4 1
A removeSubSemanticData() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like SemanticData often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SemanticData, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SMW;
4
5
use MWException;
6
use SMWContainerSemanticData;
7
use SMWDataItem;
8
use SMWDataValue;
9
use SMWDIContainer;
10
use SMWPropertyValue;
11
use SMW\Exception\SemanticDataImportException;
12
use SMW\DataModel\SubSemanticData;
13
14
/**
15
 * Class for representing chunks of semantic data for one given
16
 * subject. This consists of property-value pairs, grouped by property,
17
 * and possibly by SMWSemanticData objects about subobjects.
18
 *
19
 * Data about subobjects can be added in two ways: by directly adding it
20
 * using addSubSemanticData() or by adding a property value of type
21
 * SMWDIContainer.
22
 *
23
 * By its very design, the container is unable to hold inverse properties.
24
 * For one thing, it would not be possible to identify them with mere keys.
25
 * Since SMW cannot annotate pages with inverses, this is not a limitation.
26
 *
27
 * @ingroup SMW
28
 *
29
 * @author Markus Krötzsch
30
 * @author Jeroen De Dauw
31
 */
32
class SemanticData {
33
34
	/**
35
	 * Returns the last modified timestamp the data were stored to the Store or
36
	 * have been fetched from cache.
37
	 */
38
	const OPT_LAST_MODIFIED = 'opt.last.modified';
39
40
	/**
41
	 * Cache for the localized version of the namespace prefix "Property:".
42
	 *
43
	 * @var string
44
	 */
45
	static protected $mPropertyPrefix = '';
46
47
	/**
48
	 * States whether this is a stub object. Stubbing might happen on
49
	 * serialisation to save DB space.
50
	 *
51
	 * @todo Check why this is public and document this here. Or fix it.
52
	 *
53
	 * @var boolean
54
	 */
55
	public $stubObject;
56
57
	/**
58
	 * Array mapping property keys (string) to arrays of SMWDataItem
59
	 * objects.
60
	 *
61
	 * @var SMWDataItem[]
62
	 */
63
	protected $mPropVals = array();
64
65
	/**
66
	 * Array mapping property keys (string) to DIProperty objects.
67
	 *
68
	 * @var DIProperty[]
69
	 */
70
	protected $mProperties = array();
71
72
	/**
73
	 * States whether the container holds any normal properties.
74
	 *
75
	 * @var boolean
76
	 */
77
	protected $mHasVisibleProps = false;
78
79
	/**
80
	 * States whether the container holds any displayable predefined
81
	 * $mProperties (as opposed to predefined properties without a display
82
	 * label). For some settings we need this to decide if a Factbox is
83
	 * displayed.
84
	 *
85
	 * @var boolean
86
	 */
87
	protected $mHasVisibleSpecs = false;
88
89
	/**
90
	 * States whether repeated values should be avoided. Not needing
91
	 * duplicate elimination (e.g. when loading from store) can save some
92
	 * time, especially in subclasses like SMWSqlStubSemanticData, where
93
	 * the first access to a data item is more costy.
94
	 *
95
	 * @note This setting is merely for optimization. The SMW data model
96
	 * never cares about the multiplicity of identical data assignments.
97
	 *
98
	 * @var boolean
99
	 */
100
	protected $mNoDuplicates;
101
102
	/**
103
	 * DIWikiPage object that is the subject of this container.
104
	 * Subjects can never be null (and this is ensured in all methods setting
105
	 * them in this class).
106
	 *
107
	 * @var DIWikiPage
108
	 */
109
	protected $mSubject;
110
111
	/**
112
	 * Semantic data associated to subobjects of the subject of this
113
	 * SMWSemanticData.
114
	 * These key-value pairs of subObjectName (string) =>SMWSemanticData.
115
	 *
116
	 * @since 1.8
117
	 * @var SubSemanticData
118
	 */
119
	protected $subSemanticData;
120
121
	/**
122
	 * Internal flag that indicates if this semantic data will accept
123
	 * subdata. Semantic data objects that are subdata already do not allow
124
	 * (second level) subdata to be added. This ensures that all data is
125
	 * collected on the top level, and in particular that there is only one
126
	 * way to represent the same data with subdata. This is also useful for
127
	 * diff computation.
128
	 */
129
	protected $subDataAllowed = true;
130
131
	/**
132
	 * @var array
133
	 */
134
	protected $errors = array();
135
136
	/**
137
	 * Cache the hash to ensure a minimal impact in case of repeated usage. Any
138
	 * removal or insert action will reset the hash to null to ensure it is
139
	 * recreated in corresponds to changed nature of the data.
140
	 *
141
	 * @var string|null
142
	 */
143
	private $hash = null;
144
145
	/**
146
	 * @var Options
147
	 */
148
	private $options = null;
149
150
	/**
151
	 * This is kept public to keep track of the depth during a recursive processing
152
	 * when accessed through the SubSemanticData instance.
153
	 *
154
	 * @var integer
155
	 */
156
	public $subContainerDepthCounter = 0;
157
158
	/**
159 326
	 * Constructor.
160 326
	 *
161 326
	 * @param DIWikiPage $subject to which this data refers
162 326
	 * @param boolean $noDuplicates stating if duplicate data should be avoided
163 326
	 */
164 326
	public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
165
		$this->clear();
166
		$this->mSubject = $subject;
167
		$this->mNoDuplicates = $noDuplicates;
168
		$this->subSemanticData = new SubSemanticData( $subject, $noDuplicates );
169
	}
170
171
	/**
172
	 * This object is added to the parser output of MediaWiki, but it is
173
	 * not useful to have all its data as part of the parser cache since
174
	 * the data is already stored in more accessible format in SMW. Hence
175
	 * this implementation of __sleep() makes sure only the subject is
176
	 * serialised, yielding a minimal stub data container after
177
	 * unserialisation. This is a little safer than serialising nothing:
178
	 * if, for any reason, SMW should ever access an unserialised parser
179 270
	 * output, then the Semdata container will at least look as if properly
180 270
	 * initialised (though empty).
181
	 *
182
	 * @return array
183
	 */
184
	public function __sleep() {
185
		return array( 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mHasVisibleProps', 'mHasVisibleSpecs', 'options' );
186
	}
187
188 302
	/**
189 302
	 * Return subject to which the stored semantic annotations refer to.
190
	 *
191
	 * @return DIWikiPage subject
192
	 */
193
	public function getSubject() {
194
		return $this->mSubject;
195
	}
196
197 300
	/**
198 300
	 * Get the array of all properties that have stored values.
199 300
	 *
200
	 * @return array of DIProperty objects
201
	 */
202
	public function getProperties() {
203
		ksort( $this->mProperties, SORT_STRING );
204
		return $this->mProperties;
205
	}
206
207
	/**
208
	 * @since 2.4
209 268
	 *
210 268
	 * @param DIProperty $property
211
	 *
212
	 * @return boolean
213
	 */
214
	public function hasProperty( DIProperty $property ) {
215
		return isset( $this->mProperties[$property->getKey()] ) || array_key_exists( $property->getKey(), $this->mProperties );
216
	}
217
218
	/**
219 295
	 * Get the array of all stored values for some property.
220 295
	 *
221 1
	 * @param DIProperty $property
222
	 * @return SMWDataItem[]
223
	 */
224 295
	public function getPropertyValues( DIProperty $property ) {
225 279
		if ( $property->isInverse() ) { // we never have any data for inverses
226
			return array();
227
		}
228 277
229
		if ( array_key_exists( $property->getKey(), $this->mPropVals ) ) {
230
			return array_values( $this->mPropVals[$property->getKey()] );
231
		}
232
233
		return array();
234
	}
235
236
	/**
237
	 * @since 2.5
238 89
	 *
239 89
	 * @param string $key
240
	 *
241
	 * @return mixed
242
	 */
243
	public function getOption( $key ) {
244
245
		if ( !$this->options instanceof Options ) {
246
			$this->options = new Options();
247
		}
248
249 41
		return $this->options->has( $key ) ? $this->options->get( $key ) : null;
250 41
	}
251 41
252
	/**
253
	 * @since 2.5
254
	 *
255
	 * @param string $key
256
	 * @param string $value
257
	 */
258
	public function setOption( $key, $value ) {
259
260
		if ( !$this->options instanceof Options ) {
261
			$this->options = new Options();
262
		}
263
264
		return $this->options->set( $key, $value );
265
	}
266 13
267
	/**
268 13
	 * Returns collected errors occurred during processing
269
	 *
270
	 * @since 1.9
271
	 *
272 13
	 * @return array
273
	 */
274
	public function getErrors() {
275
		return $this->errors;
276
	}
277
278
	/**
279
	 * Adds an error array
280
	 *
281
	 * @since  1.9
282 301
	 *
283
	 * @return array|string
284
	 */
285 301
	public function addError( $error ) {
286
		$this->errors = array_merge( $this->errors, (array)$error );
287
	}
288
289 301
	/**
290
	 * Generate a hash value to simplify the comparison of this data
291
	 * container with other containers. Subdata is taken into account.
292
	 *
293
	 * The hash uses PHP's md5 implementation, which is among the fastest
294 301
	 * hash algorithms that PHP offers.
295
	 *
296
	 * @note This function may be used to obtain keys for SemanticData
297
	 * objects or to do simple equality tests. Equal hashes with very
298
	 * high probability indicate equal data.
299
	 *
300
	 * @return string
301
	 */
302 326
	public function getHash() {
303 326
304 326
		if ( $this->hash !== null ) {
305
			return $this->hash;
306
		}
307
308
		return $this->hash = Hash::createFromSemanticData( $this );
309
	}
310
311
	/**
312
	 * @see SubSemanticData::getSubSemanticData
313
	 *
314
	 * @since 1.8
315 26
	 *
316 26
	 * @return ContainerSemanticData[]
317
	 */
318
	public function getSubSemanticData() {
319
320
		// Remove the check in 3.0
321
		$subSemanticData = $this->subSemanticData;
322
323
		// Avoids an issue where the serialized array from a previous usage is
324
		// returned from a __wakeup, where now a SubSemanticData (#2177) is expected.
325
		if ( !$subSemanticData instanceof SubSemanticData ) {
326
			$this->subSemanticData = new SubSemanticData( $this->mSubject, $this->mNoDuplicates );
327
			$this->subSemanticData->copyDataFrom( $subSemanticData );
328
		}
329 25
330 25
		return $this->subSemanticData->getSubSemanticData();
331
	}
332
333
	/**
334
	 * @since 2.5
335
	 */
336
	public function clearSubSemanticData() {
337
338
		if ( $this->subContainerDepthCounter > 0 ) {
339
			$this->subContainerDepthCounter--;
340
		}
341
342
		$this->subSemanticData !== null ? $this->subSemanticData->clear() : '';
343
	}
344 293
345
	/**
346 293
	 * Return true if there are any visible properties.
347
	 *
348 293
	 * @note While called "visible" this check actually refers to the
349 175
	 * function DIProperty::isShown(). The name is kept for
350 174
	 * compatibility.
351
	 *
352
	 * @return boolean
353 293
	 */
354
	public function hasVisibleProperties() {
355
		return $this->mHasVisibleProps;
356
	}
357 293
358 293
	/**
359 293
	 * Return true if there are any special properties that can
360
	 * be displayed.
361
	 *
362 293
	 * @note While called "visible" this check actually refers to the
363 293
	 * function DIProperty::isShown(). The name is kept for
364
	 * compatibility.
365
	 *
366
	 * @return boolean
367
	 */
368 293
	public function hasVisibleSpecialProperties() {
369 264
		return $this->mHasVisibleSpecs;
370 224
	}
371 264
372
	/**
373
	 * Store a value for a property identified by its SMWDataItem object.
374 245
	 *
375
	 * @note There is no check whether the type of the given data item
376
	 * agrees with the type of the property. Since property types can
377
	 * change, all parts of SMW are prepared to handle mismatched data item
378 293
	 * types anyway.
379 217
	 *
380 139
	 * @param $property DIProperty
381 139
	 * @param $dataItem SMWDataItem
382
	 */
383
	public function addPropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
384
385 293
		$this->hash = null;
386
387
		if( $dataItem instanceof SMWDIContainer ) {
388
			$this->addSubSemanticData( $dataItem->getSemanticData() );
389
			$dataItem = $dataItem->getSemanticData()->getSubject();
390
		}
391
392
		if( $property->getKey() === DIProperty::TYPE_MODIFICATION_DATE ) {
393
			$this->setOption( self::OPT_LAST_MODIFIED, $dataItem->getMwTimestamp() );
394 1
		}
395 1
396
		if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
397 1
			return;
398
		}
399
400 1
		if ( !array_key_exists( $property->getKey(), $this->mPropVals ) ) {
401 1
			$this->mPropVals[$property->getKey()] = array();
402 1
			$this->mProperties[$property->getKey()] = $property;
403
		}
404
405 1
		if ( $this->mNoDuplicates ) {
406
			$this->mPropVals[$property->getKey()][$dataItem->getHash()] = $dataItem;
407 1
		} else {
408
			$this->mPropVals[$property->getKey()][] = $dataItem;
409
		}
410
411 1
		if ( !$property->isUserDefined() ) {
412
			if ( $property->isShown() ) {
413
				$this->mHasVisibleSpecs = true;
414 1
				$this->mHasVisibleProps = true;
415 1
			}
416
		} else {
417
			$this->mHasVisibleProps = true;
418
		}
419
420
		// Inherit the sortkey from the root if not explicitly given
421
		if ( $this->mSubject->getSubobjectName() === '' && $property->getKey() === DIProperty::TYPE_SORTKEY ) {
422 238
			foreach ( $this->getSubSemanticData() as $subSemanticData ) {
423
				if ( !$subSemanticData->hasProperty( $property ) ) {
424 238
					$subSemanticData->addPropertyObjectValue( $property, $dataItem );
425
				}
426 34
			}
427 34
		}
428
	}
429
430 34
	/**
431
	 * Store a value for a given property identified by its text label
432 34
	 * (without namespace prefix).
433
	 *
434
	 * @param $propertyName string
435 34
	 * @param $dataItem SMWDataItem
436
	 */
437
	public function addPropertyValue( $propertyName, SMWDataItem $dataItem ) {
438 234
		$propertyKey = smwfNormalTitleDBKey( $propertyName );
439 234
440 234
		if ( array_key_exists( $propertyKey, $this->mProperties ) ) {
441
			$property = $this->mProperties[$propertyKey];
442 234
		} else {
443
			if ( self::$mPropertyPrefix === '' ) {
444
				global $wgContLang;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
445
				self::$mPropertyPrefix = $wgContLang->getNsText( SMW_NS_PROPERTY ) . ':';
446
			} // explicitly use prefix to cope with things like [[Property:User:Stupid::somevalue]]
447
448
			$propertyDV = SMWPropertyValue::makeUserProperty( self::$mPropertyPrefix . $propertyName );
449 72
450 72
			if ( !$propertyDV->isValid() ) { // error, maybe illegal title text
451 72
				return;
452 72
			}
453
454 71
			$property = $propertyDV->getDataItem();
455
		}
456
457
		$this->addPropertyObjectValue( $property, $dataItem );
0 ignored issues
show
Compatibility introduced by
$property of type object<SMWDataItem> is not a sub-type of object<SMW\DIProperty>. It seems like you assume a child class of the class SMWDataItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
458
	}
459
460
	/**
461
	 * @since 1.9
462
	 *
463
	 * @param SMWDataValue $dataValue
464
	 */
465
	public function addDataValue( SMWDataValue $dataValue ) {
466
467
		if ( !$dataValue->getProperty() instanceof DIProperty || !$dataValue->isValid() ) {
468
469
			$processingErrorMsgHandler = new ProcessingErrorMsgHandler(
470
				$this->getSubject()
471
			);
472 7
473
			$processingErrorMsgHandler->addToSemanticData(
474 7
				$this,
475
				$processingErrorMsgHandler->newErrorContainerFromDataValue( $dataValue )
476
			);
477 7
478 1
			return $this->addError( $dataValue->getErrors() );
479 1
		}
480
481
		$this->addPropertyObjectValue(
482 7
			$dataValue->getProperty(),
483
			$dataValue->getDataItem()
484
		);
485
	}
486 7
487
	/**
488
	 * @since 2.1
489
	 *
490 7
	 * @param Subobject $subobject
491
	 */
492 7
	public function addSubobject( Subobject $subobject ) {
493
		$this->addPropertyObjectValue(
494
			$subobject->getProperty(),
495
			$subobject->getContainer()
496
		);
497
	}
498
499
	/**
500
	 * Remove a value for a property identified by its SMWDataItem object.
501
	 * This method removes a property-value specified by the property and
502 7
	 * dataitem. If there are no more property-values for this property it
503 7
	 * also removes the property from the mProperties.
504 7
	 *
505
	 * @note There is no check whether the type of the given data item
506 7
	 * agrees with the type of the property. Since property types can
507
	 * change, all parts of SMW are prepared to handle mismatched data item
508
	 * types anyway.
509
	 *
510
	 * @param $property DIProperty
511 326
	 * @param $dataItem SMWDataItem
512 326
	 *
513 326
	 * @since 1.8
514 326
	 */
515 326
	public function removePropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
516 326
517 326
		$this->hash = null;
518 326
519 326
		//delete associated subSemanticData
520
		if( $dataItem instanceof SMWDIContainer ) {
521
			$this->removeSubSemanticData( $dataItem->getSemanticData() );
522
			$dataItem = $dataItem->getSemanticData()->getSubject();
523
		}
524
525
		if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
526
			return;
527
		}
528
529
		if ( !array_key_exists( $property->getKey(), $this->mPropVals ) || !array_key_exists( $property->getKey(), $this->mProperties ) ) {
530 224
			return;
531 224
		}
532
533
		if ( $this->mNoDuplicates ) {
534
			//this didn't get checked for my tests, but should work
535
			unset( $this->mPropVals[$property->getKey()][$dataItem->getHash()] );
536
		} else {
537
			foreach( $this->mPropVals[$property->getKey()] as $index => $di ) {
0 ignored issues
show
Bug introduced by
The expression $this->mPropVals[$property->getKey()] of type object<SMWDataItem> is not traversable.
Loading history...
538
				if( $di->equals( $dataItem ) ) {
539
					unset( $this->mPropVals[$property->getKey()][$index] );
540
				}
541
			}
542
			$this->mPropVals[$property->getKey()] = array_values( $this->mPropVals[$property->getKey()] );
543
		}
544
545 14
		if ( $this->mPropVals[$property->getKey()] === array() ) {
546
			unset( $this->mProperties[$property->getKey()] );
547 14
			unset( $this->mPropVals[$property->getKey()] );
548 1
		}
549
	}
550
551 13
	/**
552
	 * Removes a property and all the values associated with this property.
553
	 *
554
	 * @since 2.5
555 13
	 *
556 13
	 * @param $property DIProperty
557 3
	 */
558 3
	public function removeProperty( DIProperty $property ) {
559
560 3
		$this->hash = null;
561 3
		$key = $property->getKey();
562
563
		 // Inverse properties cannot be used for an annotation
564 3
		if ( $property->isInverse() || !isset( $this->mProperties[$key] ) ) {
565 3
			return;
566
		}
567 10
568 10
		// Find and remove associated assignments (e.g. _ASK as subobject
569
		// contains _ASKSI ...)
570 10
		foreach ( $this->mPropVals[$key] as $dataItem ) {
0 ignored issues
show
Bug introduced by
The expression $this->mPropVals[$key] of type object<SMWDataItem> is not traversable.
Loading history...
571 10
572
			if ( !$dataItem instanceof DIWikiPage || $dataItem->getSubobjectName() === '' ) {
573
				continue;
574
			}
575
576 13
			if ( ( $subSemanticData = $this->findSubSemanticData( $dataItem->getSubobjectName() ) ) !== null ) {
577 2
				$this->removeSubSemanticData( $subSemanticData );
578
			}
579 13
		}
580
581
		unset( $this->mPropVals[$key] );
582
		unset( $this->mProperties[$key] );
583
	}
584
585
	/**
586
	 * Delete all data other than the subject.
587
	 */
588
	public function clear() {
589
		$this->mPropVals = array();
590
		$this->mProperties = array();
591
		$this->mHasVisibleProps = false;
592 3
		$this->mHasVisibleSpecs = false;
593 3
		$this->stubObject = false;
594
		$this->clearSubSemanticData();
595
		$this->hash = null;
596
		$this->options = null;
597 3
	}
598 3
599 3
	/**
600 3
	 * Return true if this SemanticData is empty.
601
	 * This is the case when the subject has neither property values nor
602
	 * data for subobjects.
603
	 *
604 3
	 * @since 1.8
605
	 *
606
	 * @return boolean
607 3
	 */
608
	public function isEmpty() {
609
		return $this->getProperties() === array() && $this->getSubSemanticData() === array();
610
	}
611
612
	/**
613
	 * Add all data from the given SMWSemanticData.
614
	 * Only works if the imported SMWSemanticData has the same subject as
615
	 * this SMWSemanticData; an exception is thrown otherwise.
616
	 *
617 154
	 * @since 1.7
618 154
	 *
619
	 * @param SemanticData $semanticData object to copy from
620
	 *
621
	 * @throws SemanticDataImportException
622
	 */
623
	public function importDataFrom( SemanticData $semanticData ) {
624
625
		if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
626
			throw new SemanticDataImportException( "SemanticData can only represent data about one subject. Importing data for another subject is not possible." );
627
		}
628
629 6
		$this->hash = null;
630 6
631
		// Shortcut when copying into empty objects that don't ask for
632
		// more duplicate elimination:
633
		if ( count( $this->mProperties ) == 0 &&
634
		     ( $semanticData->mNoDuplicates >= $this->mNoDuplicates ) ) {
635
			$this->mProperties = $semanticData->getProperties();
636
			$this->mPropVals = array();
637
638
			foreach ( $this->mProperties as $property ) {
639
				$this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
640 178
			}
641 178
642 178
			$this->mHasVisibleProps = $semanticData->hasVisibleProperties();
643 176
			$this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
644
		} else {
645
			foreach ( $semanticData->getProperties() as $property ) {
646
				$values = $semanticData->getPropertyValues( $property );
647
648
				foreach ( $values as $dataItem ) {
649
					$this->addPropertyObjectValue( $property, $dataItem);
650
				}
651 3
			}
652 3
		}
653 3
654 3
		foreach( $semanticData->getSubSemanticData() as $semData ) {
655
			$this->addSubSemanticData( $semData );
656
		}
657
	}
658
659
	/**
660
	 * Removes data from the given SMWSemanticData.
661
	 * If the subject of the data that is to be removed is not equal to the
662
	 * subject of this SMWSemanticData, it will just be ignored (nothing to
663
	 * remove). Likewise, removing data that is not present does not change
664 256
	 * anything.
665
	 *
666 256
	 * @since 1.8
667 204
	 *
668
	 * @param SemanticData $semanticData
669
	 */
670 255
	public function removeDataFrom( SemanticData $semanticData ) {
671
		if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
672 255
			return;
673 180
		}
674
675
		foreach ( $semanticData->getProperties() as $property ) {
676 214
			$this->removeProperty( $property );
677
		}
678 214
679
		foreach( $semanticData->getSubSemanticData() as $semData ) {
680
			$this->removeSubSemanticData( $semData );
681
		}
682
	}
683
684
	/**
685
	 * @see SubSemanticData::hasSubSemanticData
686 272
	 * @since 1.9
687 272
	 *
688 272
	 * @param string $subobjectName|null
0 ignored issues
show
Documentation introduced by
There is no parameter named $subobjectName|null. Did you maybe mean $subobjectName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
689
	 *
690
	 * @return boolean
691
	 */
692
	public function hasSubSemanticData( $subobjectName = null ) {
693
		return $this->subSemanticData->hasSubSemanticData( $subobjectName );
694
	}
695
696
	/**
697
	 * @see SubSemanticData::findSubSemanticData
698
	 * @since 1.9
699
	 *
700
	 * @param string $subobjectName
701
	 *
702
	 * @return SMWContainerSemanticData|null
703
	 */
704
	public function findSubSemanticData( $subobjectName ) {
705
		return $this->subSemanticData->findSubSemanticData( $subobjectName );
706
	}
707
708
	/**
709
	 * @see SubSemanticData::addSubSemanticData
710
	 * @since 1.8
711
	 *
712
	 * @param SemanticData $semanticData
713
	 * @throws SubSemanticDataException
714
	 */
715
	public function addSubSemanticData( SemanticData $semanticData ) {
716
		$this->hash = null;
717
		$this->subSemanticData->addSubSemanticData( $semanticData );
718
	}
719
720
	/**
721
	 * @see SubSemanticData::removeSubSemanticData
722
	 * @since 1.8
723
	 *
724
	 * @param SemanticData $semanticData
725
	*/
726
	public function removeSubSemanticData( SemanticData $semanticData ) {
727
		$this->hash = null;
728
		$this->subSemanticData->removeSubSemanticData( $semanticData );
729
	}
730
731
}
732