Mwjames has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/60393


Change subject: Refactor SMWParserExtensions, convert static into an 
instantiable class
......................................................................

Refactor SMWParserExtensions, convert static into an instantiable class

This commit does not contain any processing or logic changes.

+ Rename SMWParserExtensions class to SMW\ParserTextProcessor
(because the class is actually doing text processing)
+ Remove hook caller from class and move into the SMWHooks class
+ Eliminate static SMWParserData caller
+ Convert all static methods
+ Split parse into smaller piece for better modularity
+ Add test

Change-Id: I66b812e0276820fa58dbbfbc632236ea6f8ba6d5
---
M SemanticMediaWiki.hooks.php
M includes/ParserData.php
A includes/ParserTextProcessor.php
D includes/SMW_ParserExtensions.php
M includes/Setup.php
M tests/phpunit/includes/HooksTest.php
A tests/phpunit/includes/ParserTextProcessorTest.php
7 files changed, 860 insertions(+), 230 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticMediaWiki 
refs/changes/93/60393/1

diff --git a/SemanticMediaWiki.hooks.php b/SemanticMediaWiki.hooks.php
index 8dc4fd6..be0f613 100644
--- a/SemanticMediaWiki.hooks.php
+++ b/SemanticMediaWiki.hooks.php
@@ -240,6 +240,7 @@
                        'Hooks',
                        'DataValueFactory',
                        'Settings',
+                       'ParserTextProcessor',
 
                        'api/ApiSMWInfo',
 
@@ -610,4 +611,40 @@
                $parserData->addSpecialProperties( $wikiPage, $revision, $user 
);
                return true;
        }
+
+       /**
+        * Hook: InternalParseBeforeLinks is used to process the expanded wiki
+        * code after <nowiki>, HTML-comments, and templates have been treated.
+        *
+        * This method will be called before an article is displayed or 
previewed.
+        * For display and preview we strip out the semantic properties and 
append them
+        * at the end of the article.
+        *
+        * @note MW 1.20+ see InternalParseBeforeSanitize
+        *
+        * @see Parser
+        * @see 
http://http://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks
+        *
+        * @since  1.10
+        *
+        * @param Parser $parser
+        * @param string $text
+        *
+        * @return true
+        */
+       public static function onInternalParseBeforeLinks( Parser &$parser, 
&$text ) {
+
+               $settings = SMW\Settings::newFromArray( array(
+                       'smwgNamespacesWithSemanticLinks' => 
$GLOBALS['smwgNamespacesWithSemanticLinks'],
+                       'smwgLinksInValues' => $GLOBALS['smwgLinksInValues'],
+                       'smwgInlineErrors'  => $GLOBALS['smwgInlineErrors']
+               ) );
+
+               $processor = new SMW\ParserTextProcessor(
+                       new SMW\ParserData( $parser->getTitle(), 
$parser->getOutput() ),
+                       $settings
+               );
+               $processor->parse( $text );
+               return true;
+       }
 }
diff --git a/includes/ParserData.php b/includes/ParserData.php
index 4f3d3f7..d35d307 100644
--- a/includes/ParserData.php
+++ b/includes/ParserData.php
@@ -166,18 +166,6 @@
        protected $updateJobs = true;
 
        /**
-        * Allows explicitly to switch storage method, MW 1.21 comes with a new
-        * method setExtensionData/getExtensionData in how the ParserOutput
-        * stores arbitrary data
-        *
-        * MW 1.21 unit tests are passed but real page content did vanished
-        * therefore for now disable this feature for MW 1.21 as well
-        *
-        * @var $extensionData
-        */
-       protected $useExtensionData = false;
-
-       /**
         * Constructor
         *
         * @since 1.9
@@ -302,13 +290,15 @@
        }
 
        /**
-        * Init semanticData container either from the ParserOutput object
-        * or if not available use the subject
+        * Initializes the semantic data container either from the ParserOutput 
or
+        * if not available a new container is being created
+        *
+        * @note MW 1.21+ use getExtensionData()
         *
         * @since 1.9
         */
        protected function setData() {
-               if ( method_exists( $this->parserOutput, 'getExtensionData' ) 
&& $this->useExtensionData ) {
+               if ( method_exists( $this->parserOutput, 'getExtensionData' ) ) 
{
                        $this->semanticData = 
$this->parserOutput->getExtensionData( 'smwdata' );
                } elseif ( isset( $this->parserOutput->mSMWData ) ) {
                        $this->semanticData = $this->parserOutput->mSMWData;
@@ -323,6 +313,8 @@
        /**
         * Update ParserOutput with processed semantic data
         *
+        * @note MW 1.21+ use setExtensionData()
+        *
         * @since 1.9
         *
         * @throws MWException
@@ -332,7 +324,7 @@
                        throw new MWException( 'The semantic data container is 
not available' );
                }
 
-               if ( method_exists( $this->parserOutput, 'setExtensionData' ) 
&& $this->useExtensionData ) {
+               if ( method_exists( $this->parserOutput, 'setExtensionData' ) ) 
{
                        $this->parserOutput->setExtensionData( 'smwdata', 
$this->semanticData );
                } else {
                        $this->parserOutput->mSMWData = $this->semanticData;
diff --git a/includes/ParserTextProcessor.php b/includes/ParserTextProcessor.php
new file mode 100644
index 0000000..771dab3
--- /dev/null
+++ b/includes/ParserTextProcessor.php
@@ -0,0 +1,408 @@
+<?php
+
+namespace SMW;
+
+use MagicWord;
+use Title;
+use Html;
+use SpecialPage;
+
+use SMWOutputs;
+use SMWDIWikiPage;
+use SMWDIProperty;
+
+/**
+ * Class collects all functions for wiki text parsing / processing that are
+ * relevant for SMW
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ * @ingroup Parser
+ *
+ * @author Markus Krötzsch
+ * @author Denny Vrandecic
+ * @author mwjames
+ */
+
+/**
+ * This class is contains all functions necessary for parsing wiki text before
+ * it is displayed or previewed while identifying SMW related
+ * annotations.
+ *
+ * @ingroup SMW
+ * @ingroup Parser
+ */
+class ParserTextProcessor {
+
+       /**
+        * Represents a Settings object
+        * @var Settings
+        */
+       protected $settings;
+
+       /**
+        * Represents a IParserData object
+        * @var IParserData
+        */
+       protected $parserData;
+
+       /**
+        * Represents $smwgNamespacesWithSemanticLinks status
+        * @var boolean
+        */
+       protected $isEnabled;
+
+       /**
+        * Internal state for switching SMW link annotations off/on during 
parsing
+        * ([[SMW::on]] and [[SMW:off]])
+        * @var boolean
+        */
+       protected $isAnnotation = true;
+
+       /**
+        * @par Example:
+        * @code
+        * $settings = Settings::newFromArray( array(
+        *  'smwgNamespacesWithSemanticLinks' => 
$GLOBALS['smwgNamespacesWithSemanticLinks'],
+        *  'smwgLinksInValues' => $GLOBALS['smwgLinksInValues'],
+        *  'smwgInlineErrors'  => $GLOBALS['smwgInlineErrors']
+        * ) );
+        *
+        * $parserData = new ParserData( $title, $parserOutput );
+        *
+        * new ParserTextProcessor( $parserData, $settings );
+        * @endcode
+        *
+        * @see SMWHooks::onInternalParseBeforeLinks
+        *
+        * @since 1.9
+        *
+        * @param IParserData $parserData
+        * @param Settings $settings
+        */
+       public function __construct( IParserData $parserData, Settings 
$settings ) {
+               $this->parserData = $parserData;
+               $this->settings = $settings;
+       }
+
+       /**
+        * Parsing text before an article is displayed or previewed, strip out
+        * semantic properties and add them to the ParserOutput object
+        *
+        * @since 1.9
+        *
+        * @param string &$text
+        */
+       public function parse( &$text ) {
+               $title = $this->parserData->getTitle();
+
+               // Strip magic words from text body
+               $this->stripMagicWords( $text );
+
+               // Attest if semantic data should be processed
+               $this->isSemanticEnabled( $title );
+
+               // Process redirects
+               $this->setRedirect( $title );
+
+               // Parse links to extract semantic properties
+               $linksInValues = $this->settings->get( 'smwgLinksInValues' );
+               $text = preg_replace_callback(
+                       $this->getRegexpPattern( $linksInValues ),
+                       $linksInValues ? 'self::process' : 'self::preprocess',
+                       $text
+               );
+
+               // Add RDF link to the HTML header.
+               $this->parserData->getOutput()->addHeadItem( "\t\t" . 
$this->getRDFUrl( $title ) . "\n" );
+
+               // Update ParserOutput
+               $this->parserData->getOutput()->addModules( $this->getModules() 
 );
+               $this->parserData->updateOutput();
+               SMWOutputs::commitToParserOutput( 
$this->parserData->getOutput() );
+       }
+
+       /**
+        * Attest if semantic data should be processed and displayed
+        * for a page in the given namespace. (have to parse them in
+        * any case, in order to clean the wiki source for further processing)
+        *
+        * @since 1.9
+        *
+        * @param Title $title
+        *
+        * @return boolean
+        */
+       protected function isSemanticEnabled( Title $title ) {
+               $namespaces = $this->settings->get( 
'smwgNamespacesWithSemanticLinks' );
+               $this->isEnabled = !empty( $namespaces[$title->getNamespace()] 
);
+       }
+
+       /**
+        * Returns required resource modules
+        *
+        * @since 1.9
+        *
+        * @return array
+        */
+       protected function getModules() {
+               return array(
+                       'ext.smw.style',
+                       'ext.smw.tooltips'
+               );
+       }
+
+       /**
+        * Process and add '_REDI' property in case the current Title is a 
redirect
+        *
+        * @since 1.9
+        *
+        * @param Title $title
+        */
+       protected function setRedirect( Title $title ) {
+               if ( $this->isEnabled && $title->isRedirect() ) {
+                       $this->parserData->getData()->addPropertyObjectValue(
+                               new SMWDIProperty( '_REDI' ),
+                               SMWDIWikiPage::newFromTitle( $title, '__red' )
+                       );
+               }
+       }
+
+       /**
+        * $smwgLinksInValues (default = false) determines which regexp pattern
+        * is returned, either a more complex (lib PCRE may cause segfaults if 
text
+        * is long) or a simpler (no segfaults found for those, but no links
+        * in values) pattern.
+        *
+        * If enabled (SMW accepts inputs like [[property::Some [[link]] in 
value]]),
+        * this may lead to PHP crashes (!) when very long texts are
+        * used as values. This is due to limitations in the library PCRE that
+        * PHP uses for pattern matching.
+        *
+        * @since 1.9
+        *
+        * @param boolean $linksInValues
+        *
+        * @return string
+        */
+       protected function getRegexpPattern( $linksInValues ) {
+               if ( $linksInValues ) {
+                       return '/\[\[             # Beginning of the link
+                               (?:([^:][^]]*):[=:])+ # Property name (or a 
list of those)
+                               (                     # After that:
+                                 (?:[^|\[\]]         #   either normal text 
(without |, [ or ])
+                                 |\[\[[^]]*\]\]      #   or a [[link]]
+                                 |\[[^]]*\]          #   or an [external link]
+                               )*)                   # all this zero or more 
times
+                               (?:\|([^]]*))?        # Display text (like 
"text" in [[link|text]]), optional
+                               \]\]                  # End of link
+                               /xu';
+               } else {
+                       return '/\[\[             # Beginning of the link
+                               (?:([^:][^]]*):[=:])+ # Property name (or a 
list of those)
+                               ([^\[\]]*)            # content: anything but 
[, |, ]
+                               \]\]                  # End of link
+                               /xu';
+               }
+       }
+
+       /**
+        * Returns ExportRDF URL
+        *
+        * @since 1.9
+        *
+        * @param Title $title
+        *
+        * @return string
+        */
+       protected function getRDFUrl( Title $title ) {
+               return Html::element( 'link', array(
+                       'type'  => 'application/rdf+xml',
+                       'title' => $title->getPrefixedText(),
+                       'href'  => SpecialPage::getTitleFor( 'ExportRDF', 
$title->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' )
+                       )
+               );
+       }
+
+       /**
+        * A method that precedes the process() callback, it takes care of 
separating
+        * value and caption (instead of leaving this to a more complex regexp).
+        *
+        * @since 1.9
+        *
+        * @param array $semanticLink expects (linktext, properties, 
value|caption)
+        *
+        * @return string
+        */
+       protected function preprocess( array $semanticLink ) {
+               $value = '';
+               $caption = false;
+
+               if ( array_key_exists( 2, $semanticLink ) ) {
+                       $parts = explode( '|', $semanticLink[2] );
+                       if ( array_key_exists( 0, $parts ) ) {
+                               $value = $parts[0];
+                       }
+                       if ( array_key_exists( 1, $parts ) ) {
+                               $caption = $parts[1];
+                       }
+               }
+
+               if ( $caption !== false ) {
+                       return $this->process( array( $semanticLink[0], 
$semanticLink[1], $value, $caption ) );
+               } else {
+                       return $this->process( array( $semanticLink[0], 
$semanticLink[1], $value ) );
+               }
+       }
+
+       /**
+        * This callback function strips out the semantic attributes from a wiki
+        * link.
+        *
+        * @since 1.9
+        *
+        * @param array $semanticLink expects (linktext, properties, 
value|caption)
+        *
+        * @return string
+        */
+       protected function process( array $semanticLink ) {
+               wfProfileIn( __METHOD__ );
+
+               if ( array_key_exists( 1, $semanticLink ) ) {
+                       $property = $semanticLink[1];
+               } else {
+                       $property = '';
+               }
+
+               if ( array_key_exists( 2, $semanticLink ) ) {
+                       $value = $semanticLink[2];
+               } else {
+                       $value = '';
+               }
+
+               if ( $value === '' ) { // silently ignore empty values
+                       wfProfileOut( __METHOD__ );
+                       return '';
+               }
+
+               if ( $property == 'SMW' ) {
+                       switch ( $value ) {
+                               case 'on':
+                                       $this->isAnnotation = true;
+                                       break;
+                               case 'off':
+                                       $this->isAnnotation = false;
+                                       break;
+                       }
+                       wfProfileOut( __METHOD__ );
+                       return '';
+               }
+
+               if ( array_key_exists( 3, $semanticLink ) ) {
+                       $valueCaption = $semanticLink[3];
+               } else {
+                       $valueCaption = false;
+               }
+
+               // Extract annotations and create tooltip.
+               $properties = preg_split( '/:[=:]/u', $property );
+
+               wfProfileOut( __METHOD__ );
+               return $this->addPropertyValue( $properties, $value, 
$valueCaption );
+       }
+
+       /**
+        * Adds property values to the ParserOutput instance
+        *
+        * @since 1.9
+        *
+        * @param array $properties
+        *
+        * @return string
+        */
+       protected function addPropertyValue( array $properties, $value, 
$valueCaption ) {
+               wfProfileIn( __METHOD__ );
+
+               $subject = $this->parserData->getData()->getSubject();
+
+               // Add properties to the semantic container
+               foreach ( $properties as $property ) {
+                       $dataValue = DataValueFactory::newPropertyValue(
+                               $property,
+                               $value,
+                               $valueCaption,
+                               $subject
+                       );
+
+                       if ( $this->isEnabled && $this->isAnnotation ) {
+                               $this->parserData->addPropertyValue( $dataValue 
);
+                       }
+               }
+
+               // Return the text representation
+               $result = $dataValue->getShortWikitext( true );
+
+               // If necessary add an error text
+               if ( ( $this->settings->get( 'smwgInlineErrors' ) &&
+                       $this->isEnabled && $this->isAnnotation ) &&
+                       ( !$dataValue->isValid() ) ) {
+                       $result .= $dataValue->getErrorText();
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $result;
+       }
+
+       /**
+        * Remove relevant SMW magic words from the given text and return
+        * an array of the names of all discovered magic words. Moreover,
+        * store this array in the current parser output, using the variable
+        * mSMWMagicWords and for MW 1.21+ 'smwmagicwords'
+        *
+        * @since 1.9
+        *
+        * @param &$text
+        *
+        * @return array
+        */
+       protected function stripMagicWords( &$text ) {
+               $words = array();
+               $mw = MagicWord::get( 'SMW_NOFACTBOX' );
+
+               if ( $mw->matchAndRemove( $text ) ) {
+                       $words[] = 'SMW_NOFACTBOX';
+               }
+
+               $mw = MagicWord::get( 'SMW_SHOWFACTBOX' );
+
+               if ( $mw->matchAndRemove( $text ) ) {
+                       $words[] = 'SMW_SHOWFACTBOX';
+               }
+
+               // Store values into the mSMWMagicWords property
+               if ( method_exists( $this->parserData->getOutput(), 
'setExtensionData' ) ) {
+                       $this->parserData->getOutput()->setExtensionData( 
'smwmagicwords', $words );
+               } else {
+                       $this->parserData->getOutput()->mSMWMagicWords = $words;
+               }
+
+               return $words;
+       }
+}
diff --git a/includes/SMW_ParserExtensions.php 
b/includes/SMW_ParserExtensions.php
deleted file mode 100644
index 989ce6b..0000000
--- a/includes/SMW_ParserExtensions.php
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-/**
- * Static class to collect all functions related to parsing wiki text in SMW.
- * It includes all parser function declarations and hooks.
- *
- * @file SMW_ParserExtensions.php
- * @ingroup SMW
- *
- * @author Markus Krötzsch
- * @author Denny Vrandecic
- */
-class SMWParserExtensions {
-
-       /// Temporarily store parser as it cannot be passed to call-back 
functions otherwise.
-       protected static $mTempParser;
-       /// Internal state for switching off/on SMW link annotations during 
parsing
-       protected static $mTempStoreAnnotations;
-
-       /**
-        * This method will be called before an article is displayed or 
previewed.
-        * For display and preview we strip out the semantic properties and 
append them
-        * at the end of the article.
-        *
-        * @param Parser $parser
-        * @param string $text
-        *
-        * @return true
-        */
-       static public function onInternalParseBeforeLinks( Parser &$parser, 
&$text ) {
-               global $smwgStoreAnnotations, $smwgLinksInValues;
-
-               SMWParserExtensions::stripMagicWords( $text, $parser );
-
-               // Store the results if enabled (we have to parse them in any 
case,
-               // in order to clean the wiki source for further processing).
-               $smwgStoreAnnotations = smwfIsSemanticsProcessed( 
$parser->getTitle()->getNamespace() );
-               SMWParserExtensions::$mTempStoreAnnotations = true; // used for 
[[SMW::on]] and [[SMW:off]]
-
-               // Process redirects, if any (it seems that there is indeed no 
more direct way of getting this info from MW)
-               if ( $smwgStoreAnnotations ) {
-                       $rt = Title::newFromRedirect( $text );
-
-                       if ( !is_null( $rt ) ) {
-                               $p = new SMWDIProperty( '_REDI' );
-                               $di = SMWDIWikiPage::newFromTitle( $rt, '__red' 
);
-                               SMWParseData::getSMWData( $parser 
)->addPropertyObjectValue( $p, $di );
-                       }
-               }
-
-               // only used in subsequent callbacks, forgotten afterwards
-               SMWParserExtensions::$mTempParser = $parser;
-
-               // In the regexp matches below, leading ':' escapes the markup, 
as known for Categories.
-               // Parse links to extract semantic properties.
-               if ( $smwgLinksInValues ) { // More complex regexp -- lib PCRE 
may cause segfaults if text is long :-(
-                       $semanticLinkPattern = '/\[\[                 # 
Beginning of the link
-                                               (?:([^:][^]]*):[=:])+ # 
Property name (or a list of those)
-                                               (                     # After 
that:
-                                                 (?:[^|\[\]]         #   
either normal text (without |, [ or ])
-                                                 |\[\[[^]]*\]\]      #   or a 
[[link]]
-                                                 |\[[^]]*\]          #   or an 
[external link]
-                                               )*)                   # all 
this zero or more times
-                                               (?:\|([^]]*))?        # Display 
text (like "text" in [[link|text]]), optional
-                                               \]\]                  # End of 
link
-                                               /xu';
-                       $text = preg_replace_callback( $semanticLinkPattern, 
array( 'SMWParserExtensions', 'parsePropertiesCallback' ), $text );
-               } else { // Simpler regexps -- no segfaults found for those, 
but no links in values.
-                       $semanticLinkPattern = '/\[\[                 # 
Beginning of the link
-                                               (?:([^:][^]]*):[=:])+ # 
Property name (or a list of those)
-                                               ([^\[\]]*)            # 
content: anything but [, |, ]
-                                               \]\]                  # End of 
link
-                                               /xu';
-                       $text = preg_replace_callback( $semanticLinkPattern, 
array( 'SMWParserExtensions', 'simpleParsePropertiesCallback' ), $text );
-               }
-
-               // Add link to RDF to HTML header.
-               // TODO: do escaping via Html or Xml class.
-               SMWOutputs::requireHeadItem(
-                       'smw_rdf', '<link rel="alternate" 
type="application/rdf+xml" title="' .
-                       htmlspecialchars( 
$parser->getTitle()->getPrefixedText() ) . '" href="' .
-                       htmlspecialchars(
-                               SpecialPage::getTitleFor( 'ExportRDF', 
$parser->getTitle()->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' )
-                       ) . "\" />"
-               );
-
-               SMWOutputs::commitToParser( $parser );
-               return true; // always return true, in order not to stop MW's 
hook processing!
-       }
-
-       /**
-        * This callback function strips out the semantic attributes from a wiki
-        * link. Expected parameter: array(linktext, properties, value|caption)
-        * This function is a preprocessing for smwfParsePropertiesCallback, and
-        * takes care of separating value and caption (instead of leaving this 
to
-        * a more complex regexp).
-        */
-       static public function simpleParsePropertiesCallback( $semanticLink ) {
-               $value = '';
-               $caption = false;
-
-               if ( array_key_exists( 2, $semanticLink ) ) {
-                       $parts = explode( '|', $semanticLink[2] );
-                       if ( array_key_exists( 0, $parts ) ) {
-                               $value = $parts[0];
-                       }
-                       if ( array_key_exists( 1, $parts ) ) {
-                               $caption = $parts[1];
-                       }
-               }
-
-               if ( $caption !== false ) {
-                       return SMWParserExtensions::parsePropertiesCallback( 
array( $semanticLink[0], $semanticLink[1], $value, $caption ) );
-               } else {
-                       return SMWParserExtensions::parsePropertiesCallback( 
array( $semanticLink[0], $semanticLink[1], $value ) );
-               }
-       }
-
-       /**
-        * This callback function strips out the semantic attributes from a wiki
-        * link. Expected parameter: array(linktext, properties, value, caption)
-        */
-       static public function parsePropertiesCallback( $semanticLink ) {
-               global $smwgInlineErrors, $smwgStoreAnnotations;
-
-               wfProfileIn( 'smwfParsePropertiesCallback (SMW)' );
-
-               if ( array_key_exists( 1, $semanticLink ) ) {
-                       $property = $semanticLink[1];
-               } else {
-                       $property = '';
-               }
-
-               if ( array_key_exists( 2, $semanticLink ) ) {
-                       $value = $semanticLink[2];
-               } else {
-                       $value = '';
-               }
-
-               if ( $value === '' ) { // silently ignore empty values
-                       wfProfileOut( 'smwfParsePropertiesCallback (SMW)' );
-                       return '';
-               }
-
-               if ( $property == 'SMW' ) {
-                       switch ( $value ) {
-                               case 'on':
-                                       
SMWParserExtensions::$mTempStoreAnnotations = true;
-                                       break;
-                               case 'off':
-                                       
SMWParserExtensions::$mTempStoreAnnotations = false;
-                                       break;
-                       }
-                       wfProfileOut( 'smwfParsePropertiesCallback (SMW)' );
-                       return '';
-               }
-
-               if ( array_key_exists( 3, $semanticLink ) ) {
-                       $valueCaption = $semanticLink[3];
-               } else {
-                       $valueCaption = false;
-               }
-
-               // Extract annotations and create tooltip.
-               $properties = preg_split( '/:[=:]/u', $property );
-
-               foreach ( $properties as $singleprop ) {
-                       $dv = SMWParseData::addProperty( $singleprop, $value, 
$valueCaption, SMWParserExtensions::$mTempParser, $smwgStoreAnnotations && 
SMWParserExtensions::$mTempStoreAnnotations );
-               }
-
-               $result = $dv->getShortWikitext( true );
-
-               if ( ( $smwgInlineErrors && $smwgStoreAnnotations && 
SMWParserExtensions::$mTempStoreAnnotations ) && ( !$dv->isValid() ) ) {
-                       $result .= $dv->getErrorText();
-               }
-
-               wfProfileOut( 'smwfParsePropertiesCallback (SMW)' );
-
-               return $result;
-       }
-
-       /**
-        * Remove relevant SMW magic words from the given text and return
-        * an array of the names of all discovered magic words. Moreover,
-        * store this array in the current parser output, using the variable
-        * mSMWMagicWords.
-        *
-        * @note moved from SMWParseData::stripMagicWords as it is only used
-        * in here
-        *
-        * @since  1.9
-        */
-       static public function stripMagicWords( &$text, Parser $parser ) {
-               $words = array();
-               $mw = MagicWord::get( 'SMW_NOFACTBOX' );
-
-               if ( $mw->matchAndRemove( $text ) ) {
-                       $words[] = 'SMW_NOFACTBOX';
-               }
-
-               $mw = MagicWord::get( 'SMW_SHOWFACTBOX' );
-
-               if ( $mw->matchAndRemove( $text ) ) {
-                       $words[] = 'SMW_SHOWFACTBOX';
-               }
-
-               $output = $parser->getOutput();
-               $output->mSMWMagicWords = $words;
-
-               return $words;
-       }
-
-}
diff --git a/includes/Setup.php b/includes/Setup.php
index 3354267..693f0e8 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -72,7 +72,8 @@
        // Additional special properties (modification date etc.)
        $wgHooks['NewRevisionFromEditComplete'][] = 
'SMWHooks::onNewRevisionFromEditComplete';
 
-       $wgHooks['InternalParseBeforeLinks'][] = 
'SMWParserExtensions::onInternalParseBeforeLinks'; // parse annotations in 
[[link syntax]]
+       // Parsing [[link::syntax]] and resolves property annotations
+       $wgHooks['InternalParseBeforeLinks'][] = 
'SMWHooks::onInternalParseBeforeLinks';
 
        $wgHooks['OutputPageParserOutput'][] = 
'SMWFactbox::onOutputPageParserOutput'; // copy some data for later Factbox 
display
        $wgHooks['ArticleFromTitle'][] = 'SMWHooks::onArticleFromTitle'; // 
special implementations for property/type articles
@@ -122,7 +123,7 @@
        $wgAutoloadClasses['SMWFactbox']                = $incDir . 
'SMW_Factbox.php';
        $wgAutoloadClasses['SMWInfolink']               = $incDir . 
'SMW_Infolink.php';
        $wgAutoloadClasses['SMWOutputs']                = $incDir . 
'SMW_Outputs.php';
-       $wgAutoloadClasses['SMWParserExtensions']       = $incDir . 
'SMW_ParserExtensions.php';
+       $wgAutoloadClasses['SMW\ParserTextProcessor']   = $incDir . 
'ParserTextProcessor.php';
        $wgAutoloadClasses['SMWQueryLanguage']          = $incDir . 
'SMW_QueryLanguage.php';
        $wgAutoloadClasses['SMWSemanticData']           = $incDir . 
'SMW_SemanticData.php';
        $wgAutoloadClasses['SMWPageLister']             = $incDir . 
'SMW_PageLister.php';
diff --git a/tests/phpunit/includes/HooksTest.php 
b/tests/phpunit/includes/HooksTest.php
index e0ad9df..66eed79 100644
--- a/tests/phpunit/includes/HooksTest.php
+++ b/tests/phpunit/includes/HooksTest.php
@@ -382,4 +382,19 @@
                        }
                }
        }
+
+       /**
+        * @test SMWHooks::onInternalParseBeforeLinks
+        * @dataProvider getTextDataProvider
+        *
+        * @since 1.9
+        *
+        * @param $text
+        */
+       public function testOnInternalParseBeforeLinks( $text ) {
+               $parser = $this->getParser();
+               $result = SMWHooks::onInternalParseBeforeLinks( $parser, $text 
);
+
+               $this->assertTrue( $result );
+       }
 }
diff --git a/tests/phpunit/includes/ParserTextProcessorTest.php 
b/tests/phpunit/includes/ParserTextProcessorTest.php
new file mode 100644
index 0000000..2584bc5
--- /dev/null
+++ b/tests/phpunit/includes/ParserTextProcessorTest.php
@@ -0,0 +1,389 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\ParserTextProcessor;
+use SMW\ParserData;
+use SMW\Settings;
+
+use SMWDIProperty;
+use SMWDataItem;
+use SMWDataValueFactory;
+use Title;
+use MWException;
+use ParserOutput;
+use ReflectionClass;
+
+/**
+ * Tests for the SMW\ParserTextProcessor class
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ * @ingroup SMWParser
+ * @ingroup Test
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Testing methods provided by the ParserTextProcessor class
+ *
+ * @ingroup SMW
+ */
+class ParserTextProcessorTest extends \MediaWikiTestCase {
+
+       protected $className = 'SMW\ParserTextProcessor';
+
+       /**
+        * Provides text samples
+        *
+        * @return array
+        */
+       public function getTextDataProvider() {
+               return array(
+
+                       // #0 NS_MAIN; [[FooBar...]] with a different caption
+                       array(
+                               NS_MAIN,
+                               array(
+                                       'smwgNamespacesWithSemanticLinks' => 
array( NS_MAIN => true ),
+                                       'smwgLinksInValues' => false,
+                                       'smwgInlineErrors' => true,
+                               ),
+                               'Lorem ipsum dolor sit &$% consectetuer auctor 
at quis' .
+                               ' [[FooBar::dictumst|寒い]] cursus. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' Aliquam {{volutpat}} arcu ultrices eu Ut quis 
[[foo::9001]] et Donec.',
+                               array(
+                                       'resultText'    => 'Lorem ipsum dolor 
sit &$% consectetuer auctor at quis' .
+                                               ' [[:Dictumst|寒い]] cursus. Nisl 
sit condimentum Quisque facilisis' .
+                                               ' Suspendisse [[:Tincidunt 
semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+                                               ' Aliquam {{volutpat}} arcu 
ultrices eu Ut quis [[:9001|9001]] et Donec.',
+                                       'propertyCount' => 3,
+                                       'propertyLabel' => array( 'Foo', 'Bar', 
'FooBar' ),
+                                       'propertyValue' => array( 'Dictumst', 
'Tincidunt semper', '9001' )
+                               )
+                       ),
+
+                       // #1 NS_MAIN; [[FooBar...]] with a different caption 
and smwgLinksInValues = true
+                       array(
+                               NS_MAIN,
+                               array(
+                                       'smwgNamespacesWithSemanticLinks' => 
array( NS_MAIN => true ),
+                                       'smwgLinksInValues' => true,
+                                       'smwgInlineErrors' => true,
+                               ),
+                               'Lorem ipsum dolor sit &$% consectetuer auctor 
at quis' .
+                               ' [[FooBar::dictumst|寒い]] cursus. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::[[tincidunt semper]]]] 
facilisi dolor Aenean. Ut' .
+                               ' Aliquam {{volutpat}} arcu ultrices eu Ut quis 
[[foo::[http:://www/foo/9001] ]] et Donec.',
+                               array(
+                                       'resultText'    => 'Lorem ipsum dolor 
sit &$% consectetuer auctor at quis' .
+                                               ' [[:Dictumst|寒い]] cursus. Nisl 
sit condimentum Quisque facilisis' .
+                                               ' Suspendisse [[:Tincidunt 
semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+                                               ' Aliquam {{volutpat}} arcu 
ultrices eu Ut quis'.
+                                               ' 
[[:Http:://www/foo/9001|http:://www/foo/9001]] et Donec.',
+                                       'propertyCount' => 3,
+                                       'propertyLabel' => array( 'Foo', 'Bar', 
'FooBar' ),
+                                       'propertyValue' => array( 'Dictumst', 
'Tincidunt semper', 'Http:://www/foo/9001' )
+                               )
+                       ),
+
+                       // #1 NS_MAIN, [[-FooBar...]] produces an error with 
inlineErrors = true
+                       // (only check for an indication of an error in 
'resultText' )
+                       array(
+                               NS_MAIN,
+                               array(
+                                       'smwgNamespacesWithSemanticLinks' => 
array( NS_MAIN => true ),
+                                       'smwgLinksInValues' => false,
+                                       'smwgInlineErrors' => true,
+                               ),
+                               'Lorem ipsum dolor sit &$% consectetuer auctor 
at quis' .
+                               ' [[-FooBar::dictumst|重い]] cursus. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' Aliquam {{volutpat}} arcu ultrices eu Ut quis 
[[foo::9001]] et Donec.',
+                               array(
+                                       'resultText'    => 
'class="smw-highlighter" data-type="4" data-state="inline"',
+                                       'propertyCount' => 2,
+                                       'propertyLabel' => array( 'Foo', 'Bar' 
),
+                                       'propertyValue' => array( 'Tincidunt 
semper', '9001' )
+                               )
+                       ),
+
+                       // #2 NS_MAIN, [[-FooBar...]] produces an error but 
inlineErrors = false
+                       array(
+                               NS_MAIN,
+                               array(
+                                       'smwgNamespacesWithSemanticLinks' => 
array( NS_MAIN => true ),
+                                       'smwgLinksInValues' => false,
+                                       'smwgInlineErrors' => false,
+                               ),
+                               'Lorem ipsum dolor sit &$% consectetuer auctor 
at quis' .
+                               ' [[-FooBar::dictumst|軽い]] cursus. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' Aliquam {{volutpat}} arcu ultrices eu Ut quis 
[[foo::9001]] et Donec.',
+                               array(
+                                       'resultText'    => 'Lorem ipsum dolor 
sit &$% consectetuer auctor at quis' .
+                                               ' 軽い cursus. Nisl sit 
condimentum Quisque facilisis' .
+                                               ' Suspendisse [[:Tincidunt 
semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+                                               ' Aliquam {{volutpat}} arcu 
ultrices eu Ut quis [[:9001|9001]] et Donec.',
+                                       'propertyCount' => 2,
+                                       'propertyLabel' => array( 'Foo', 'Bar' 
),
+                                       'propertyValue' => array( 'Tincidunt 
semper', '9001' )
+                               )
+                       ),
+
+                       // #3 NS_HELP disabled
+                       array(
+                               NS_HELP,
+                               array(
+                                       'smwgNamespacesWithSemanticLinks' => 
array( NS_HELP => false ),
+                                       'smwgLinksInValues' => false,
+                                       'smwgInlineErrors' => true,
+                               ),
+                               'Lorem ipsum dolor sit &$% consectetuer auctor 
at quis' .
+                               ' [[FooBar::dictumst|おもろい]] cursus. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' Aliquam {{volutpat}} arcu ultrices eu Ut quis 
[[foo::9001]] et Donec.',
+                               array(
+                                       'resultText'    => 'Lorem ipsum dolor 
sit &$% consectetuer auctor at quis' .
+                                               ' [[:Dictumst|おもろい]] cursus. 
Nisl sit condimentum Quisque facilisis' .
+                                               ' Suspendisse [[:Tincidunt 
semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+                                               ' Aliquam {{volutpat}} arcu 
ultrices eu Ut quis [[:9001|9001]] et Donec.',
+                                       'propertyCount' => 0,
+                                       'propertyLabel' => array(),
+                                       'propertyValue' => array()
+                               )
+                       ),
+               );
+       }
+
+       /**
+        * Provides magic words sample text
+        *
+        * @return array
+        */
+       public function getMagicWordDataProvider() {
+               return array(
+                       // #0 __NOFACTBOX__
+                       array(
+                               NS_MAIN,
+                               'Lorem ipsum dolor sit amet consectetuer auctor 
at quis' .
+                               ' [[Foo::dictumst cursus]]. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' __NOFACTBOX__ ',
+                               array( 'SMW_NOFACTBOX' )
+                       ),
+
+                       // #1 __SHOWFACTBOX__
+                       array(
+                               NS_HELP,
+                               'Lorem ipsum dolor sit amet consectetuer auctor 
at quis' .
+                               ' [[Foo::dictumst cursus]]. Nisl sit 
condimentum Quisque facilisis' .
+                               ' Suspendisse [[Bar::tincidunt semper]] 
facilisi dolor Aenean. Ut' .
+                               ' __SHOWFACTBOX__',
+                               array( 'SMW_SHOWFACTBOX' )
+                       ),
+               );
+       }
+
+       /**
+        * Helper method that returns a random string
+        *
+        * @since 1.9
+        *
+        * @param $length
+        *
+        * @return string
+        */
+       private function getRandomString( $length = 10 ) {
+               return substr( str_shuffle( 
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ), 0, $length 
);
+       }
+
+       /**
+        * Helper method that returns a Title object
+        *
+        * @param $namespace
+        *
+        * @return Title
+        */
+       private function getTitle( $namespace = NS_MAIN ){
+               return Title::newFromText( $this->getRandomString(), $namespace 
);
+       }
+
+       /**
+        * Helper method that returns a ParserOutput object
+        *
+        * @return ParserOutput
+        */
+       private function getParserOutput(){
+               return new ParserOutput();
+       }
+
+       /**
+        * Helper method that returns a ParserData object
+        *
+        * @param $title
+        * @param $parserOutput
+        *
+        * @return ParserData
+        */
+       private function getParserData( Title $title, ParserOutput 
$parserOutput ){
+               return new ParserData( $title, $parserOutput );
+       }
+
+       /**
+        * Helper method that returns a ParserTextProcessor object
+        *
+        * @param $title
+        * @param $parserOutput
+        * @param $settings
+        *
+        * @return ParserTextProcessor
+        */
+       private function getInstance( Title $title, ParserOutput $parserOutput, 
array $settings = array() ) {
+               return new ParserTextProcessor(
+                       $this->getParserData( $title, $parserOutput ),
+                       Settings::newFromArray( $settings )
+               );
+       }
+
+       /**
+        * @test ParserTextProcessor::__construct
+        * @dataProvider getTextDataProvider
+        *
+        * @since 1.9
+        *
+        * @param $namespace
+        */
+       public function testConstructor( $namespace ) {
+               $instance = $this->getInstance( $this->getTitle( $namespace ), 
$this->getParserOutput() );
+               $this->assertInstanceOf( $this->className, $instance );
+       }
+
+       /**
+        * @test ParserTextProcessor::getRDFUrl
+        *
+        * @since 1.9
+        */
+       public function testGetRDFUrl() {
+               $reflection = new ReflectionClass( $this->className );
+
+               $parserOutput = $this->getParserOutput();
+               $title = $this->getTitle();
+               $instance =     $this->getInstance( $title, $parserOutput );
+
+               // Make proected method accessible
+               $method = $reflection->getMethod( 'getRDFUrl' );
+               $method->setAccessible( true );
+
+               // Invoke the instance
+               $result = $method->invoke( $instance, $title );
+
+               // Doing a lazy check, the url has to contain the title text
+               $this->assertInternalType( 'string', $result );
+               $this->assertContains( 'title="' . $title->getText() . '"', 
$result );
+       }
+
+       /**
+        * @test ParserTextProcessor::stripMagicWords
+        * @dataProvider getMagicWordDataProvider
+        *
+        * @since 1.9
+        *
+        * @param $namespace
+        * @param $text
+        * @param $expected
+        */
+       public function testStripMagicWords( $namespace, $text, array $expected 
) {
+               $reflection = new ReflectionClass( $this->className );
+
+               $parserOutput = $this->getParserOutput();
+               $title = $this->getTitle( $namespace );
+               $instance =     $this->getInstance( $title, $parserOutput );
+
+               // Make protected method accessible
+               $method = $reflection->getMethod( 'stripMagicWords' );
+               $method->setAccessible( true );
+
+               $result = $method->invoke( $instance, array( &$text ) );
+
+               // Check return values
+               $this->assertInternalType( 'array', $result );
+               $this->assertEquals( $expected, $result );
+
+               // Check values against ParserData/ParserOutput object
+               $parserData = $this->getParserData( $title, $parserOutput );
+
+               if ( method_exists( $parserOutput, 'getExtensionData' ) ) {
+                       $this->assertEquals( $expected, 
$parserData->getOutput()->getExtensionData( 'smwmagicwords' ) );
+               } else {
+                       $this->assertEquals( $expected, 
$parserData->getOutput()->mSMWMagicWords );
+               }
+       }
+
+       /**
+        * @test ParserTextProcessor::parse
+        * @dataProvider getTextDataProvider
+        *
+        * @since 1.9
+        *
+        * @param $namespace
+        * @param $settings
+        * @param $text
+        * @param $expected
+        */
+       public function testParse( $namespace, array $settings, $text, array 
$expected ) {
+               $parserOutput =  $this->getParserOutput();
+               $title = $this->getTitle( $namespace );
+               $instance = $this->getInstance( $title, $parserOutput, 
$settings );
+
+               // Text parsing
+               $instance->parse( $text );
+
+               // Check transformed text
+               $this->assertContains( $expected['resultText'], $text );
+
+               // Re-read data from stored parserOutput
+               $parserData = $this->getParserData( $title, $parserOutput );
+
+               // Check the returned instance
+               $this->assertInstanceOf( 'SMWSemanticData', 
$parserData->getData() );
+               $this->assertCount( $expected['propertyCount'], 
$parserData->getData()->getProperties() );
+
+               // Check added properties
+               foreach ( $parserData->getData()->getProperties() as $key => 
$diproperty ){
+                       $this->assertInstanceOf( 'SMWDIProperty', $diproperty );
+                       $this->assertContains( $diproperty->getLabel(), 
$expected['propertyLabel'] );
+
+                       // Check added property values
+                       foreach ( $parserData->getData()->getPropertyValues( 
$diproperty ) as $dataItem ){
+                               $dataValue = 
SMWDataValueFactory::newDataItemValue( $dataItem, $diproperty );
+                               if ( $dataValue->getDataItem()->getDIType() === 
SMWDataItem::TYPE_WIKIPAGE ){
+                                       $this->assertContains( 
$dataValue->getWikiValue(), $expected['propertyValue'] );
+                               }
+                       }
+               }
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/60393
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I66b812e0276820fa58dbbfbc632236ea6f8ba6d5
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/SemanticMediaWiki
Gerrit-Branch: master
Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to