diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php new file mode 100644 index 00000000..fa3c6096 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php @@ -0,0 +1,847 @@ +<?php + +namespace SMW\Query\Parser; + +use SMW\DataTypeRegistry; +use SMW\DataValueFactory; +use SMW\DIProperty; +use SMW\DIWikiPage; +use SMW\Localizer; +use SMW\Query\DescriptionFactory; +use SMW\Query\Language\ClassDescription; +use SMW\Query\Language\Disjunction; +use SMW\Query\Language\SomeProperty; +use SMW\Query\Parser; +use SMW\Query\QueryToken; +use Title; + +/** + * @license GNU GPL v2+ + * @since 3.0 + * + * @author Markus Krötzsch + */ +class LegacyParser implements Parser { + + /** + * @var DescriptionProcessor + */ + private $descriptionProcessor; + + /** + * @var QueryToken + */ + private $queryToken; + + /** + * @var Tokenizer + */ + private $tokenizer; + + /** + * @var DescriptionFactory + */ + private $descriptionFactory; + + /** + * @var DataTypeRegistry + */ + private $dataTypeRegistry; + + /** + * Description of the default namespace restriction, or NULL if not used + * + * @var array|null + */ + private $defaultNamespace; + + /** + * List of open blocks ("parentheses") that need closing at current step + * + * @var array + */ + private $separatorStack = []; + + /** + * Remaining string to be parsed (parsing eats query string from the front) + * + * @var string + */ + private $currentString; + + /** + * Cache label of category namespace . ':' + * + * @var string + */ + private $categoryPrefix; + + /** + * Cache label of concept namespace . ':' + * + * @var string + */ + private $conceptPrefix; + + /** + * Cache canonnical label of category namespace . ':' + * + * @var string + */ + private $categoryPrefixCannonical; + + /** + * Cache canonnical label of concept namespace . ':' + * + * @var string + */ + private $conceptPrefixCannonical; + + /** + * @var DIWikiPage|null + */ + private $contextPage; + + /** + * @var boolean + */ + private $selfReference = false; + + /** + * @since 3.0 + * + * @param DescriptionProcessor $descriptionProcessor + * @param Tokenizer $tokenizer + * @param QueryToken $queryToken + */ + public function __construct( DescriptionProcessor $descriptionProcessor, Tokenizer $tokenizer, QueryToken $queryToken ) { + $this->descriptionProcessor = $descriptionProcessor; + $this->tokenizer = $tokenizer; + $this->queryToken = $queryToken; + $this->descriptionFactory = new DescriptionFactory(); + $this->dataTypeRegistry = DataTypeRegistry::getInstance(); + $this->setDefaultPrefix(); + } + + /** + * @since 3.0 + * + * @param DIWikiPage|null $contextPage + */ + public function setContextPage( DIWikiPage $contextPage = null ) { + $this->contextPage = $contextPage; + } + + /** + * Provide an array of namespace constants that are used as default restrictions. + * If NULL is given, no such default restrictions will be added (faster). + * + * @since 1.6 + */ + public function setDefaultNamespaces( $namespaces ) { + $this->defaultNamespace = null; + + if ( !is_array( $namespaces ) ) { + return; + } + + foreach ( $namespaces as $namespace ) { + $this->defaultNamespace = $this->descriptionProcessor->asOr( + $this->defaultNamespace, + $this->descriptionFactory->newNamespaceDescription( $namespace ) + ); + } + } + + /** + * @since 3.0 + * + * @param string|null $languageCode + */ + public function setDefaultPrefix( $languageCode = null ) { + + $localizer = Localizer::getInstance(); + + if ( $languageCode === null ) { + $language = $localizer->getContentLanguage(); + } else { + $language = $localizer->getLanguage( $languageCode ); + } + + $this->categoryPrefix = $language->getNsText( NS_CATEGORY ) . ':'; + $this->conceptPrefix = $language->getNsText( SMW_NS_CONCEPT ) . ':'; + + $this->categoryPrefixCannonical = 'Category:'; + $this->conceptPrefixCannonical = 'Concept:'; + + $this->tokenizer->setDefaultPattern( + [ + $this->categoryPrefix, + $this->conceptPrefix, + $this->categoryPrefixCannonical, + $this->conceptPrefixCannonical + ] + ); + } + + /** + * Return array of error messages (possibly empty). + * + * @return array + */ + public function getErrors() { + return $this->descriptionProcessor->getErrors(); + } + + /** + * @since 3.0 + * + * @return boolean + */ + public function containsSelfReference() { + + if ( $this->selfReference ) { + return true; + } + + return $this->descriptionProcessor->containsSelfReference(); + } + + /** + * Return error message or empty string if no error occurred. + * + * @return string + */ + public function getErrorString() { + throw new \RuntimeException( "Shouldnot be used, remove getErrorString usage!" ); + return smwfEncodeMessages( $this->getErrors() ); + } + + /** + * @since 2.5 + * + * @return QueryToken + */ + public function getQueryToken() { + return $this->queryToken; + } + + /** + * @since 3.0 + * + * {@inheritDoc} + */ + public function createCondition( $property, $value ) { + + if ( $property instanceOf DIProperty ) { + $property = $property->getLabel(); + } + + return "[[$property::$value]]"; + } + + /** + * Compute an SMWDescription from a query string. Returns whatever descriptions could be + * wrestled from the given string (the most general result being SMWThingDescription if + * no meaningful condition was extracted). + * + * @param string $queryString + * + * @return Description + */ + public function getQueryDescription( $queryString ) { + + if ( $queryString === '' ) { + $this->descriptionProcessor->addErrorWithMsgKey( + 'smw-query-condition-empty' + ); + + return $this->descriptionFactory->newThingDescription(); + } + + $this->descriptionProcessor->clear(); + $this->descriptionProcessor->setContextPage( $this->contextPage ); + + $this->currentString = $queryString; + $this->separatorStack = []; + + $this->selfReference = false; + $setNS = false; + + $description = $this->getSubqueryDescription( $setNS ); + + // add default namespaces if applicable + if ( !$setNS ) { + $description = $this->descriptionProcessor->asAnd( + $this->defaultNamespace, + $description + ); + } + + // parsing went wrong, no default namespaces + if ( $description === null ) { + $description = $this->descriptionFactory->newThingDescription(); + } + + return $description; + } + + /** + * Compute an SMWDescription for current part of a query, which should + * be a standalone query (the main query or a subquery enclosed within + * "\<q\>...\</q\>". Recursively calls similar methods and returns NULL upon error. + * + * The call-by-ref parameter $setNS is a boolean. Its input specifies whether + * the query should set the current default namespace if no namespace restrictions + * were given. If false, the calling super-query is happy to set the required + * NS-restrictions by itself if needed. Otherwise the subquery has to impose the defaults. + * This is so, since outermost queries and subqueries of disjunctions will have to set + * their own default restrictions. + * + * The return value of $setNS specifies whether or not the subquery has a namespace + * specification in place. This might happen automatically if the query string imposes + * such restrictions. The return value is important for those callers that otherwise + * set up their own restrictions. + * + * Note that $setNS is no means to switch on or off default namespaces in general, + * but just controls query generation. For general effect, the default namespaces + * should be set to NULL. + * + * @return Description|null + */ + private function getSubqueryDescription( &$setNS ) { + + $conjunction = null; // used for the current inner conjunction + $disjuncts = []; // (disjunctive) array of subquery conjunctions + + $hasNamespaces = false; // does the current $conjnuction have its own namespace restrictions? + $mustSetNS = $setNS; // must NS restrictions be set? (may become true even if $setNS is false) + + $continue = ( $chunk = $this->readChunk() ) !== ''; // skip empty subquery completely, thorwing an error + + while ( $continue ) { + $setsubNS = false; + + switch ( $chunk ) { + case '[[': // start new link block + $ld = $this->getLinkDescription( $setsubNS ); + + if ( !is_null( $ld ) ) { + $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $ld ); + } + break; + case 'AND': + case '<q>': // enter new subquery, currently irrelevant but possible + $this->pushDelimiter( '</q>' ); + $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $this->getSubqueryDescription( $setsubNS ) ); + break; + case 'OR': + case '||': + case '': + case '</q>': // finish disjunction and maybe subquery + if ( !is_null( $this->defaultNamespace ) ) { // possibly add namespace restrictions + if ( $hasNamespaces && !$mustSetNS ) { + // add NS restrictions to all earlier conjunctions (all of which did not have them yet) + $mustSetNS = true; // enforce NS restrictions from now on + $newdisjuncts = []; + + foreach ( $disjuncts as $conj ) { + $newdisjuncts[] = $this->descriptionProcessor->asAnd( $conj, $this->defaultNamespace ); + } + + $disjuncts = $newdisjuncts; + } elseif ( !$hasNamespaces && $mustSetNS ) { + // add ns restriction to current result + $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $this->defaultNamespace ); + } + } + + $disjuncts[] = $conjunction; + // start anew + $conjunction = null; + $hasNamespaces = false; + + // finish subquery? + if ( $chunk == '</q>' ) { + if ( $this->popDelimiter( '</q>' ) ) { + $continue = false; // leave the loop + } else { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_toomanyclosing', $chunk ); + return null; + } + } elseif ( $chunk === '' ) { + $continue = false; + } + break; + case '+': // "... AND true" (ignore) + break; + default: // error: unexpected $chunk + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $chunk ); + // return null; // Try to go on, it can only get better ... + } + + if ( $setsubNS ) { // namespace restrictions encountered in current conjunct + $hasNamespaces = true; + } + + if ( $continue ) { // read on only if $continue remained true + $chunk = $this->readChunk(); + } + } + + if ( count( $disjuncts ) > 0 ) { // make disjunctive result + $result = null; + + foreach ( $disjuncts as $d ) { + if ( is_null( $d ) ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_emptysubquery' ); + $setNS = false; + return null; + } else { + $result = $this->descriptionProcessor->asOr( $result, $d ); + } + } + } else { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_emptysubquery' ); + $setNS = false; + return null; + } + + // NOTE: also false if namespaces were given but no default NS descs are available + $setNS = $mustSetNS; + + return $result; + } + + /** + * Compute an SMWDescription for current part of a query, which should + * be the content of "[[ ... ]]". Returns NULL upon error. + * + * Parameters $setNS has the same use as in getSubqueryDescription(). + */ + private function getLinkDescription( &$setNS ) { + // This method is called when we encountered an opening '[['. The following + // block could be a Category-statement, fixed object, or property statement. + + // NOTE: untrimmed, initial " " escapes prop. chains + $chunk = $this->readChunk( '', true, false ); + + if ( $this->hasClassPrefix( $chunk ) ) { + return $this->getClassDescription( $setNS, $this->isClass( $chunk ) ); + } + + // fixed subject, namespace restriction, property query, or subquery + + // Do not consume hit, "look ahead" + $sep = $this->readChunk( '', false ); + + if ( ( $sep == '::' ) || ( $sep == ':=' ) ) { + if ( $chunk{0} != ':' ) { // property statement + return $this->getPropertyDescription( $chunk, $setNS ); + } else { // escaped article description, read part after :: to get full contents + $chunk .= $this->readChunk( '\[\[|\]\]|\|\||\|' ); + return $this->getArticleDescription( trim( $chunk ), $setNS ); + } + } + + // Fixed article/namespace restriction. $sep should be ]] or || + return $this->getArticleDescription( trim( $chunk ), $setNS ); + } + + /** + * Parse a category description (the part of an inline query that + * is in between "[[Category:" and the closing "]]" and create a + * suitable description. + */ + private function getClassDescription( &$setNS, $category = true ) { + + // No subqueries allowed here, inline disjunction allowed, wildcards allowed + $description = null; + $continue = true; + $invalidName = false; + + while ( $continue ) { + $chunk = $this->readChunk(); + + if ( $chunk == '+' ) { + $desc = $this->descriptionFactory->newNamespaceDescription( $category ? NS_CATEGORY : SMW_NS_CONCEPT ); + $description = $this->descriptionProcessor->asOr( $description, $desc ); + } else { // assume category/concept title + $isNegation = false; + + // [[Category:!Foo]] + // Only the ElasticStore does actively support this construct + if ( $chunk{0} === '!' ) { + $chunk = substr( $chunk, 1 ); + $isNegation = true; + } + + // We add a prefix to prevent problems with, e.g., [[Category:Template:Test]] + $prefix = $category ? $this->categoryPrefix : $this->conceptPrefix; + $title = Title::newFromText( $prefix . $chunk ); + + // Something like [[Category::Foo]] doesn't produce any meaningful + // results + if ( strpos( $prefix . $chunk, '::' ) !== false ) { + $invalidName .= "{$prefix}{$chunk}"; + } elseif ( $invalidName ) { + $invalidName .= "||{$chunk}"; + } + + if ( $title !== null ) { + $diWikiPage = new DIWikiPage( $title->getDBkey(), $title->getNamespace(), '' ); + + if ( !$this->selfReference && $this->contextPage !== null ) { + $this->selfReference = $diWikiPage->equals( $this->contextPage ); + } + + $desc = $category ? $this->descriptionFactory->newClassDescription( $diWikiPage ) : $this->descriptionFactory->newConceptDescription( $diWikiPage ); + + if ( $isNegation ) { + $desc->isNegation = $isNegation; + } + + $description = $this->descriptionProcessor->asOr( $description, $desc ); + } + } + + $chunk = $this->readChunk(); + + // Disjunctions only for categories + $continue = ( $chunk == '||' ) && $category; + } + + if ( $invalidName ) { + return $this->descriptionProcessor->addErrorWithMsgKey( 'smw-category-invalid-value-assignment', "[[{$invalidName}]]" ); + } + + return $this->finishLinkDescription( $chunk, false, $description, $setNS ); + } + + /** + * Parse a property description (the part of an inline query that + * is in between "[[Some property::" and the closing "]]" and create a + * suitable description. The "::" is the first chunk on the current + * string. + */ + private function getPropertyDescription( $propertyName, &$setNS ) { + + // Consume separator ":=" or "::" + $this->readChunk(); + $dataValueFactory = DataValueFactory::getInstance(); + + // First process property chain syntax (e.g. "property1.property2::value"), + // escaped by initial " ": + $propertynames = ( $propertyName{0} == ' ' ) ? [ $propertyName ] : explode( '.', $propertyName ); + $propertyValueList = []; + + $typeid = '_wpg'; + $inverse = false; + + // After iteration, $property and $typeid correspond to last value + foreach ( $propertynames as $name ) { + + // Non-final property in chain was no wikipage: not allowed + if ( !$this->isPagePropertyType( $typeid ) ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_valuesubquery', $name ); + + // TODO: read some more chunks and try to finish [[ ]] + return null; + } + + $propertyValue = $dataValueFactory->newPropertyValueByLabel( $name ); + + // Illegal property identifier + if ( !$propertyValue->isValid() ) { + $this->descriptionProcessor->addError( $propertyValue->getErrors() ); + + // TODO: read some more chunks and try to finish [[ ]] + return null; + } + + // Set context to allow evading restriction checks for specific + // entities that handle the context such as pre-defined properties + // (Has query, Modification date etc.) + $propertyValue->setOption( $propertyValue::OPT_QUERY_CONTEXT, true ); + + // Check restriction + if ( $propertyValue->isRestricted() ) { + $this->descriptionProcessor->addError( $propertyValue->getRestrictionError() ); + return null; + } + + $property = $propertyValue->getDataItem(); + $propertyValueList[] = $propertyValue; + + $typeid = $property->findPropertyTypeID(); + $inverse = $property->isInverse(); + } + + $innerdesc = null; + $continue = true; + + while ( $continue ) { + $chunk = $this->readChunk(); + + switch ( $chunk ) { + // !+ + case '!+': + $desc = $this->descriptionFactory->newThingDescription(); + $desc->isNegation = true; + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $desc ); + $chunk = $this->readChunk(); + break; + // wildcard, add namespaces for page-type properties + case '+': + if ( !is_null( $this->defaultNamespace ) && ( $this->isPagePropertyType( $typeid ) || $inverse ) ) { + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->defaultNamespace ); + } else { + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() ); + } + $chunk = $this->readChunk(); + break; + // subquery, set default namespaces + case '<q>': + if ( $this->isPagePropertyType( $typeid ) || $inverse ) { + $this->pushDelimiter( '</q>' ); + $setsubNS = true; + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->getSubqueryDescription( $setsubNS ) ); + } else { // no subqueries allowed for non-pages + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_valuesubquery', end( $propertynames ) ); + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() ); + } + $chunk = $this->readChunk(); + break; + // normal object value + default: + // read value(s), possibly with inner [[...]] + $open = 1; + $value = $chunk; + $continue2 = true; + // read value with inner [[, ]], || + while ( ( $open > 0 ) && ( $continue2 ) ) { + $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' ); + switch ( $chunk ) { + case '[[': // open new [[ ]] + $open++; + break; + case ']]': // close [[ ]] + $open--; + break; + case '|': + case '||': // terminates only outermost [[ ]] + if ( $open == 1 ) { + $open = 0; + } + break; + case '': ///TODO: report error; this is not good right now + $continue2 = false; + break; + } + if ( $open != 0 ) { + $value .= $chunk; + } + } ///NOTE: at this point, we normally already read one more chunk behind the value + $outerDesription = $this->descriptionProcessor->newDescriptionForPropertyObjectValue( + $propertyValue->getDataItem(), + $value + ); + + $this->queryToken->addFromDesciption( $outerDesription ); + $innerdesc = $this->descriptionProcessor->asOr( + $innerdesc, + $outerDesription + ); + + } + $continue = ( $chunk == '||' ); + } + + // No description, make a wildcard search + if ( $innerdesc === null ) { + if ( $this->defaultNamespace !== null && $this->isPagePropertyType( $typeid ) ) { + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->defaultNamespace ); + } else { + $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() ); + } + + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_propvalueproblem', $propertyValue->getWikiValue() ); + } + + $propertyValueList = array_reverse( $propertyValueList ); + + foreach ( $propertyValueList as $propertyValue ) { + $innerdesc = $this->descriptionFactory->newSomeProperty( $propertyValue->getDataItem(), $innerdesc ); + } + + $result = $innerdesc; + + return $this->finishLinkDescription( $chunk, false, $result, $setNS ); + } + + /** + * Parse an article description (the part of an inline query that + * is in between "[[" and the closing "]]" assuming it is not specifying + * a category or property) and create a suitable description. + * The first chunk behind the "[[" has already been read and is + * passed as a parameter. + */ + private function getArticleDescription( $firstChunk, &$setNS ) { + + $chunk = $firstChunk; + $description = null; + + $continue = true; + $localizer = Localizer::getInstance(); + + while ( $continue ) { + + // No subqueries of the form [[<q>...</q>]] (not needed) + if ( $chunk == '<q>' ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_misplacedsubquery' ); + return null; + } + + // ":Category:Foo" "User:bar" ":baz" ":+" + $list = preg_split( '/:/', $chunk, 3 ); + + if ( ( $list[0] === '' ) && ( count( $list ) == 3 ) ) { + $list = array_slice( $list, 1 ); + } + + // try namespace restriction + if ( ( count( $list ) == 2 ) && ( $list[1] == '+' ) ) { + + $idx = $localizer->getNamespaceIndexByName( $list[0] ); + + if ( $idx !== false ) { + $description = $this->descriptionProcessor->asOr( + $description, + $this->descriptionFactory->newNamespaceDescription( $idx ) + ); + } + } else { + $outerDesription = $this->descriptionProcessor->newDescriptionForWikiPageValueChunk( + $chunk + ); + + $this->queryToken->addFromDesciption( $outerDesription ); + + $description = $this->descriptionProcessor->asOr( + $description, + $outerDesription + ); + } + + $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' ); + + if ( $chunk == '||' ) { + $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' ); + $continue = true; + } else { + $continue = false; + } + } + + return $this->finishLinkDescription( $chunk, true, $description, $setNS ); + } + + private function finishLinkDescription( $chunk, $hasNamespaces, $description, &$setNS ) { + + if ( is_null( $description ) ) { // no useful information or concrete error found + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $chunk ); // was smw_badqueryatom + } elseif ( !$hasNamespaces && $setNS && !is_null( $this->defaultNamespace ) ) { + $description = $this->descriptionProcessor->asAnd( $description, $this->defaultNamespace ); + $hasNamespaces = true; + } + + $setNS = $hasNamespaces; + + if ( $chunk == '|' ) { // skip content after single |, but report a warning + // Note: Using "|label" in query atoms used to be a way to set the mainlabel in SMW <1.0; no longer supported now + $chunk = $this->readChunk( '\]\]' ); + $labelpart = '|'; + $hasError = true; + + // Set an individual hierarchy depth + if ( strpos( $chunk, '+depth=' ) !== false ) { + list( $k, $depth ) = explode( '=', $chunk, 2 ); + + if ( $description instanceOf ClassDescription || $description instanceOf SomeProperty || $description instanceOf Disjunction ) { + $description->setHierarchyDepth( $depth ); + } + + $chunk = $this->readChunk( '\]\]' ); + $hasError = false; + } + + if ( $chunk != ']]' ) { + $labelpart .= $chunk; + $chunk = $this->readChunk( '\]\]' ); + } + + if ( $hasError ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $labelpart ); + } + } + + if ( $chunk != ']]' ) { + // What happended? We found some chunk that could not be processed as + // link content (as in [[Category:Test<q>]]), or the closing ]] are + // just missing entirely. + if ( $chunk !== '' ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_misplacedsymbol', $chunk ); + + // try to find a later closing ]] to finish this misshaped subpart + $chunk = $this->readChunk( '\]\]' ); + + if ( $chunk != ']]' ) { + $chunk = $this->readChunk( '\]\]' ); + } + } + if ( $chunk === '' ) { + $this->descriptionProcessor->addErrorWithMsgKey( 'smw_noclosingbrackets' ); + } + } + + return $description; + } + + /** + * @see Tokenizer::read + */ + private function readChunk( $stoppattern = '', $consume = true, $trim = true ) { + return $this->tokenizer->getToken( $this->currentString, $stoppattern, $consume, $trim ); + } + + /** + * Enter a new subblock in the query, which must at some time be terminated by the + * given $endstring delimiter calling popDelimiter(); + */ + private function pushDelimiter( $endstring ) { + array_push( $this->separatorStack, $endstring ); + } + + /** + * Exit a subblock in the query ending with the given delimiter. + * If the delimiter does not match the top-most open block, false + * will be returned. Otherwise return true. + */ + private function popDelimiter( $endstring ) { + $topdelim = array_pop( $this->separatorStack ); + return ( $topdelim == $endstring ); + } + + private function isPagePropertyType( $typeid ) { + return $typeid == '_wpg' || $this->dataTypeRegistry->isSubDataType( $typeid ); + } + + private function hasClassPrefix( $chunk ) { + return in_array( smwfNormalTitleText( $chunk ), [ $this->categoryPrefix, $this->conceptPrefix, $this->categoryPrefixCannonical, $this->conceptPrefixCannonical ] ); + } + + private function isClass( $chunk ) { + return smwfNormalTitleText( $chunk ) == $this->categoryPrefix || smwfNormalTitleText( $chunk ) == $this->categoryPrefixCannonical; + } + +} |