Skip to content

Commit

Permalink
Move SubSemanticData to its own class (#2177)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwjames committed Jan 15, 2017
1 parent f2f6e83 commit 470987a
Show file tree
Hide file tree
Showing 9 changed files with 651 additions and 221 deletions.
126 changes: 32 additions & 94 deletions includes/SemanticData.php
Expand Up @@ -10,6 +10,7 @@
use SMWPropertyValue;
use SMW\Exception\SemanticDataImportException;
use SMW\Exception\SubSemanticDataException;
use SMW\DataModel\SubSemanticData;

/**
* Class for representing chunks of semantic data for one given
Expand Down Expand Up @@ -108,9 +109,9 @@ class SemanticData {
* These key-value pairs of subObjectName (string) =>SMWSemanticData.
*
* @since 1.8
* @var SemanticData[]
* @var SubSemanticData
*/
protected $subSemanticData = array();
protected $subSemanticData;

/**
* Internal flag that indicates if this semantic data will accept
Expand Down Expand Up @@ -142,15 +143,12 @@ class SemanticData {
private $lastModified = null;

/**
* Maximum depth for an recursive sub data assignment
* @var integer
*/
private $subContainerMaxDepth = 2;

/**
* This is kept public to keep track of the depth during a recursive processing
* when accessed through the SubSemanticData instance.
*
* @var integer
*/
private $subContainerDepthCounter = 0;
public $subContainerDepthCounter = 0;

/**
* Constructor.
Expand All @@ -162,6 +160,7 @@ public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
$this->clear();
$this->mSubject = $subject;
$this->mNoDuplicates = $noDuplicates;
$this->subSemanticData = new SubSemanticData( $subject, $noDuplicates );
}

/**
Expand Down Expand Up @@ -280,7 +279,16 @@ public function getHash() {
* @return SMWContainerSemanticData[] subobject => SMWContainerSemanticData
*/
public function getSubSemanticData() {
return $this->subSemanticData;
return $this->subSemanticData->getSubSemanticData();
}

/**
* Return the array of subSemanticData objects for this SemanticData
*
* @since 2.5
*/
public function clearSubSemanticData() {
$this->subSemanticData !== null ? $this->subSemanticData->clear() : '';
}

/**
Expand Down Expand Up @@ -356,7 +364,7 @@ public function addPropertyObjectValue( DIProperty $property, SMWDataItem $dataI

// Inherit the sortkey from the root if not explicitly given
if ( $this->mSubject->getSubobjectName() === '' && $property->getKey() === DIProperty::TYPE_SORTKEY ) {
foreach ( $this->subSemanticData as $subSemanticData ) {
foreach ( $this->getSubSemanticData() as $subSemanticData ) {
if ( !$subSemanticData->hasProperty( $property ) ) {
$subSemanticData->addPropertyObjectValue( $property, $dataItem );
}
Expand Down Expand Up @@ -494,7 +502,7 @@ public function clear() {
$this->mHasVisibleProps = false;
$this->mHasVisibleSpecs = false;
$this->stubObject = false;
$this->subSemanticData = array();
$this->clearSubSemanticData();
$this->hash = null;
}

Expand Down Expand Up @@ -587,120 +595,50 @@ public function removeDataFrom( SemanticData $semanticData ) {
}

/**
* Whether the SemanticData has a SubSemanticData container and if
* specified has a particular subobject using its name as identifier
*
* @see SubSemanticData::hasSubSemanticData
* @since 1.9
*
* @param string $subobjectName|null
*
* @return boolean
*/
public function hasSubSemanticData( $subobjectName = null ) {

if ( $this->subSemanticData === array() || $subobjectName === '' ) {
return false;
}

return $subobjectName !== null ? isset( $this->subSemanticData[$subobjectName] ) : true;
return $this->subSemanticData->hasSubSemanticData( $subobjectName );
}

/**
* Find a particular subobject container using its name as identifier
*
* @see SubSemanticData::findSubSemanticData
* @since 1.9
*
* @param string $subobjectName
*
* @return SMWContainerSemanticData|[]
*/
public function findSubSemanticData( $subobjectName ) {

if ( $this->hasSubSemanticData( $subobjectName ) && isset( $this->subSemanticData[$subobjectName] ) ) {
return $this->subSemanticData[$subobjectName];
}

return array();
return $this->subSemanticData->findSubSemanticData( $subobjectName );
}

/**
* Add data about subobjects.
* Will only work if the data that is added is about a subobject of
* this SMWSemanticData's subject. Otherwise an exception is thrown.
* The SMWSemanticData object that is given will belong to this object
* after the operation; it should not be modified further by the caller.
*
* @see SubSemanticData::addSubSemanticData
* @since 1.8
*
* @param SemanticData $semanticData
*
* @throws SubSemanticDataException if not adding data about a subobject of this data
* @throws SubSemanticDataException
*/
public function addSubSemanticData( SemanticData $semanticData ) {

$semanticData->setLastModified( $this->getLastModified() );
$this->hash = null;

if ( $this->subContainerDepthCounter > $this->subContainerMaxDepth ) {
throw new SubSemanticDataException( "Cannot add further subdata. You are trying to add data beyond the max depth of {$this->subContainerMaxDepth} to an SemanticData object." );
}

$subobjectName = $semanticData->getSubject()->getSubobjectName();

if ( $subobjectName == '' ) {
throw new SubSemanticDataException( "Cannot add data that is not about a subobject." );
}

if( $semanticData->getSubject()->getDBkey() !== $this->getSubject()->getDBkey() ) {
throw new SubSemanticDataException( "Data for a subobject of {$semanticData->getSubject()->getDBkey()} cannot be added to {$this->getSubject()->getDBkey()}." );
}

if( $this->hasSubSemanticData( $subobjectName ) ) {
$this->subSemanticData[$subobjectName]->importDataFrom( $semanticData );

foreach ( $semanticData->getSubSemanticData() as $subsubdata ) {
$this->addSubSemanticData( $subsubdata );
}
} else {
$semanticData->subContainerDepthCounter++;
foreach ( $semanticData->getSubSemanticData() as $subsubdata ) {

// Skip container that are known to be registered (avoids recursive statement extension)
if ( $this->hasSubSemanticData( $subsubdata->getSubject()->getSubobjectName() ) ) {
continue;
}

$this->addSubSemanticData( $subsubdata );
}
$semanticData->subSemanticData = array();
$this->subSemanticData[$subobjectName] = $semanticData;
}
$this->subSemanticData->addSubSemanticData( $semanticData );
}

/**
* Remove data about a subobject.
* If the removed data is not about a subobject of this object,
* it will silently be ignored (nothing to remove). Likewise,
* removing data that is not present does not change anything.
*
* @since 1.8
* @param SMWSemanticData
* @see SubSemanticData::removeSubSemanticData
* @since 1.8
*
* @param SemanticData $semanticData
*/
public function removeSubSemanticData( SemanticData $semanticData ) {
if( $semanticData->getSubject()->getDBkey() !== $this->getSubject()->getDBkey() ) {
return;
}

$this->hash = null;
$subobjectName = $semanticData->getSubject()->getSubobjectName();

if( $this->hasSubSemanticData( $subobjectName ) ) {
$this->subSemanticData[$subobjectName]->removeDataFrom( $semanticData );

if( $this->subSemanticData[$subobjectName]->isEmpty() ) {
unset( $this->subSemanticData[$subobjectName] );
}
}
$this->subSemanticData->removeSubSemanticData( $semanticData );
}

/**
Expand Down
117 changes: 0 additions & 117 deletions includes/dataitems/SMW_DI_Container.php
Expand Up @@ -7,123 +7,6 @@
use SMW\DIProperty;
use SMWDIBlob as DIBlob;

/**
* Subclass of SMWSemanticData that is used to store the data in SMWDIContainer
* objects. It is special since the subject that the stored property-value pairs
* refer may or may not be specified explicitly. This can be tested with
* hasAnonymousSubject(). When trying to access the subject in anonymous state,
* an Exception will be thrown. Anonymous container data items are used when no
* page context is available, e.g. when specifying such a value in a search form
* where the parent page is not known.
*
* Besides this change, the subclass mainly is needed to restroe the disabled
* serialization of SMWSemanticData.
*
* See also the documentation of SMWDIContainer.
*
* @since 1.6
*
* @author Markus Kr枚tzsch
* @ingroup SMWDataItems
*/
class SMWContainerSemanticData extends SMWSemanticData {

/**
* @var boolean
*/
private $skipAnonymousCheck = false;

/**
* Construct a data container that refers to an anonymous subject. See
* the documentation of the class for details.
*
* @since 1.7
*
* @param boolean $noDuplicates stating if duplicate data should be avoided
*/
public static function makeAnonymousContainer( $noDuplicates = true, $skipAnonymousCheck = false ) {
$subject = new SMWDIWikiPage( 'SMWInternalObject', NS_SPECIAL, '', 'int' );
$containerSemanticData = new SMWContainerSemanticData( $subject, $noDuplicates );

if ( $skipAnonymousCheck ) {
$containerSemanticData->skipAnonymousCheck();
}

return $containerSemanticData;
}

/**
* Restore complete serialization which is disabled in SMWSemanticData.
*/
public function __sleep() {
return array( 'mSubject', 'mProperties', 'mPropVals',
'mHasVisibleProps', 'mHasVisibleSpecs', 'mNoDuplicates', 'skipAnonymousCheck' );
}

/**
* @since 2.4
*/
public function skipAnonymousCheck() {
$this->skipAnonymousCheck = true;
}

/**
* Check if the subject of this container is an anonymous object.
* See the documenation of the class for details.
*
* @return boolean
*/
public function hasAnonymousSubject() {
if ( $this->mSubject->getNamespace() == NS_SPECIAL &&
$this->mSubject->getDBkey() == 'SMWInternalObject' &&
$this->mSubject->getInterwiki() === '' &&
$this->mSubject->getSubobjectName() === 'int' ) {
return true;
} else {
return false;
}
}

/**
* Return subject to which the stored semantic annotation refer to, or
* throw an exception if the subject is anonymous (if the data has not
* been contextualized with setMasterPage() yet).
*
* @return SMWDIWikiPage subject
*/
public function getSubject() {

if ( !$this->skipAnonymousCheck && $this->hasAnonymousSubject() ) {
throw new DataItemException("This container has been classified as anonymous and by trying to access its subject (that has not been given any) an exception is raised to inform about the incorrect usage. An anonymous container can only be used for a search pattern match.");
}

return $this->mSubject;
}

/**
* Change the object to become an exact copy of the given
* SMWSemanticData object. This is used to make other types of
* SMWSemanticData into an SMWContainerSemanticData. To copy objects of
* the same type, PHP clone() should be used.
*
* @since 1.7
*
* @param $semanticData SMWSemanticData object to copy from
*/
public function copyDataFrom( SMWSemanticData $semanticData ) {
$this->mSubject = $semanticData->getSubject();
$this->mProperties = $semanticData->getProperties();
$this->mPropVals = array();
foreach ( $this->mProperties as $property ) {
$this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
}
$this->mHasVisibleProps = $semanticData->hasVisibleProperties();
$this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
$this->mNoDuplicates = $semanticData->mNoDuplicates;
}

}

/**
* This class implements container data items that can store SMWSemanticData
* objects. Containers are not dataitems in the proper sense: they do not
Expand Down

0 comments on commit 470987a

Please sign in to comment.