diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php new file mode 100644 index 00000000..46e87508 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php @@ -0,0 +1,357 @@ +<?php + +namespace SMW\Query\Result; + +use SMW\DataValueFactory; +use SMW\DataValues\MonolingualTextValue; +use SMW\DIProperty; +use SMW\DIWikiPage; +use SMW\Parser\InTextAnnotationParser; +use SMW\Query\PrintRequest; +use SMW\Query\QueryToken; +use SMW\RequestOptions; +use SMW\Store; +use SMWDataItem as DataItem; +use SMWDIBlob as DIBlob; +use SMWDIBoolean as DIBoolean; + +/** + * Returns the result content (DI objects) for a single PrintRequest, representing + * as cell of the intersection between a subject row and a print column. + * + * @license GNU GPL v2+ + * @since 2.5 + * + * @author Markus Krötzsch + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + * @author mwjames + */ +class ResultFieldMatchFinder { + + /** + * @var Store + */ + private $store; + + /** + * @var PrintRequest + */ + private $printRequest; + + /** + * @var QueryToken + */ + private $queryToken; + + /** + * @var boolean|array + */ + private static $catCacheObj = false; + + /** + * @var boolean|array + */ + private static $catCache = false; + + /** + * @since 2.5 + * + * @param Store $store + * @param PrintRequest $printRequest + */ + public function __construct( Store $store, PrintRequest $printRequest ) { + $this->printRequest = $printRequest; + $this->store = $store; + } + + /** + * @since 2.5 + * + * @param QueryToken|null $queryToken + */ + public function setQueryToken( QueryToken $queryToken = null ) { + + if ( $queryToken === null ) { + return; + } + + $this->queryToken = $queryToken; + + $this->queryToken->setOutputFormat( + $this->printRequest->getOutputFormat() + ); + } + + /** + * @since 2.5 + * + * @param DataItem $dataItem + * + * @param DataItem[]|[] + */ + public function findAndMatch( DataItem $dataItem ) { + + $content = []; + + // Request the current element (page in result set). + // The limit is ignored here. + if ( $this->printRequest->isMode( PrintRequest::PRINT_THIS ) ) { + return [ $dataItem ]; + } + + // Request all direct categories of the current element + // Always recompute cache here to ensure output format is respected. + if ( $this->printRequest->isMode( PrintRequest::PRINT_CATS ) ) { + self::$catCache = $this->store->getPropertyValues( + $dataItem, + new DIProperty( '_INST' ), + $this->getRequestOptions( false ) + ); + + self::$catCacheObj = $dataItem->getHash(); + + $limit = $this->printRequest->getParameter( 'limit' ); + + return ( $limit === false ) ? ( self::$catCache ) : array_slice( self::$catCache, 0, $limit ); + } + + // Request to whether current element is in given category (Boolean printout). + // The limit is ignored here. + if ( $this->printRequest->isMode( PrintRequest::PRINT_CCAT ) ) { + if ( self::$catCacheObj !== $dataItem->getHash() ) { + self::$catCache = $this->store->getPropertyValues( + $dataItem, + new DIProperty( '_INST' ) + ); + self::$catCacheObj = $dataItem->getHash(); + } + + $found = false; + $prkey = $this->printRequest->getData()->getDBkey(); + + foreach ( self::$catCache as $cat ) { + if ( $cat->getDBkey() == $prkey ) { + $found = true; + break; + } + } + + return [ new DIBoolean( $found ) ]; + } + + // Request all property values of a certain attribute of the current element. + if ( $this->printRequest->isMode( PrintRequest::PRINT_PROP ) || $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) { + return $this->getResultsForProperty( $dataItem ); + } + + return $content; + } + + /** + * Make a request option object based on the given parameters, and + * return NULL if no such object is required. The parameter defines + * if the limit should be taken into account, which is not always desired + * (especially if results are to be cached for future use). + * + * @param boolean $useLimit + * + * @return RequestOptions|null + */ + public function getRequestOptions( $useLimit = true ) { + $limit = $useLimit ? $this->printRequest->getParameter( 'limit' ) : false; + $order = trim( $this->printRequest->getParameter( 'order' ) ); + $options = null; + + // Important: use "!=" for order, since trim() above does never return "false", use "!==" for limit since "0" is meaningful here. + if ( ( $limit !== false ) || ( $order != false ) ) { + $options = new RequestOptions(); + + if ( $limit !== false ) { + $options->limit = trim( $limit ); + } + + // Expecting a natural sort behaviour (n-asc, n-desc)? + if ( strpos( $order, 'n-' ) !== false ) { + $order = str_replace( 'n-', '', $order ); + $options->natural = true; + } + + if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) { + $options->sort = true; + $options->ascending = false; + } elseif ( ( $order == 'ascending' ) || ( $order == 'asc' ) ) { + $options->sort = true; + $options->ascending = true; + } + } + + return $options; + } + + private function getResultsForProperty( $dataItem ) { + + $content = $this->getResultContent( + $dataItem + ); + + if ( !$this->isMultiValueWithParameter( 'index' ) && !$this->isMultiValueWithParameter( 'lang' ) ) { + return $content; + } + + // Print one component of a multi-valued string. + // + // Known limitation: the printrequest still is of type _rec, so if + // printers check for this then they will not recognize that it returns + // some more concrete type. + if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) { + $propertyValue = $this->printRequest->getData()->getLastPropertyChainValue(); + } else { + $propertyValue = $this->printRequest->getData(); + } + + $index = $this->printRequest->getParameter( 'index' ); + $lang = $this->printRequest->getParameter( 'lang' ); + $newcontent = []; + + // Replace content with specific content from a Container/MultiValue + foreach ( $content as $diContainer ) { + + /* AbstractMultiValue */ + $multiValue = DataValueFactory::getInstance()->newDataValueByItem( + $diContainer, + $propertyValue->getDataItem() + ); + + $multiValue->setOption( $multiValue::OPT_QUERY_CONTEXT, true ); + + if ( $multiValue instanceof MonolingualTextValue && $lang !== false && ( $textValue = $multiValue->getTextValueByLanguage( $lang ) ) !== null ) { + + // Return the text representation without a language reference + // (tag) since the value has been filtered hence only matches + // that language + $newcontent[] = $this->applyContentManipulation( $textValue->getDataItem() ); + + // Set the index so ResultArray::getNextDataValue can + // find the correct PropertyDataItem (_TEXT;_LCODE) position + // to match the DI + $this->printRequest->setParameter( 'index', 1 ); + } elseif ( $lang === false && $index !== false && ( $dataItemByRecord = $multiValue->getDataItemByIndex( $index ) ) !== null ) { + $newcontent[] = $this->applyContentManipulation( $dataItemByRecord ); + } + } + + $content = $newcontent; + unset( $newcontent ); + + return $content; + } + + private function isMultiValueWithParameter( $parameter ) { + return strpos( $this->printRequest->getTypeID(), '_rec' ) !== false && $this->printRequest->getParameter( $parameter ) !== false; + } + + private function getResultContent( DataItem $dataItem ) { + + $dataValue = $this->printRequest->getData(); + $dataItems = [ $dataItem ]; + + if ( !$dataValue->isValid() ) { + return []; + } + + // If it is a chain then try to find a connected DIWikiPage subject that + // matches the property on the chained PrintRequest. + // For example, Number.Date.SomeThing will not return any meaningful results + // because Number will return a DINumber object and not a DIWikiPage. + // If on the other hand Has page.Number (with Number being the Last and + // `Has page` is of type Page) then the iteration will lookup on results + // for `Has page` and try to match a Number annotation on the results + // retrieved from `Has page`. + if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) { + + // Output of the previous iteration is the input for the next iteration + foreach ( $dataValue->getPropertyChainValues() as $pv ) { + $dataItems = $this->doFetchPropertyValues( $dataItems, $pv ); + + // If the results return empty then it means that for this element + // the chain has no matchable items hence we stop + if ( $dataItems === [] ) { + return []; + } + } + + $dataValue = $dataValue->getLastPropertyChainValue(); + } + + return $this->doFetchPropertyValues( $dataItems, $dataValue ); + } + + private function doFetchPropertyValues( $dataItems, $dataValue ) { + + $propertyValues = []; + + foreach ( $dataItems as $dataItem ) { + + if ( !$dataItem instanceof DIWikiPage ) { + continue; + } + + $pv = $this->store->getPropertyValues( + $dataItem, + $dataValue->getDataItem(), + $this->getRequestOptions() + ); + + if ( $pv instanceof \Iterator ) { + $pv = iterator_to_array( $pv ); + } + + $propertyValues = array_merge( $propertyValues, $pv ); + unset( $pv ); + } + + array_walk( $propertyValues, function( &$dataItem ) { + $dataItem = $this->applyContentManipulation( $dataItem ); + } ); + + return $propertyValues; + } + + private function applyContentManipulation( $dataItem ) { + + if ( !$dataItem instanceof DIBlob ) { + return $dataItem; + } + + $type = $this->printRequest->getTypeID(); + + // Avoid `_cod`, `_eid` or similar types that use the DIBlob as storage + // object + if ( $type !== '_txt' && strpos( $type, '_rec' ) === false ) { + return $dataItem; + } + + $outputFormat = $this->printRequest->getOutputFormat(); + + // #2325 + // Output format marked with -raw are allowed to retain a possible [[ :: ]] + // annotation + // '-ia' is deprecated use `-raw` + if ( strpos( $outputFormat, '-raw' ) !== false || strpos( $outputFormat, '-ia' ) !== false ) { + return $dataItem; + } + + // #1314 + $string = InTextAnnotationParser::removeAnnotation( + $dataItem->getString() + ); + + // #2253 + if ( $this->queryToken !== null ) { + $string = $this->queryToken->highlight( $string ); + } + + return new DIBlob( $string ); + } + +} |