r98687 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r98686‎ | r98687 | r98688 >
Date:16:36, 2 October 2011
Author:mkroetzsch
Status:deferred
Tags:
Comment:
Improved support for subobjects (encoded using fragments);
new experimental parser function #subobject to allow arbitrary subobjects to be created
Modified paths:
  • /trunk/extensions/SemanticMediaWiki/includes/SMW_Factbox.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/SMW_SemanticData.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/SMW_Setup.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/parserhooks/SMW_Subobject.php (added) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStore.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStoreQueryEngine.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/specials/SearchTriple/SMW_SpecialSearchByProperty.php (modified) (history)

Diff [purge]

Index: trunk/extensions/SemanticMediaWiki/specials/SearchTriple/SMW_SpecialSearchByProperty.php
@@ -205,7 +205,7 @@
206206 $html .= '<li>' . $result[0]->getLongHTMLText( smwfGetLinker() );
207207
208208 if ( $result[0]->getTypeID() == '_wpg' ) {
209 - $html .= '&#160;&#160;' . SMWInfolink::newBrowsingLink( '+', $result[0]->getShortHTMLText() )->getHTML( smwfGetLinker() );
 209+ $html .= '&#160;&#160;' . SMWInfolink::newBrowsingLink( '+', $result[0]->getLongWikiText() )->getHTML( smwfGetLinker() );
210210 }
211211
212212 if ( is_object( $result[1] ) && ( ( $this->value != $result[1] ) || $highlight ) ) {
Index: trunk/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php
@@ -11,34 +11,6 @@
1212 */
1313
1414 /**
15 - * Small data object that specifies one wiki page to be serialised.
16 - * SMWSmallTitle objects are used to queue pages for serialisation, hence it
17 - * should be small to save memory.
18 - *
19 - * @ingroup SMW
20 - */
21 -class SMWSmallTitle {
22 - /// DB key version of the title.
23 - public $dbkey;
24 - /// MediaWiki namespace constant.
25 - public $namespace;
26 - /**
27 - * Recursion depth for serialising this object. Depth of 1 or above means
28 - * the object is serialised with all property values, and referenced
29 - * objects are serialised with depth reduced by 1. Depth 0 means that only
30 - * minimal declarations are serialised, so no dependencies are added. A
31 - * depth of -1 encodes "infinite" depth, i.e. a complete recursive
32 - * serialisation without limit.
33 - * @var integer
34 - */
35 - public $recdepth = 1;
36 -
37 - public function getHash() {
38 - return $this->dbkey . ' ' . $this->namespace;
39 - }
40 -}
41 -
42 -/**
4315 * Class for controlling the export of SMW page data, supporting high-level
4416 * features such as recursive export and backlink inclusion. The class controls
4517 * export independent of the serialisation syntax that is used.
@@ -135,18 +107,20 @@
136108 * level of pages, i.e. it serialises parts of SMW content and implements
137109 * features like recursive export or backlinks that are available for this
138110 * type of data.
 111+ *
 112+ * The recursion depth means the following. Depth of 1 or above means
 113+ * the object is serialised with all property values, and referenced
 114+ * objects are serialised with depth reduced by 1. Depth 0 means that only
 115+ * minimal declarations are serialised, so no dependencies are added. A
 116+ * depth of -1 encodes "infinite" depth, i.e. a complete recursive
 117+ * serialisation without limit.
139118 *
140119 * @param SMWDIWikiPage $diWikiPage specifying the page to be exported
141 - * @param integer $recursiondepth specifying the depth of recursion, see
142 - * SMWSmallTitle::$recdepth
 120+ * @param integer $recursiondepth specifying the depth of recursion
143121 */
144122 protected function serializePage( SMWDIWikiPage $diWikiPage, $recursiondepth = 1 ) {
145 - $st = new SMWSmallTitle();
146 - $st->dbkey = $diWikiPage->getDBKey();
147 - $st->namespace = $diWikiPage->getNamespace();
148 - $st->recdepth = $recursiondepth;
149 - if ( $this->isDone( $st ) ) return; // do not export twice
150 - $this->markAsDone( $st );
 123+ if ( $this->isPageDone( $diWikiPage, $recursiondepth ) ) return; // do not export twice
 124+ $this->markPageAsDone( $diWikiPage, $recursiondepth );
151125 $data = SMWExporter::makeExportData( $this->getSemanticData( $diWikiPage, ( $recursiondepth == 0 ) ) );
152126 $this->serializer->serializeExpData( $data, $recursiondepth );
153127
@@ -195,11 +169,7 @@
196170 }
197171 $inSubs = smwfGetStore()->getPropertySubjects( $inprop, $diWikiPage );
198172 foreach ( $inSubs as $inSub ) {
199 - $stb = new SMWSmallTitle();
200 - $stb->dbkey = $inSub->getDBkey();
201 - $stb->namespace = $inSub->getNamespace();
202 - $stb->recdepth = $subrecdepth;
203 - if ( !$this->isDone($stb) ) {
 173+ if ( !$this->isPageDone( $inSub, $subrecdepth ) ) {
204174 $semdata = $this->getSemanticData( $inSub, true );
205175 $semdata->addPropertyObjectValue( $inprop, $diWikiPage );
206176 $data = SMWExporter::makeExportData( $semdata );
@@ -215,11 +185,7 @@
216186 $pinst = new SMWDIProperty( '_INST' );
217187
218188 foreach ( $instances as $instance ) {
219 - $stb = new SMWSmallTitle();
220 - $stb->dbkey = $instance->getDBkey();
221 - $stb->namespace = $instance->getNamespace();
222 -
223 - if ( !array_key_exists( $stb->getHash(), $this->element_done ) ) {
 189+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
224190 $semdata = $this->getSemanticData( $instance, true );
225191 $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
226192 $data = SMWExporter::makeExportData( $semdata );
@@ -238,12 +204,8 @@
239205
240206 while ( $resarray !== false ) {
241207 $instance = end( $resarray )->getNextDataValue();
242 -
243 - $stb = new SMWSmallTitle();
244 - $stb->dbkey = $instance->getDBkey();
245 - $stb->namespace = $instance->getNamespace();
246 -
247 - if ( !array_key_exists( $stb->getHash(), $this->element_done ) ) {
 208+
 209+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
248210 $semdata = $this->getSemanticData( $instance, true );
249211 $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
250212 $data = SMWExporter::makeExportData( $semdata );
@@ -259,57 +221,57 @@
260222 }
261223
262224 /**
263 - * Serialize data associated to a specific page.
264 - *
265 - * @param SMWSmallTitle $st specifying the page to be exported
266 - */
267 - protected function serializeSmallTitle( SMWSmallTitle $st ) {
268 - if ( $this->isDone( $st ) ) return; // do not export twice
269 - $diWikiPage = new SMWDIWikiPage( $st->dbkey, $st->namespace, '' );
270 - $this->serializePage( $diWikiPage, $st->recdepth );
271 - }
272 -
273 - /**
274225 * Add a given SMWDIWikiPage to the export queue if needed.
275226 */
276227 protected function queuePage( SMWDIWikiPage $diWikiPage, $recursiondepth ) {
277 - $spt = new SMWSmallTitle();
278 - $spt->dbkey = $diWikiPage->getDBkey();
279 - $spt->namespace = $diWikiPage->getNamespace();
280 - $spt->recdepth = $recursiondepth;
281 - if ( !$this->isDone( $spt ) ) {
282 - $this->element_queue[$spt->getHash()] = $spt;
 228+ if ( !$this->isPageDone( $diWikiPage, $recursiondepth ) ) {
 229+ $diWikiPage->recdepth = $recursiondepth; // add a field
 230+ $this->element_queue[$diWikiPage->getHash()] = $diWikiPage;
283231 }
284232 }
285233
286234 /**
287235 * Mark an article as done while making sure that the cache used for this
288 - * stays reasonably small. Input is given as an SMWSmallTitle object.
 236+ * stays reasonably small. Input is given as an SMWDIWikiPage object.
289237 */
290 - protected function markAsDone( $st ) {
 238+ protected function markPageAsDone( SMWDIWikiPage $di, $recdepth ) {
 239+ $this->markHashAsDone( $di->getHash(), $recdepth );
 240+ }
 241+
 242+ /**
 243+ * Mark a task as done while making sure that the cache used for this
 244+ * stays reasonably small.
 245+ */
 246+ protected function markHashAsDone( $hash, $recdepth ) {
291247 if ( count( $this->element_done ) >= self::MAX_CACHE_SIZE ) {
292248 $this->element_done = array_slice( $this->element_done,
293 - self::CACHE_BACKJUMP,
294 - self::MAX_CACHE_SIZE - self::CACHE_BACKJUMP,
295 - true );
 249+ self::CACHE_BACKJUMP,
 250+ self::MAX_CACHE_SIZE - self::CACHE_BACKJUMP,
 251+ true );
296252 }
297 - $hash = $st->getHash();
298 - if ( !$this->isDone( $st ) ) {
299 - $this->element_done[$hash] = $st->recdepth; // mark title as done, with given recursion
 253+ if ( !$this->isHashDone( $hash, $recdepth ) ) {
 254+ $this->element_done[$hash] = $recdepth; // mark title as done, with given recursion
300255 }
301256 unset( $this->element_queue[$hash] ); // make sure it is not in the queue
302257 }
303 -
 258+
304259 /**
305260 * Check if the given object has already been serialised at sufficient
306261 * recursion depth.
307 - * @param SMWSmallTitle $st specifying the object to check
 262+ * @param SMWDIWikiPage $st specifying the object to check
308263 */
309 - protected function isDone( SMWSmallTitle $st ) {
310 - $hash = $st->getHash();
 264+ protected function isPageDone( SMWDIWikiPage $di, $recdepth ) {
 265+ return $this->isHashDone( $di->getHash(), $recdepth );
 266+ }
 267+
 268+ /**
 269+ * Check if the given task has already been completed at sufficient
 270+ * recursion depth.
 271+ */
 272+ protected function isHashDone( $hash, $recdepth ) {
311273 return ( ( array_key_exists( $hash, $this->element_done ) ) &&
312274 ( ( $this->element_done[$hash] == -1 ) ||
313 - ( ( $st->recdepth != -1 ) && ( $this->element_done[$hash] >= $st->recdepth ) ) ) );
 275+ ( ( $recdepth != -1 ) && ( $this->element_done[$hash] >= $recdepth ) ) ) );
314276 }
315277
316278 /**
@@ -386,11 +348,9 @@
387349 $rev = Revision::getTimeStampFromID( $title, $title->getLatestRevID() );
388350 if ( $rev < $revisiondate ) continue;
389351 }
390 - $st = new SMWSmallTitle();
391 - $st->dbkey = $title->getDBkey();
392 - $st->namespace = $title->getNamespace();
393 - $st->recdepth = $recursion==1 ? -1 : 1;
394 - $this->element_queue[$st->getHash()] = $st;
 352+
 353+ $diPage = SMWDIWikiPage::newFromTitle( $title );
 354+ $this->queuePage( $diPage, ( $recursion==1 ? -1 : 1 ) );
395355 }
396356
397357 $this->serializer->startSerialization();
@@ -403,7 +363,8 @@
404364 $this->serializer->serializeExpData( SMWExporter::getOntologyExpData( $ontologyuri ) );
405365
406366 while ( count( $this->element_queue ) > 0 ) {
407 - $this->serializeSmallTitle( reset( $this->element_queue ) );
 367+ $diPage = reset( $this->element_queue );
 368+ $this->serializePage( $diPage, $diPage->recdepth );
408369 $this->flush();
409370 $linkCache->clear(); // avoid potential memory leak
410371 }
@@ -444,18 +405,16 @@
445406 if ( !SMWExportController::fitsNsRestriction( $ns_restriction, $title->getNamespace() ) ) continue;
446407 $a_count += 1; // DEBUG
447408
448 - $st = new SMWSmallTitle();
449 - $st->dbkey = $title->getDBkey();
450 - $st->namespace = $title->getNamespace();
451 - $st->recdepth = 1;
452 - $this->element_queue[$st->getHash()] = $st;
 409+ $diPage = SMWDIWikiPage::newFromTitle( $title );
 410+ $this->queuePage( $diPage, 1 );
453411
454412 while ( count( $this->element_queue ) > 0 ) {
455 - $this->serializeSmallTitle( reset( $this->element_queue ) );
 413+ $diPage = reset( $this->element_queue );
 414+ $this->serializePage( $diPage, $diPage->recdepth );
456415 // resolve dependencies that will otherwise not be printed
457 - foreach ( $this->element_queue as $key => $staux ) {
458 - if ( !smwfIsSemanticsProcessed( $staux->namespace ) ||
459 - !SMWExportController::fitsNsRestriction( $ns_restriction, $staux->namespace ) ) {
 416+ foreach ( $this->element_queue as $key => $diaux ) {
 417+ if ( !smwfIsSemanticsProcessed( $diaux->getNamespace() ) ||
 418+ !SMWExportController::fitsNsRestriction( $ns_restriction, $diaux->getNamespace() ) ) {
460419 // Note: we do not need to check the cache to guess if an element was already
461420 // printed. If so, it would not be included in the queue in the first place.
462421 $d_count += 1; // DEBUG
@@ -512,14 +471,16 @@
513472
514473 foreach ( $res as $row ) {
515474 $foundpages = true;
516 - $st = new SMWSmallTitle();
517 - $st->dbkey = $row->page_title;
518 - $st->namespace = $row->page_namespace;
519 - $st->recdepth = 0;
520 - $this->serializeSmallTitle( $st );
521 - $this->flush();
522 - $linkCache->clear();
 475+ try {
 476+ $diPage = new SMWDIWikiPage( $row->page_title, $row->page_namespace, '' );
 477+ $this->serializePage( $diPage, 0 );
 478+ $this->flush();
 479+ $linkCache->clear();
 480+ } catch ( SMWDataItemException $e ) {
 481+ // strange data, who knows, not our DB table, keep calm and carry on
 482+ }
523483 }
 484+
524485 if ( $foundpages ) { // add link to next result page
525486 if ( strpos( SMWExporter::expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
526487 $nexturl = SMWExporter::expandURI( '&export;?offset=' ) . ( $offset + $limit );
Index: trunk/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php
@@ -45,14 +45,11 @@
4646 * Create exportable data from a given semantic data record.
4747 *
4848 * @param $semdata SMWSemanticData
49 - * @param $subject mixed SMWDIWikiPage to use as subject, or null to use the one from $semdata
5049 * @return SMWExpData
5150 */
52 - static public function makeExportData( SMWSemanticData $semdata, $subject = null ) {
 51+ static public function makeExportData( SMWSemanticData $semdata ) {
5352 self::initBaseURIs();
54 - if ( is_null( $subject ) ) {
55 - $subject = $semdata->getSubject();
56 - }
 53+ $subject = $semdata->getSubject();
5754 if ( $subject->getNamespace() == SMW_NS_PROPERTY ) {
5855 $types = $semdata->getPropertyValues( new SMWDIProperty( '_TYPE' ) );
5956 } else {
@@ -81,13 +78,13 @@
8279 */
8380 static public function makeExportDataForSubject( SMWDIWikiPage $diWikiPage, $typesvalueforproperty = null, $addStubData = false ) {
8481 global $wgContLang;
85 - $wikiPageExpElement = self::getDataItemExpElement( $diWikiPage, $diWikiPage );
 82+ $wikiPageExpElement = self::getDataItemExpElement( $diWikiPage );
8683 $result = new SMWExpData( $wikiPageExpElement );
8784
8885 if ( $diWikiPage->getSubobjectName() != '' ) {
8986 $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), self::getSpecialNsResource( 'swivt', 'Subject' ) );
9087 $masterPage = new SMWDIWikiPage( $diWikiPage->getDBkey(), $diWikiPage->getNamespace(), $diWikiPage->getInterwiki() );
91 - $masterExpElement = self::getDataItemExpElement( $masterPage, $masterPage );
 88+ $masterExpElement = self::getDataItemExpElement( $masterPage );
9289 $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'masterPage' ), $masterExpElement );
9390 } else {
9491 $pageTitle = str_replace( '_', ' ', $diWikiPage->getDBkey() );
@@ -145,14 +142,13 @@
146143 * @param $property SMWDIProperty
147144 * @param $dataItems array of SMWDataItem objects for the given property
148145 * @param $data SMWExpData to add the data to
149 - * @param $masterPage SMWDIWikiPage to which the data belongs; needed for internal object URIs
150146 */
151 - static public function addPropertyValues( SMWDIProperty $property, array $dataItems, SMWExpData &$expData, SMWDIWikiPage $masterPage ) {
 147+ static public function addPropertyValues( SMWDIProperty $property, array $dataItems, SMWExpData &$expData ) {
152148 if ( $property->isUserDefined() ) {
153149 $pe = self::getResourceElementForProperty( $property );
154150 $peHelper = self::getResourceElementForProperty( $property, true );
155151 foreach ( $dataItems as $dataItem ) {
156 - $ed = self::getDataItemExpElement( $dataItem, $masterPage );
 152+ $ed = self::getDataItemExpElement( $dataItem );
157153 if ( $ed !== null ) {
158154 $expData->addPropertyObjectValue( $pe, $ed );
159155 }
@@ -186,7 +182,7 @@
187183 ( $dataItem->getNamespace() != $diSubject->getNamespace() ) ) ) {
188184 continue;
189185 }
190 - $ed = self::getDataItemExpElement( $dataItem, $masterPage );
 186+ $ed = self::getDataItemExpElement( $dataItem );
191187 if ( $ed !== null ) {
192188 if ( ( $property->getKey() == '_CONC' ) && ( $ed->getSubject()->getUri() == '' ) ) {
193189 // equivalent to anonymous class -> simplify description
@@ -273,9 +269,6 @@
274270 $namespace = $importValue->getNS();
275271 $namespaceId = $importValue->getNSID();
276272 $localName = $importValue->getLocalName();
277 - } elseif ( self::isInternalObjectDiPage( $diWikiPage ) ) { // blank node
278 - $localName = $namespace = $namespaceId = '';
279 - $diWikiPage = null; // do not associate any wiki page with blank nodes
280273 } else {
281274 $localName = '';
282275 if ( $diWikiPage->getNamespace() == SMW_NS_PROPERTY ) {
@@ -537,10 +530,9 @@
538531 * SMWExporter::getSpecialPropertyResource().
539532 *
540533 * @param $dataItem SMWDataItem
541 - * @param $masterPage mixed SMWDIWikiPage to which the data belongs (needed for internal object URIs); or NULL if deemed irrelevant
542534 * @return SMWExpElement
543535 */
544 - static public function getDataItemExpElement( SMWDataItem $dataItem, $masterPage ) {
 536+ static public function getDataItemExpElement( SMWDataItem $dataItem ) {
545537 switch ( $dataItem->getDIType() ) {
546538 case SMWDataItem::TYPE_NUMBER:
547539 $lit = new SMWExpLiteral( $dataItem->getNumber(), 'http://www.w3.org/2001/XMLSchema#double', $dataItem );
@@ -585,7 +577,7 @@
586578 /// TODO
587579 return null;
588580 case SMWDataItem::TYPE_CONTAINER:
589 - return self::makeExportData( $dataItem->getSemanticData(), $dataItem->getSubjectPage( $masterPage ) );
 581+ return self::makeExportData( $dataItem->getSemanticData() );
590582 case SMWDataItem::TYPE_WIKIPAGE:
591583 return self::getResourceElementForWikiPage( $dataItem );
592584 case SMWDataItem::TYPE_CONCEPT:
@@ -643,35 +635,4 @@
644636 return ( $dataItemType == SMWDataItem::TYPE_TIME );
645637 }
646638
647 - /**
648 - * Create a dataitem of a wikipage that is used to represent internal
649 - * objects. These objects are used as anonymous placeholders that are
650 - * only defined by their context. In particular, no two distinct
651 - * dataitems for this wiki page should be assumed to represent the same
652 - * object.
653 - *
654 - * @return SMWDIWikiPage
655 - */
656 - static public function getInternalObjectDiPage() {
657 - return new SMWDIWikiPage( 'SMWInternalObject', NS_SPECIAL, '' );
658 - }
659 -
660 - /**
661 - * Check if the given wiki page represents an internal object. See
662 - * SMWExporter::getInternalObjectDiPage() for details.
663 - *
664 - * @see SMWExporter::getInternalObjectDiPage()
665 - * @param $diWikiPage SMWDIWikiPage
666 - * @return boolean
667 - */
668 - static public function isInternalObjectDiPage( SMWDIWikiPage $diWikiPage ) {
669 - if ( $diWikiPage->getNamespace() == NS_SPECIAL &&
670 - $diWikiPage->getDBkey() == 'SMWInternalObject' &&
671 - $diWikiPage->getInterwiki() == '' ) {
672 - return true;
673 - } else {
674 - return false;
675 - }
676 - }
677 -
678639 }
Index: trunk/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php
@@ -117,12 +117,18 @@
118118 * @return boolean
119119 */
120120 protected function loadDataItem( SMWDataItem $dataItem ) {
 121+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_CONTAINER ) {
 122+ // might throw an exception, we just pass it through
 123+ $dataItem = $dataItem->getSemanticData()->getSubject();
 124+ }
 125+
121126 if ( $dataItem->getDIType() == SMWDataItem::TYPE_WIKIPAGE ) {
122127 $this->m_dataitem = $dataItem;
123128 $this->m_textform = str_replace( '_', ' ', $dataItem->getDBkey() );
124129 $this->m_id = -1;
125130 $this->m_title = null;
126 - $this->m_fragment = $this->m_prefixedtext = '';
 131+ $this->m_fragment = $dataItem->getSubobjectName();
 132+ $this->m_prefixedtext = '';
127133 $this->m_caption = false;
128134 if ( ( $this->m_fixNamespace != NS_MAIN ) && ( $this->m_fixNamespace != $dataItem->getNamespace() ) ) {
129135 smwfLoadExtensionMessages( 'SemanticMediaWiki' );
@@ -138,8 +144,7 @@
139145 if ( ( $linked === null ) || ( $linked === false ) || ( $this->m_outformat == '-' ) || ( !$this->isValid() ) || ( $this->m_caption === '' ) ) {
140146 return $this->getCaption();
141147 } else {
142 - return '[[:' . str_replace( "'", '&#x0027;', $this->getPrefixedText() ) .
143 - ( $this->m_fragment ? "#{$this->m_fragment}" : '' ) . '|' . $this->getCaption() . ']]';
 148+ return '[[:' . $this->getWikiLinkTarget() . '|' . $this->getCaption() . ']]';
144149 }
145150 }
146151
@@ -164,14 +169,16 @@
165170 return $this->getErrorText();
166171 }
167172 if ( ( $linked === null ) || ( $linked === false ) || ( $this->m_outformat == '-' ) ) {
168 - return $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText():$this->getText();
 173+ return ( $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText() : $this->getText() ) .
 174+ ( $this->m_fragment ? "#{$this->m_fragment}" : '' );
169175 } elseif ( $this->m_dataitem->getNamespace() == NS_FILE ) {
170176 // For images and other files, embed them and display
171177 // their name, instead of just displaying their name
 178+ // TODO? We forget about subobjecs/fragments here.
172179 $fileName = str_replace( "'", '&#x0027;', $this->getPrefixedText() );
173180 return '[[' . $fileName . '|' . $this->m_textform . '|frameless|border|text-top]]' . "<br />\n" . '[[:' . $fileName . '|' . $this->m_textform . ']]';
174181 } else {
175 - return '[[:' . str_replace( "'", '&#x0027;', $this->getPrefixedText() ) . '|' . $this->m_textform . ']]';
 182+ return '[[' . $this->getWikiLinkTarget() . '|' . $this->m_textform . ']]';
176183 }
177184 }
178185
@@ -191,13 +198,15 @@
192199
193200 public function getWikiValue() {
194201 if ( $this->m_fixNamespace != NS_MAIN ) { // no explicit namespace needed!
195 - return $this->getText();
 202+ $result = $this->getText();
196203 } elseif ( $this->m_dataitem->getNamespace() == NS_CATEGORY ) {
197204 // escape to enable use in links; todo: not generally required/suitable :-/
198 - return ':' . $this->getPrefixedText();
 205+ $result = ':' . $this->getPrefixedText();
199206 } else {
200 - return $this->getPrefixedText();
 207+ $result = $this->getPrefixedText();
201208 }
 209+
 210+ return $result . ( $this->m_fragment ? "#{$this->m_fragment}" : '' );
202211 }
203212
204213 public function getHash() {
@@ -309,10 +318,22 @@
310319 */
311320 protected function getCaption() {
312321 return $this->m_caption !== false ? $this->m_caption :
313 - ( $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText() : $this->getText() );
 322+ ( $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText() : $this->getText() ) .
 323+ ( $this->m_fragment ? "#{$this->m_fragment}" : '' );
314324 }
315325
316326 /**
 327+ * Compute a text that can be used in wiki text to link to this
 328+ * datavalue. Processing includes some escaping and adding the
 329+ * fragment.
 330+ */
 331+ protected function getWikiLinkTarget() {
 332+ return ':' .
 333+ str_replace( "'", '&#x0027;', $this->getPrefixedText() ) .
 334+ ( $this->m_fragment ? "#{$this->m_fragment}" : '' );
 335+ }
 336+
 337+ /**
317338 * Find the sortkey for this object.
318339 *
319340 * @deprecated Use SMWStore::getWikiPageSortKey().
Index: trunk/extensions/SemanticMediaWiki/includes/parserhooks/SMW_Subobject.php
@@ -0,0 +1,72 @@
 2+<?php
 3+
 4+/**
 5+ * Class for the 'subobject' parser functions.
 6+ *
 7+ *
 8+ * @since 1.6.3
 9+ *
 10+ * @file SMW_Subobject.php
 11+ * @ingroup SMW
 12+ * @ingroup ParserHooks
 13+ *
 14+ * @author Markus Krötzsch
 15+ */
 16+class SMWSubobject {
 17+
 18+ /**
 19+ * Method for handling the subobject parser function.
 20+ *
 21+ * @since 1.6.3
 22+ *
 23+ * @param Parser $parser
 24+ */
 25+ public static function render( Parser &$parser ) {
 26+ $params = func_get_args();
 27+ array_shift( $params ); // We already know the $parser ...
 28+
 29+ $semanticData = new SMWContainerSemanticData();
 30+ $propertyName = null;
 31+ foreach ( $params as $param ) {
 32+ if ( is_null( $propertyName ) ) {
 33+ $propertyName = trim( $param );
 34+ } else {
 35+ $parts = explode( '=', trim( $param ), 2 );
 36+
 37+ // Only add the property when there is both a name and a value.
 38+ if ( count( $parts ) == 2 ) {
 39+ self::addPropertyValueToSemanticData( $parts[0], $parts[1], $semanticData );
 40+ }
 41+ }
 42+ }
 43+
 44+ $propertyDv = SMWPropertyValue::makeUserProperty( $propertyName );
 45+ $propertyDi = $propertyDv->getDataItem();
 46+
 47+ if ( !$propertyDi->isInverse() ) {
 48+ try {
 49+ $subObjectDi = new SMWDIContainer( $semanticData );
 50+ SMWParseData::getSMWData( $parser )->addPropertyObjectValue( $propertyDi, $subObjectDi );
 51+ } catch ( SMWDataItemException $e ) {
 52+ SMWParseData::getSMWData( $parser )->addPropertyObjectValue( new SMWDIProperty( '_ERRP' ), $propertyDi->getDiWikiPage() );
 53+ }
 54+ } // else: error reporting for this parser function unclear
 55+
 56+ return '';
 57+ }
 58+
 59+ protected static function addPropertyValueToSemanticData( $propertyName, $valueString, $semanticData ) {
 60+ $propertyDv = SMWPropertyValue::makeUserProperty( $propertyName );
 61+ $propertyDi = $propertyDv->getDataItem();
 62+
 63+ if ( !$propertyDi->isInverse() ) {
 64+ $valueDv = SMWDataValueFactory::newPropertyObjectValue( $propertyDi, $valueString );
 65+ $semanticData->addPropertyObjectValue( $propertyDi, $valueDv->getDataItem() );
 66+ // Take note of the error for storage (do this here and not in storage, thus avoiding duplicates).
 67+ if ( !$valueDv->isValid() ) {
 68+ $semanticData->addPropertyObjectValue( new SMWDIProperty( '_ERRP' ), $propertyDi->getDiWikiPage() );
 69+ }
 70+ } // else: error reporting for this parser function unclear
 71+ }
 72+
 73+}
\ No newline at end of file
Property changes on: trunk/extensions/SemanticMediaWiki/includes/parserhooks/SMW_Subobject.php
___________________________________________________________________
Added: svn:eol-style
174 + native
Index: trunk/extensions/SemanticMediaWiki/includes/SMW_Factbox.php
@@ -82,7 +82,7 @@
8383
8484 foreach ( $propvalues as $dataItem ) {
8585 $dataValue = SMWDataValueFactory::newDataItemValue( $dataItem, $propertyDi );
86 -
 86+
8787 if ( $dataValue->isValid() ) {
8888 $valuesHtml[] = $dataValue->getLongWikiText( true ) . $dataValue->getInfolinkText( SMW_OUTPUT_WIKI );
8989 }
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStoreQueryEngine.php
@@ -812,7 +812,7 @@
813813 } elseif ( $comparator == '=' ) {
814814 $expElement = SMWExporter::getDataItemHelperExpElement( $dataItem );
815815 if ( $expElement === null ) {
816 - $expElement = SMWExporter::getDataItemExpElement( $dataItem, null );
 816+ $expElement = SMWExporter::getDataItemExpElement( $dataItem );
817817 }
818818 $result = new SMWSparqlSingletonCondition( $expElement );
819819 $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty, $dataItem->getDIType() );
@@ -832,11 +832,11 @@
833833 $orderByVariable = $result->orderByVariable;
834834
835835 if ( $dataItem instanceof SMWDIWikiPage ) {
836 - $expElement = SMWExporter::getDataItemExpElement( $dataItem->getSortKeyDataItem(), null );
 836+ $expElement = SMWExporter::getDataItemExpElement( $dataItem->getSortKeyDataItem() );
837837 } else {
838838 $expElement = SMWExporter::getDataItemHelperExpElement( $dataItem );
839839 if ( $expElement === null ) {
840 - $expElement = SMWExporter::getDataItemExpElement( $dataItem, null );
 840+ $expElement = SMWExporter::getDataItemExpElement( $dataItem );
841841 }
842842 }
843843
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php
@@ -471,7 +471,8 @@
472472 $db = wfGetDB( DB_SLAVE );
473473
474474 if ( $value instanceof SMWDIContainer ) { // recursive handling of containers
475 - $joinfield = "t$tableindex." . reset( array_keys( $proptable->objectfields ) ); // this must be a type 'p' object
 475+ $keys = array_keys( $proptable->objectfields );
 476+ $joinfield = "t$tableindex." . reset( $keys ); // this must be a type 'p' object
476477 $proptables = self::getPropertyTables();
477478 $semanticData = $value->getSemanticData();
478479
@@ -832,7 +833,7 @@
833834 }
834835
835836 if ( $di instanceof SMWDIContainer ) { // process subobjects recursively
836 - $subObject = $di->getSubjectPage( $subject );
 837+ $subObject = $di->getSemanticData()->getSubject();
837838 $subObjectId = $this->prepareDBUpdates( $updates, $di->getSemanticData(), 0, $subObject );
838839 // Note: tables for container objects MUST have objectfields == array(<somename> => 'p')
839840 reset( $proptable->objectfields );
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStore.php
@@ -24,7 +24,7 @@
2525
2626 public function deleteSubject( Title $subject ) {
2727 $dataItem = SMWDIWikiPage::newFromTitle( $subject );
28 - $expResource = SMWExporter::getDataItemExpElement( $dataItem, $dataItem );
 28+ $expResource = SMWExporter::getDataItemExpElement( $dataItem );
2929 $this->deleteSparqlData( $expResource );
3030 parent::deleteSubject( $subject );
3131 }
@@ -32,8 +32,8 @@
3333 public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) {
3434 $oldWikiPage = SMWDIWikiPage::newFromTitle( $oldtitle );
3535 $newWikiPage = SMWDIWikiPage::newFromTitle( $newtitle );
36 - $oldExpResource = SMWExporter::getDataItemExpElement( $oldWikiPage, $oldWikiPage );
37 - $newExpResource = SMWExporter::getDataItemExpElement( $newWikiPage, $newWikiPage );
 36+ $oldExpResource = SMWExporter::getDataItemExpElement( $oldWikiPage );
 37+ $newExpResource = SMWExporter::getDataItemExpElement( $newWikiPage );
3838 $namespaces = array( $oldExpResource->getNamespaceId() => $oldExpResource->getNamespace() );
3939 $namespaces[$newExpResource->getNamespaceId()] = $newExpResource->getNamespace();
4040 $oldUri = SMWTurtleSerializer::getTurtleNameForExpElement( $oldExpResource );
@@ -62,7 +62,7 @@
6363 $expDataArray = $this->prepareUpdateExpData( $data );
6464
6565 if ( count( $expDataArray ) > 0 ) {
66 - $subjectResource = SMWExporter::getDataItemExpElement( $data->getSubject(), $data->getSubject() );
 66+ $subjectResource = SMWExporter::getDataItemExpElement( $data->getSubject() );
6767 $this->deleteSparqlData( $subjectResource );
6868
6969 $turtleSerializer = new SMWTurtleSerializer( true );
Index: trunk/extensions/SemanticMediaWiki/includes/SMW_SemanticData.php
@@ -181,8 +181,8 @@
182182 foreach ( $this->getProperties() as $property ) {
183183 hash_update( $ctx, '_#_' . $property->getKey() . '##' );
184184
185 - foreach ( $this->getPropertyValues( $property ) as $dv ) {
186 - hash_update( $ctx, '_#_' . $dv->getSerialization() );
 185+ foreach ( $this->getPropertyValues( $property ) as $di ) {
 186+ hash_update( $ctx, '_#_' . $di->getSerialization() );
187187 }
188188 }
189189
@@ -218,6 +218,8 @@
219219
220220 /**
221221 * Store a value for a property identified by its SMWDataItem object.
 222+ * For container "pseudo" dataitems, this function also sets the master
 223+ * page.
222224 *
223225 * @note There is no check whether the type of the given data item
224226 * agrees with the type of the property. Since property types can
@@ -243,6 +245,10 @@
244246 $this->mPropVals[$property->getKey()][] = $dataItem;
245247 }
246248
 249+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_CONTAINER ) {
 250+ $dataItem->getSemanticData()->setMasterPage( $this->mSubject );
 251+ }
 252+
247253 if ( !$property->isUserDefined() ) {
248254 if ( $property->isShown() ) {
249255 $this->mHasVisibleSpecs = true;
Index: trunk/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php
@@ -95,8 +95,8 @@
9696 */
9797 public function getTitle() {
9898 if ( $this->m_interwiki == '' ) {
99 - return Title::makeTitleSafe( $this->m_namespace, $this->m_dbkey, '' );
100 - } else {
 99+ return Title::makeTitleSafe( $this->m_namespace, $this->m_dbkey, $this->m_subobjectname );
 100+ } else { // TODO inefficient; incomplete for fragments (see above commment)
101101 $datavalue = new SMWWikiPageValue( '_wpg' );
102102 $datavalue->setDataItem( $this );
103103 return Title::newFromText( $datavalue->getPrefixedText() );
@@ -134,7 +134,8 @@
135135 * @return SMWDIWikiPage
136136 */
137137 public static function newFromTitle( Title $title ) {
138 - return new SMWDIWikiPage( $title->getDBkey(), $title->getNamespace(), $title->getInterwiki() );
 138+ return new SMWDIWikiPage( $title->getDBkey(), $title->getNamespace(),
 139+ $title->getInterwiki(), str_replace( ' ', '_', $title->getFragment() ) );
139140 }
140141
141142 }
Index: trunk/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php
@@ -16,23 +16,25 @@
1717 * contain only immutable objects and values of basic types. Arrays are copied
1818 * (lazily) when cloning in PHP, so later changes in the cloce will not affect
1919 * the original.
 20+ *
 21+ * In contrast to normal SMWSemanticData objects, SMWContainerSemanticData can
 22+ * be created without specifying a subject. In this case, the subject is some
 23+ * "anonymous" object that is left unspecified (for search) or generated later
 24+ * (for storage). The method hasAnonymousSubject() should be used to check for
 25+ * this case (as the method getSubject() will always return a valid subject).
 26+ * See the documentation of SMWDIContainer for further details.
2027 */
2128 class SMWContainerSemanticData extends SMWSemanticData {
2229
2330 /**
24 - * If true, the object will not allow further changes.
25 - * @var boolean
26 - */
27 - protected $m_immutable = false;
28 -
29 - /**
30 - * Constructor.
 31+ * Construct a data container that refers to an anonymous subject. See
 32+ * the documenation of the class for details.
3133 *
3234 * @param boolean $noDuplicates stating if duplicate data should be avoided
3335 */
3436 public function __construct( $noDuplicates = true ) {
35 - $subject = SMWExporter::getInternalObjectDiPage();
36 - parent::__construct( $subject, $noDuplicates );
 37+ $subject = new SMWDIWikiPage( 'SMWInternalObject', NS_SPECIAL, '' );
 38+ return parent::__construct( $subject, $noDuplicates );
3739 }
3840
3941 /**
@@ -43,22 +45,37 @@
4446 }
4547
4648 /**
47 - * Clone handler. Make any clone mutable again.
 49+ * Change the subject of this semantic data container so that it is used
 50+ * as a subobject of the given master page.
 51+ *
 52+ * This "contextualizes" the data to belong to a given (master) wiki
 53+ * page. It happens automatically when adding the object as part of a
 54+ * property value to another SMWSemanticData object.
 55+ *
 56+ * @note This method could be extended to allow custom subobject names
 57+ * to be set instead of using the hash. Note, however, that the length
 58+ * of the subobject name in the database is limited in SQLStore2. To
 59+ * make subobjects for sections of a page, this limit would have to be
 60+ * extended.
 61+ *
 62+ * @param SMWDIWikiPage $masterPage
4863 */
49 - public function __clone() {
50 - $this->m_immutable = false;
 64+ public function setMasterPage( SMWDIWikiPage $masterPage ) {
 65+ $subobject = $this->getHash(); // 32 chars: current max length of subobject name in store
 66+ $subobject{0} = '_'; // mark as anonymous subobject; hash is still good
 67+ $this->mSubject = new SMWDIWikiPage( $masterPage->getDBkey(),
 68+ $masterPage->getNamespace(), $masterPage->getInterwiki(),
 69+ $subobject );
 70+ foreach ( $this->getProperties() as $property ) {
 71+ foreach ( $this->getPropertyValues( $property ) as $di ) {
 72+ if ( $di->getDIType() == SMWDataItem::TYPE_CONTAINER ) {
 73+ $di->getSemanticData()->setMasterPage( $this->mSubject );
 74+ }
 75+ }
 76+ }
5177 }
5278
5379 /**
54 - * Freeze the object: no more change operations allowed after calling
55 - * this. Normally this is only called when passing the object to an
56 - * SMWDIContainer. Other code should not need this.
57 - */
58 - public function makeImmutable() {
59 - $this->m_immutable = true;
60 - }
61 -
62 - /**
6380 * Change the object to become an exact copy of the given
6481 * SMWSemanticData object. Useful to convert arbitrary such
6582 * objects into SMWContainerSemanticData objects.
@@ -66,7 +83,6 @@
6784 * @param $semanticData SMWSemanticData
6885 */
6986 public function copyDataFrom( SMWSemanticData $semanticData ) {
70 - $this->throwImmutableException();
7187 $this->mSubject = $semanticData->getSubject();
7288 $this->mProperties = $semanticData->getProperties();
7389 $this->mPropVals = array();
@@ -79,42 +95,61 @@
8096 }
8197
8298 /**
83 - * Store a value for a property identified by its SMWDataItem object,
84 - * if the object was not set to immutable.
85 - *
86 - * @param $property SMWDIProperty
87 - * @param $dataItem SMWDataItem
 99+ * Check if the subject of this container is an anonymous object.
 100+ * See the documenation of the class for details.
 101+ * @return boolean
88102 */
89 - public function addPropertyObjectValue( SMWDIProperty $property, SMWDataItem $dataItem ) {
90 - $this->throwImmutableException();
91 - parent::addPropertyObjectValue( $property, $dataItem );
 103+ public function hasAnonymousSubject() {
 104+ if ( $this->mSubject->getNamespace() == NS_SPECIAL &&
 105+ $this->mSubject->getDBkey() == 'SMWInternalObject' &&
 106+ $this->mSubject->getInterwiki() == '' ) {
 107+ return true;
 108+ } else {
 109+ return false;
 110+ }
92111 }
93112
94113 /**
95 - * Delete all data other than the subject, if the object was not set to
96 - * immutable.
 114+ * Return subject to which the stored semantic annotation refer to, or
 115+ * throw an exception if the subject is anonymous (if the data has not
 116+ * been contextualized with setMasterPage() yet).
 117+ *
 118+ * @return SMWDIWikiPage subject
97119 */
98 - public function clear() {
99 - $this->throwImmutableException();
100 - parent::clear();
 120+ public function getSubject() {
 121+ if ( $this->hasAnonymousSubject() ) {
 122+ throw new SMWDataItemException("Trying to get the subject of a container data item that has not been given any. The method hasAnonymousSubject() can be called to detect this situation.");
 123+ } else {
 124+ return $this->mSubject;
 125+ }
101126 }
102127
103 - /**
104 - * Throw an exception if the object is immutable.
105 - */
106 - protected function throwImmutableException() {
107 - if ( $this->m_immutable ) {
108 - throw new SMWDataItemException( 'Changing the SMWSemanticData object that belongs to a data item of type SMWDIContainer is not allowed. Data items are immutable.' );
109 - }
110 - }
111128 }
112129
113130 /**
114131 * This class implements container data items that can store SMWSemanticData
115 - * objects. In this sense, data items of this type are a kind of "internal
116 - * object" that can contain the data that is otherwise associated with a wiki
117 - * page.
 132+ * objects. Containers are not dataitems in the proper sense: they do not
 133+ * represent a single, opaque value that can be assigned to a property. Rather,
 134+ * a container represents a collection of individual property-value assignments.
 135+ * When a container is stored, these individual data assignments are stored --
 136+ * the data managed by SMW never contains any "container", just individual
 137+ * property assignments. Likewise, when a container is used in search, it is
 138+ * interpreted as a patterns of possible property assignments, and this pattern
 139+ * is searched for.
118140 *
 141+ * The data encapsulated in a container data item is essentially an
 142+ * SMWSemanticData object. The data represented by the container consists of
 143+ * the data stored in this SMWSemanticData object. As a special case, it is
 144+ * possible that the subject of this data is left unspecified. The name of the
 145+ * subject in this case is not part of the data: when storing such containers, a
 146+ * new name will be invented; when searching for such containers, only the
 147+ * property-value pairs are considered relevant in the search. If the subject is
 148+ * given (i.e. not anonymous), and the container DI is used as a property value
 149+ * for a wikipage Foo, then the subject of the container must be a subobject of
 150+ * Foo, for example Foo#section. In this case "Foo" is called the master page of
 151+ * the container. To get a suitable subject for a given master page, the method
 152+ * getSubjectPage() can be used.
 153+ *
119154 * @since 1.6
120155 *
121156 * @author Markus Krötzsch
@@ -137,7 +172,6 @@
138173 */
139174 public function __construct( SMWContainerSemanticData $semanticData ) {
140175 $this->m_semanticData = $semanticData;
141 - $this->m_semanticData->makeImmutable();
142176 }
143177
144178 public function getDIType() {
@@ -166,20 +200,6 @@
167201 }
168202
169203 /**
170 - * Get an internal object that can be used as a subject for this data.
171 - * This subject is not part of the data itself but makes the connection
172 - * to the wiki page for which this data will be stored. This allows to
173 - * encode a kind of provenance information when storing container data,
174 - * useful to find the source of the data, and to retrieve more data when
175 - * a structural (helper) node is found in a query etc.
176 - *
177 - * @return SMWDIWikiPage
178 - */
179 - public function getSubjectPage( SMWDIWikiPage $masterPage ) {
180 - return new SMWDIWikiPage( $masterPage->getDBkey(), $masterPage->getNamespace(), $masterPage->getInterwiki(), '_' . $this->getHash() );
181 - }
182 -
183 - /**
184204 * Create a data item from the provided serialization string and type
185205 * ID.
186206 * @return SMWDIContainer
Index: trunk/extensions/SemanticMediaWiki/includes/SMW_Setup.php
@@ -270,6 +270,7 @@
271271 $wgAutoloadClasses['SMWAsk'] = $phDir . 'SMW_Ask.php';
272272 $wgAutoloadClasses['SMWShow'] = $phDir . 'SMW_Show.php';
273273 $wgAutoloadClasses['SMWInfo'] = $phDir . 'SMW_Info.php';
 274+ $wgAutoloadClasses['SMWSubobject'] = $phDir . 'SMW_Subobject.php';
274275 $wgAutoloadClasses['SMWConcept'] = $phDir . 'SMW_Concept.php';
275276 $wgAutoloadClasses['SMWSet'] = $phDir . 'SMW_Set.php';
276277 $wgAutoloadClasses['SMWSetRecurringEvent'] = $phDir . 'SMW_SetRecurringEvent.php';
@@ -621,6 +622,7 @@
622623 $magicWords['ask'] = array( 0, 'ask' );
623624 $magicWords['show'] = array( 0, 'show' );
624625 $magicWords['info'] = array( 0, 'info' );
 626+ $magicWords['setobject'] = array( 0, 'setobject' );
625627 $magicWords['concept'] = array( 0, 'concept' );
626628 $magicWords['set'] = array( 0, 'set' );
627629 $magicWords['set_recurring_event'] = array( 0, 'set_recurring_event' );
@@ -673,6 +675,7 @@
674676 $parser->setFunctionHook( 'ask', array( 'SMWAsk', 'render' ) );
675677 $parser->setFunctionHook( 'show', array( 'SMWShow', 'render' ) );
676678 $parser->setFunctionHook( 'info', array( 'SMWInfo', 'render' ) );
 679+ $parser->setFunctionHook( 'setobject', array( 'SMWSubobject', 'render' ) );
677680 $parser->setFunctionHook( 'concept', array( 'SMWConcept', 'render' ) );
678681 $parser->setFunctionHook( 'set', array( 'SMWSet', 'render' ) );
679682 $parser->setFunctionHook( 'set_recurring_event', array( 'SMWSetRecurringEvent', 'render' ) );