summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php268
1 files changed, 268 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
new file mode 100644
index 00000000..9350a7c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
@@ -0,0 +1,268 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\SomeProperty;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWDataItem as DataItem;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class SomePropertyInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof SomeProperty;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $property = $description->getProperty();
+
+ list( $innerOrderByProperty, $innerCondition, $innerJoinVariable ) = $this->doResolveInnerConditionRecursively(
+ $property,
+ $description->getDescription()
+ );
+
+ if ( $innerCondition instanceof FalseCondition ) {
+ return new FalseCondition();
+ }
+
+ $namespaces = $innerCondition->namespaces;
+
+ $objectName = $this->findObjectNameFromInnerCondition(
+ $innerCondition,
+ $innerJoinVariable,
+ $namespaces
+ );
+
+ list ( $subjectName, $objectName, $nonInverseProperty ) = $this->doExchangeForWhenInversePropertyIsUsed(
+ $property,
+ $objectName,
+ $joinVariable
+ );
+
+ $propertyName = $this->findMostSuitablePropertyRepresentation(
+ $property,
+ $nonInverseProperty,
+ $namespaces
+ );
+
+ $this->tryToAddPropertyPathForSaturatedHierarchy(
+ $innerCondition,
+ $nonInverseProperty,
+ $propertyName,
+ $description->getHierarchyDepth()
+ );
+
+ $condition = $this->concatenateToConditionString(
+ $subjectName,
+ $propertyName,
+ $objectName,
+ $innerCondition
+ );
+
+ $result = new WhereCondition( $condition, true, $namespaces );
+
+ // Record inner ordering variable if found
+ $result->orderVariables = $innerCondition->orderVariables;
+
+ if ( $innerOrderByProperty !== null && $innerCondition->orderByVariable !== '' ) {
+ $result->orderVariables[$property->getKey()] = $innerCondition->orderByVariable;
+ }
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty,
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ return $result;
+ }
+
+ private function doResolveInnerConditionRecursively( DIProperty $property, Description $description ) {
+
+ $innerOrderByProperty = null;
+
+ // Find out if we should order by the values of this property
+ if ( array_key_exists( $property->getKey(), $this->conditionBuilder->getSortKeys() ) ) {
+ $innerOrderByProperty = $property;
+ }
+
+ // Prepare inner condition
+ $innerJoinVariable = $this->conditionBuilder->getNextVariable();
+
+ $this->conditionBuilder->setJoinVariable( $innerJoinVariable );
+ $this->conditionBuilder->setOrderByProperty( $innerOrderByProperty );
+
+ $innerCondition = $this->conditionBuilder->mapDescriptionToCondition(
+ $description
+ );
+
+ return [ $innerOrderByProperty, $innerCondition, $innerJoinVariable ];
+ }
+
+ private function findObjectNameFromInnerCondition( $innerCondition, $innerJoinVariable, &$namespaces ) {
+
+ if ( !$innerCondition instanceof SingletonCondition ) {
+ return '?' . $innerJoinVariable;
+ }
+
+ $matchElement = $innerCondition->matchElement;
+
+ if ( $matchElement instanceof ExpElement ) {
+ $objectName = TurtleSerializer::getTurtleNameForExpElement( $matchElement );
+ } else {
+ $objectName = $matchElement;
+ }
+
+ if ( $matchElement instanceof ExpNsResource ) {
+ $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
+ }
+
+ return $objectName;
+ }
+
+ private function findMostSuitablePropertyRepresentation( DIProperty $property, DIProperty $nonInverseProperty, &$namespaces ) {
+
+ $redirectByVariable = $this->conditionBuilder->tryToFindRedirectVariableForDataItem(
+ $nonInverseProperty->getDiWikiPage()
+ );
+
+ // If the property is represented by a redirect then use the variable instead
+ if ( $redirectByVariable !== null ) {
+ return $redirectByVariable;
+ }
+
+ // Use helper properties in encoding values, refer to this helper property:
+ if ( $this->exporter->hasHelperExpElement( $property ) ) {
+ $propertyExpElement = $this->exporter->getResourceElementForProperty( $nonInverseProperty, true );
+ } elseif( !$property->isUserDefined() ) {
+ $propertyExpElement = $this->exporter->getSpecialPropertyResource(
+ $nonInverseProperty->getKey(),
+ SMW_NS_PROPERTY
+ );
+ } else {
+ $propertyExpElement = $this->exporter->getResourceElementForProperty( $nonInverseProperty );
+ }
+
+ if ( $propertyExpElement instanceof ExpNsResource ) {
+ $namespaces[$propertyExpElement->getNamespaceId()] = $propertyExpElement->getNamespace();
+ }
+
+ return TurtleSerializer::getTurtleNameForExpElement( $propertyExpElement );
+ }
+
+ private function doExchangeForWhenInversePropertyIsUsed( DIProperty $property, $objectName, $joinVariable ) {
+
+ $subjectName = '?' . $joinVariable;
+ $nonInverseProperty = $property;
+
+ // Exchange arguments when property is inverse
+ // don't check if this really makes sense
+ if ( $property->isInverse() ) {
+ $subjectName = $objectName;
+ $objectName = '?' . $joinVariable;
+ $nonInverseProperty = new DIProperty( $property->getKey(), false );
+ }
+
+ return [ $subjectName, $objectName, $nonInverseProperty ];
+ }
+
+ private function concatenateToConditionString( $subjectName, $propertyName, $objectName, $innerCondition ) {
+
+ $condition = "$subjectName $propertyName $objectName .\n";
+
+ $innerConditionString = $innerCondition->getCondition() . $innerCondition->getWeakConditionString();
+
+ if ( $innerConditionString === '' ) {
+ return $condition;
+ }
+
+ if ( $innerCondition instanceof FilterCondition ) {
+ return $condition . $innerConditionString;
+ }
+
+ return $condition . "{ $innerConditionString}\n";
+ }
+
+ /**
+ * @note rdfs:subPropertyOf* where * means a property path of arbitrary length
+ * can be found using the "zero or more" will resolve the complete path
+ *
+ * @see http://www.w3.org/TR/sparql11-query/#propertypath-arbitrary-length
+ */
+ private function tryToAddPropertyPathForSaturatedHierarchy( &$condition, DIProperty $property, &$propertyName, $depth ) {
+
+ if ( !$this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_SUBP ) || !$property->isUserDefined() || ( $depth !== null && $depth < 1 ) ) {
+ return null;
+ }
+
+ if ( $this->conditionBuilder->getHierarchyLookup() == null || !$this->conditionBuilder->getHierarchyLookup()->hasSubproperty( $property ) ) {
+ return null;
+ }
+
+ $subPropExpElement = $this->exporter->getSpecialPropertyResource( '_SUBP', SMW_NS_PROPERTY );
+
+ // A discret depth other than 0 or 1 is difficult to achieve
+ // @see https://stackoverflow.com/questions/18126949/limit-the-sparql-query-result-to-first-level-in-hierarchy
+ // Path operator is defined as:
+ // - elt* ZeroOrMorePath
+ // - elt? ZeroOrOnePath
+ $pathOp = $depth > 1 || $depth === null ? '*' : '?';
+
+ $propertyByVariable = '?' . $this->conditionBuilder->getNextVariable( 'sp' );
+ $condition->weakConditions[$propertyName] = "\n". "$propertyByVariable " . $subPropExpElement->getQName() . "$pathOp $propertyName .\n"."";
+ $propertyName = $propertyByVariable;
+ }
+
+}