summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php507
1 files changed, 507 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php
new file mode 100644
index 00000000..8de435ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php
@@ -0,0 +1,507 @@
+<?php
+
+namespace SMW;
+
+use Onoi\Cache\Cache;
+use RuntimeException;
+use SMW\Query\DescriptionFactory;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWQuery as Query;
+
+/**
+ * This class should be accessed via ApplicationFactory::getPropertySpecificationLookup
+ * to ensure a singleton instance.
+ *
+ * Changes to a property should trigger a PropertySpecificationLookup::resetCacheBy to
+ * evict all cached item store to that property.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertySpecificationLookup {
+
+ /**
+ * Reference used in InMemoryPoolCache
+ */
+ const POOLCACHE_ID = 'property.specification.lookup';
+
+ /**
+ * @var CachedPropertyValuesPrefetcher
+ */
+ private $cachedPropertyValuesPrefetcher;
+
+ /**
+ * @var string
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @var Cache
+ */
+ private $intermediaryMemoryCache;
+
+ /**
+ * @since 2.4
+ *
+ * @param CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher
+ * @param Cache $intermediaryMemoryCache
+ */
+ public function __construct( CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher, Cache $intermediaryMemoryCache ) {
+ $this->cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher;
+ $this->intermediaryMemoryCache = $intermediaryMemoryCache;
+ $this->languageCode = Localizer::getInstance()->getContentLanguage()->getCode();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ */
+ public function resetCacheBy( DIWikiPage $subject ) {
+ $this->cachedPropertyValuesPrefetcher->resetCacheBy( $subject );
+ $this->intermediaryMemoryCache->delete( $subject->getHash() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty|DIWikiPage $source
+ * @param DIProperty $target
+ *
+ * @return []|DataItem[]
+ */
+ public function getSpecification( $source, DIProperty $target ) {
+
+ if ( $source instanceof DIProperty ) {
+ $dataItem = $source->getCanonicalDiWikiPage();
+ } elseif( $source instanceof DIWikiPage ) {
+ $dataItem = $source;
+ } else {
+ throw new RuntimeException( "Invalid request instance type" );
+ }
+
+ $hash = $dataItem->getHash();
+ $key = $target->getKey();
+
+ $definition = $this->intermediaryMemoryCache->fetch( $hash );
+
+ if ( $definition === false ) {
+ $definition = [];
+ }
+
+ if ( isset( $definition[$key] ) ) {
+ return $definition[$key];
+ }
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $dataItem,
+ $target
+ );
+
+ if ( !is_array( $dataItems ) ) {
+ $dataItems = [];
+ }
+
+ $definition[$key] = $dataItems;
+ $this->intermediaryMemoryCache->save( $hash, $definition );
+
+ return $dataItems;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return false|DataItem
+ */
+ public function getFieldListBy( DIProperty $property ) {
+
+ $fieldList = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_LIST' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $fieldList = end( $dataItems );
+ }
+
+ return $fieldList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getPreferredPropertyLabelBy( DIProperty $property, $languageCode = '' ) {
+
+ $languageCode = $languageCode === '' ? $this->languageCode : $languageCode;
+ $key = 'ppl:' . $languageCode . ':'. $property->getKey();
+
+ // Guard against high frequency lookup
+ if ( ( $preferredPropertyLabel = $this->intermediaryMemoryCache->fetch( $key ) ) !== false ) {
+ return $preferredPropertyLabel;
+ }
+
+ $preferredPropertyLabel = $this->findPreferredPropertyLabel(
+ $property,
+ $languageCode
+ );
+
+ $this->intermediaryMemoryCache->save( $key, $preferredPropertyLabel );
+
+ return $preferredPropertyLabel;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $displayTitle
+ *
+ * @return DIProperty|false
+ */
+ public function getPropertyFromDisplayTitle( $displayTitle ) {
+
+ $descriptionFactory = new DescriptionFactory();
+
+ $description = $descriptionFactory->newSomeProperty(
+ new DIProperty( '_DTITLE' ),
+ $descriptionFactory->newValueDescription( new DIBlob( $displayTitle ) )
+ );
+
+ $query = new Query( $description );
+ $query->setLimit( 1 );
+ $query->setOption( Query::PROC_CONTEXT, 'PropertySpecificationLookup' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->queryPropertyValuesFor(
+ $query
+ );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+
+ // Cache results as a linked list attached to
+ // the property so that it can be purged all together
+
+ return new DIProperty( $dataItem->getDBKey() );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasUniquenessConstraint( DIProperty $property ) {
+
+ $hasUniquenessConstraint = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVUC' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $hasUniquenessConstraint = end( $dataItems )->getBoolean();
+ }
+
+ return $hasUniquenessConstraint;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ *
+ * @return DataItem|null
+ */
+ public function getPropertyGroup( DIProperty $property ) {
+
+ $dataItem = null;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_INST' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+
+ foreach ( $dataItems as $dataItem ) {
+ $pv = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $dataItem,
+ new DIProperty( '_PPGR' )
+ );
+
+ $di = end( $pv );
+
+ if ( $di instanceof DIBoolean && $di->getBoolean() ) {
+ return $dataItem;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return DataItem|null
+ */
+ public function getExternalFormatterUri( DIProperty $property ) {
+
+ $dataItem = null;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PEFU' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+ }
+
+ return $dataItem;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public function getAllowedPatternBy( DIProperty $property ) {
+
+ $allowsPattern = '';
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAP' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsPattern = end( $dataItems )->getString();
+ }
+
+ return $allowsPattern;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getAllowedValues( DIProperty $property ) {
+
+ $allowsValues = [];
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAL' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsValues = $dataItems;
+ }
+
+ return $allowsValues;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getAllowedListValues( DIProperty $property ) {
+
+ $allowsListValue = [];
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVALI' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsListValue = $dataItems;
+ }
+
+ return $allowsListValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return integer|false
+ */
+ public function getDisplayPrecision( DIProperty $property ) {
+
+ $displayPrecision = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PREC' ) );
+
+ if ( $dataItems !== false && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+ $displayPrecision = abs( (int)$dataItem->getNumber() );
+ }
+
+ return $displayPrecision;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getDisplayUnits( DIProperty $property ) {
+
+ $units = [];
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ new DIProperty( '_UNIT' )
+ );
+
+ if ( $dataItems !== false && $dataItems !== [] ) {
+ foreach ( $dataItems as $dataItem ) {
+ $units = array_merge( $units, preg_split( '/\s*,\s*/u', $dataItem->getString() ) );
+ }
+ }
+
+ return $units;
+ }
+
+ /**
+ * We try to cache anything to avoid unnecessary store connections or DB
+ * lookups. For cases where a property was changed, the EventDipatcher will
+ * receive a 'property.specification.change' event (emitted as soon as the content of
+ * a property page was altered) with PropertySpecificationLookup::resetCacheBy
+ * being invoked to remove the cache entry for that specific property.
+ *
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ * @param string $languageCode
+ * @param mixed|null $linker
+ *
+ * @return string
+ */
+ public function getPropertyDescriptionByLanguageCode( DIProperty $property, $languageCode = '', $linker = null ) {
+
+ // Take the linker into account (Special vs. in page rendering etc.)
+ $languageCode = $languageCode === '' ? $this->languageCode : $languageCode;
+ $key = '--pdesc:' . $languageCode . ':' . ( $linker === null ? '0' : '1' );
+
+ $blobStore = $this->cachedPropertyValuesPrefetcher->getBlobStore();
+
+ $container = $blobStore->read(
+ $this->cachedPropertyValuesPrefetcher->getRootHashFrom( $property->getCanonicalDiWikiPage() )
+ );
+
+ if ( $container->has( $key ) ) {
+ return $container->get( $key );
+ }
+
+ $localPropertyDescription = $this->findLocalPropertyDescription(
+ $property,
+ $linker,
+ $languageCode
+ );
+
+ // If a local property description wasn't available for a predefined property
+ // the try to find a system translation
+ if ( trim( $localPropertyDescription ) === '' && !$property->isUserDefined() ) {
+ $localPropertyDescription = $this->getPredefinedPropertyDescription( $property, $linker, $languageCode );
+ }
+
+ $container->set( $key, $localPropertyDescription );
+
+ $blobStore->save(
+ $container
+ );
+
+ return $localPropertyDescription;
+ }
+
+ private function getPredefinedPropertyDescription( $property, $linker, $languageCode ) {
+
+ $description = '';
+ $key = $property->getKey();
+
+ if ( ( $msgKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) === '' ) {
+ $msgKey = 'smw-property-predefined' . str_replace( '_', '-', strtolower( $key ) );
+ }
+
+ if ( !Message::exists( $msgKey ) ) {
+ return $description;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $property
+ );
+
+ $label = $dataValue->getFormattedLabel();
+
+ $message = Message::get(
+ [ $msgKey, $label ],
+ $linker === null ? Message::ESCAPED : Message::PARSE,
+ $languageCode
+ );
+
+ return $message;
+ }
+
+ private function findLocalPropertyDescription( $property, $linker, $languageCode ) {
+
+ $text = '';
+ $descriptionProperty = new DIProperty( '_PDESC' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ $descriptionProperty
+ );
+
+ if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $descriptionProperty, $languageCode ) ) !== null ) {
+ $text = $dataValue->getShortWikiText( $linker );
+ }
+
+ return $text;
+ }
+
+ private function findPreferredPropertyLabel( $property, $languageCode ) {
+
+ $text = '';
+ $preferredProperty = new DIProperty( '_PPLB' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ $preferredProperty
+ );
+
+ if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $preferredProperty, $languageCode ) ) !== null ) {
+ $text = $dataValue->getShortWikiText();
+ }
+
+ return $text;
+ }
+
+ private function findTextValueByLanguage( $dataItems, $property, $languageCode ) {
+
+ if ( $dataItems === null || $dataItems === [] ) {
+ return null;
+ }
+
+ foreach ( $dataItems as $dataItem ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ // Here a MonolingualTextValue was retunred therefore the method
+ // can be called without validation
+ $dv = $dataValue->getTextValueByLanguage( $languageCode );
+
+ if ( $dv !== null ) {
+ return $dv;
+ }
+ }
+
+ return null;
+ }
+
+}