Index: trunk/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php |
— | — | @@ -120,7 +120,8 @@ |
121 | 121 | $this->post_ns_buffer .= "\t$indent<$type"; |
122 | 122 | } |
123 | 123 | |
124 | | - if ( $expData->getSubject() instanceof SMWExpResource ) { |
| 124 | + if ( ( $expData->getSubject() instanceof SMWExpResource ) && |
| 125 | + !$expData->getSubject()->isBlankNode() ) { |
125 | 126 | $this->post_ns_buffer .= ' rdf:about="' . $expData->getSubject()->getUri() . '"'; |
126 | 127 | } // else: blank node, no "rdf:about" |
127 | 128 | |
— | — | @@ -149,7 +150,7 @@ |
150 | 151 | if ( $collection !== false ) { // RDF-style collection (list) |
151 | 152 | $this->serializeExpCollection( $property, $collection, "\t\t$indent", $isClassTypeProp ); |
152 | 153 | } elseif ( count( $valueElement->getProperties() ) > 0 ) { // resource with data |
153 | | - $this->post_ns_buffer .= "\t\t$indent<" . $expResourceProperty->getQName() . ">\n"; |
| 154 | + $this->post_ns_buffer .= "\t\t$indent<" . $property->getQName() . ">\n"; |
154 | 155 | $this->serializeNestedExpData( $valueElement, "\t\t$indent" ); |
155 | 156 | $this->post_ns_buffer .= "\t\t$indent</" . $property->getQName() . ">\n"; |
156 | 157 | } else { // resource without data |
Index: trunk/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php |
— | — | @@ -76,7 +76,9 @@ |
77 | 77 | */ |
78 | 78 | static public function makeExportDataForSubject( SMWDIWikiPage $diWikiPage, $typesvalueforproperty = null, $addStubData = false ) { |
79 | 79 | global $wgContLang; |
80 | | - $result = new SMWExpData( self::getDataItemExpElement( $diWikiPage ) ); |
| 80 | + $wikiPageExpElement = self::getDataItemExpElement( $diWikiPage ); |
| 81 | + $result = new SMWExpData( $wikiPageExpElement ); |
| 82 | + |
81 | 83 | $pageTitle = str_replace( '_', ' ', $diWikiPage->getDBkey() ); |
82 | 84 | if ( $diWikiPage->getNamespace() !== 0 ) { |
83 | 85 | $prefixedSubjectTitle = $wgContLang->getNsText( $diWikiPage->getNamespace()) . ":" . $pageTitle; |
— | — | @@ -84,6 +86,7 @@ |
85 | 87 | $prefixedSubjectTitle = $pageTitle; |
86 | 88 | } |
87 | 89 | $prefixedSubjectUrl = wfUrlencode( str_replace( ' ', '_', $prefixedSubjectTitle ) ); |
| 90 | + |
88 | 91 | switch ( $diWikiPage->getNamespace() ) { |
89 | 92 | case NS_CATEGORY: case SMW_NS_CONCEPT: |
90 | 93 | $maintype_pe = self::getSpecialNsResource( 'owl', 'Class' ); |
— | — | @@ -101,19 +104,24 @@ |
102 | 105 | $label = $prefixedSubjectTitle; |
103 | 106 | $maintype_pe = self::getSpecialNsResource( 'swivt', 'Subject' ); |
104 | 107 | } |
105 | | - $ed = new SMWExpLiteral( $label ); |
106 | | - $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'label' ), $ed ); |
107 | | - $ed = new SMWExpResource( self::getNamespaceUri( 'wikiurl' ) . $prefixedSubjectUrl ); |
108 | | - $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'page' ), $ed ); |
109 | | - $ed = new SMWExpResource( self::$m_exporturl . '/' . $prefixedSubjectUrl ); |
110 | | - $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed ); |
| 108 | + |
111 | 109 | $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), $maintype_pe ); |
112 | | - $ed = new SMWExpLiteral( $diWikiPage->getNamespace(), 'http://www.w3.org/2001/XMLSchema#integer' ); |
113 | | - $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'wikiNamespace' ), $ed ); |
114 | | - if ( $addStubData ) { |
115 | | - $defaultSortkey = new SMWExpLiteral( str_replace( '_', ' ', $diWikiPage->getDBkey() ) ); |
116 | | - $result->addPropertyObjectValue( self::getSpecialPropertyResource( '_SKEY' ), $defaultSortkey ); |
| 110 | + |
| 111 | + if ( !$wikiPageExpElement->isBlankNode() ) { |
| 112 | + $ed = new SMWExpLiteral( $label ); |
| 113 | + $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'label' ), $ed ); |
| 114 | + $ed = new SMWExpResource( self::getNamespaceUri( 'wikiurl' ) . $prefixedSubjectUrl ); |
| 115 | + $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'page' ), $ed ); |
| 116 | + $ed = new SMWExpResource( self::$m_exporturl . '/' . $prefixedSubjectUrl ); |
| 117 | + $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed ); |
| 118 | + $ed = new SMWExpLiteral( $diWikiPage->getNamespace(), 'http://www.w3.org/2001/XMLSchema#integer' ); |
| 119 | + $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'wikiNamespace' ), $ed ); |
| 120 | + if ( $addStubData ) { |
| 121 | + $defaultSortkey = new SMWExpLiteral( str_replace( '_', ' ', $diWikiPage->getDBkey() ) ); |
| 122 | + $result->addPropertyObjectValue( self::getSpecialPropertyResource( '_SKEY' ), $defaultSortkey ); |
| 123 | + } |
117 | 124 | } |
| 125 | + |
118 | 126 | return $result; |
119 | 127 | } |
120 | 128 | |
— | — | @@ -248,6 +256,9 @@ |
249 | 257 | $namespace = $importValue->getNS(); |
250 | 258 | $namespaceId = $importValue->getNSID(); |
251 | 259 | $localName = $importValue->getLocalName(); |
| 260 | + } elseif ( self::isInternalObjectDiPage( $diWikiPage ) ) { // blank node |
| 261 | + $localName = $namespace = $namespaceId = ''; |
| 262 | + $diWikiPage = null; // do not associate any wiki page with blank nodes |
252 | 263 | } else { |
253 | 264 | $localName = ''; |
254 | 265 | if ( $diWikiPage->getNamespace() == SMW_NS_PROPERTY ) { |
— | — | @@ -256,7 +267,7 @@ |
257 | 268 | $localName = self::encodeURI( rawurlencode( $diWikiPage->getDBkey() ) ); |
258 | 269 | } |
259 | 270 | if ( ( $localName == '' ) || |
260 | | - ( in_array( $localName[0], array( '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) ) ) ) { |
| 271 | + ( in_array( $localName{0}, array( '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) ) ) ) { |
261 | 272 | $namespace = self::getNamespaceUri( 'wiki' ); |
262 | 273 | $namespaceId = 'wiki'; |
263 | 274 | if ( $diWikiPage->getNamespace() !== 0 ) { |
— | — | @@ -547,8 +558,7 @@ |
548 | 559 | /// TODO |
549 | 560 | return null; |
550 | 561 | case SMWDataItem::TYPE_CONTAINER: |
551 | | - /// TODO |
552 | | - return null; |
| 562 | + return self::makeExportData( $dataItem->getSemanticData() ); |
553 | 563 | case SMWDataItem::TYPE_WIKIPAGE: |
554 | 564 | return self::getResourceElementForWikiPage( $dataItem ); |
555 | 565 | case SMWDataItem::TYPE_CONCEPT: |
— | — | @@ -606,4 +616,35 @@ |
607 | 617 | return ( $dataItemType == SMWDataItem::TYPE_TIME ); |
608 | 618 | } |
609 | 619 | |
| 620 | + /** |
| 621 | + * Create a dataitem of a wikipage that is used to represent internal |
| 622 | + * objects. These objects are used as anonymous placeholders that are |
| 623 | + * only defined by their context. In particular, no two distinct |
| 624 | + * dataitems for this wiki page should be assumed to represent the same |
| 625 | + * object. |
| 626 | + * |
| 627 | + * @return SMWDIWikiPage |
| 628 | + */ |
| 629 | + static public function getInternalObjectDiPage() { |
| 630 | + return new SMWDIWikiPage( 'SMWInternalObject', NS_SPECIAL, '' ); |
| 631 | + } |
| 632 | + |
| 633 | + /** |
| 634 | + * Check if the given wiki page represents an internal object. See |
| 635 | + * SMWExporter::getInternalObjectDiPage() for details. |
| 636 | + * |
| 637 | + * @see SMWExporter::getInternalObjectDiPage() |
| 638 | + * @param $diWikiPage SMWDIWikiPage |
| 639 | + * @return boolean |
| 640 | + */ |
| 641 | + static public function isInternalObjectDiPage( SMWDIWikiPage $diWikiPage ) { |
| 642 | + if ( $diWikiPage->getNamespace() == NS_SPECIAL && |
| 643 | + $diWikiPage->getDBkey() == 'SMWInternalObject' && |
| 644 | + $diWikiPage->getInterwiki() == '' ) { |
| 645 | + return true; |
| 646 | + } else { |
| 647 | + return false; |
| 648 | + } |
| 649 | + } |
| 650 | + |
610 | 651 | } |
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStore.php |
— | — | @@ -58,61 +58,148 @@ |
59 | 59 | |
60 | 60 | public function doDataUpdate( SMWSemanticData $data ) { |
61 | 61 | parent::doDataUpdate( $data ); |
62 | | - $subjectResource = SMWExporter::getDataItemExpElement( $data->getSubject() ); |
63 | | - $this->deleteSparqlData( $subjectResource ); |
64 | 62 | |
65 | 63 | $expDataArray = $this->prepareUpdateExpData( $data ); |
66 | 64 | |
67 | | - $turtleSerializer = new SMWTurtleSerializer( true ); |
68 | | - $turtleSerializer->startSerialization(); |
69 | | - foreach ( $expDataArray as $expData ) { |
70 | | - $turtleSerializer->serializeExpData( $expData ); |
| 65 | + if ( count( $expDataArray ) > 0 ) { |
| 66 | + $subjectResource = SMWExporter::getDataItemExpElement( $data->getSubject() ); |
| 67 | + $this->deleteSparqlData( $subjectResource ); |
| 68 | + |
| 69 | + $turtleSerializer = new SMWTurtleSerializer( true ); |
| 70 | + $turtleSerializer->startSerialization(); |
| 71 | + foreach ( $expDataArray as $expData ) { |
| 72 | + $turtleSerializer->serializeExpData( $expData ); |
| 73 | + } |
| 74 | + $turtleSerializer->finishSerialization(); |
| 75 | + $triples = $turtleSerializer->flushContent(); |
| 76 | + $prefixes = $turtleSerializer->flushSparqlPrefixes(); |
| 77 | + |
| 78 | + smwfGetSparqlDatabase()->insertData( $triples, $prefixes ); |
71 | 79 | } |
72 | | - $turtleSerializer->finishSerialization(); |
73 | | - $triples = $turtleSerializer->flushContent(); |
74 | | - $prefixes = $turtleSerializer->flushSparqlPrefixes(); |
75 | | - |
76 | | - smwfGetSparqlDatabase()->insertData( $triples, $prefixes ); |
77 | 80 | } |
78 | 81 | |
79 | 82 | /** |
80 | 83 | * Prepare an array of SMWExpData elements that should be written to |
81 | | - * the SPARQL store, or return null if no update should be performed. |
| 84 | + * the SPARQL store. The result is empty if no updates should be done. |
| 85 | + * Note that this is different from writing an SMWExpData element that |
| 86 | + * has no content. |
82 | 87 | * Otherwise, the first SMWExpData object in the array is a translation |
83 | | - * of the given input data, but with redirects resolved. |
| 88 | + * of the given input data, but with redirects resolved. Further |
| 89 | + * SMWExpData objects might be included in the resulting list to |
| 90 | + * capture necessary stub declarations for objects that do not have |
| 91 | + * any data in the RDF store yet. |
84 | 92 | * |
85 | 93 | * @param $data SMWSemanticData object containing the update data |
| 94 | + * @return array of SMWExpData |
86 | 95 | */ |
87 | 96 | protected function prepareUpdateExpData( SMWSemanticData $data ) { |
88 | 97 | $expData = SMWExporter::makeExportData( $data ); |
89 | 98 | $result = array(); |
90 | | - $newExpData = new SMWExpData( $expData->getSubject() ); |
91 | | - $exists = false; |
| 99 | + $newExpData = $this->expandUpdateExpData( $expData, $expData->getSubject(), $result, false ); |
| 100 | + array_unshift( $result, $newExpData ); |
| 101 | + return $result; |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * Find a normalized representation of the given SMWExpElement that can |
| 106 | + * be used in an update of the stored data. Normalization uses |
| 107 | + * redirects. The type of the ExpElement might change, especially into |
| 108 | + * SMWExpData in order to store auxiliary properties. |
| 109 | + * Moreover, the method records any auxiliary data that should be |
| 110 | + * written to the store when including this SMWExpElement into updates. |
| 111 | + * This auxiliary data is collected in a call-by-ref array. |
| 112 | + * |
| 113 | + * @param $expElement SMWExpElement object containing the update data |
| 114 | + * @param $masterExpElement SMWExpResource to which encountered blank nodes will be associated |
| 115 | + * @param $auxiliaryExpData array of SMWExpData |
| 116 | + * @return SMWExpElement |
| 117 | + */ |
| 118 | + protected function expandUpdateExpElement( SMWExpElement $expElement, SMWExpResource $masterExpElement, array &$auxiliaryExpData ) { |
| 119 | + if ( $expElement instanceof SMWExpResource ) { |
| 120 | + $elementTarget = $this->expandUpdateExpResource( $expElement, $masterExpElement, $auxiliaryExpData ); |
| 121 | + } elseif ( $expElement instanceof SMWExpData ) { |
| 122 | + $elementTarget = $this->expandUpdateExpData( $expElement, $masterExpElement, $auxiliaryExpData, true ); |
| 123 | + } else { |
| 124 | + $elementTarget = $expElement; |
| 125 | + } |
| 126 | + |
| 127 | + return $elementTarget; |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Find a normalized representation of the given SMWExpResource that can |
| 132 | + * be used in an update of the stored data. Normalization uses |
| 133 | + * redirects. The type of the ExpElement might change, especially into |
| 134 | + * SMWExpData in order to store auxiliary properties. |
| 135 | + * Moreover, the method records any auxiliary data that should be |
| 136 | + * written to the store when including this SMWExpElement into updates. |
| 137 | + * This auxiliary data is collected in a call-by-ref array. |
| 138 | + * |
| 139 | + * @param $expResource SMWExpResource object containing the update data |
| 140 | + * @param $masterExpElement SMWExpResource to which encountered blank nodes will be associated |
| 141 | + * @param $auxiliaryExpData array of SMWExpData |
| 142 | + * @return SMWExpElement |
| 143 | + */ |
| 144 | + protected function expandUpdateExpResource( SMWExpResource $expResource, SMWExpResource $masterExpElement, array &$auxiliaryExpData ) { |
| 145 | + $exists = true; |
| 146 | + if ( $expResource instanceof SMWExpNsResource ) { |
| 147 | + $elementTarget = $this->getSparqlRedirectTarget( $expResource, $exists ); |
| 148 | + } else { |
| 149 | + $elementTarget = $expResource; |
| 150 | + } |
| 151 | + |
| 152 | + if ( $elementTarget->isBlankNode() ) { |
| 153 | + $auxExpData = new SMWExpData( $elementTarget ); |
| 154 | + $masterResourceProperty = SMWExporter::getSpecialNsResource( 'swivt', 'masterResource' ); |
| 155 | + $auxExpData->addPropertyObjectValue( $masterResourceProperty, $masterExpElement ); |
| 156 | + $elementTarget = $auxExpData; |
| 157 | + } elseif ( !$exists && ( $elementTarget->getDataItem() instanceof SMWDIWikiPage ) ) { |
| 158 | + $diWikiPage = $elementTarget->getDataItem(); |
| 159 | + $hash = $diWikiPage->getHash(); |
| 160 | + if ( !array_key_exists( $hash, $auxiliaryExpData ) ) { |
| 161 | + $auxiliaryExpData[$hash] = SMWExporter::makeExportDataForSubject( $diWikiPage, null, true ); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + return $elementTarget; |
| 166 | + } |
| 167 | + |
| 168 | + /** |
| 169 | + * Find a normalized representation of the given SMWExpData that can |
| 170 | + * be used in an update of the stored data. Normalization uses |
| 171 | + * redirects. |
| 172 | + * Moreover, the method records any auxiliary data that should be |
| 173 | + * written to the store when including this SMWExpElement into updates. |
| 174 | + * This auxiliary data is collected in a call-by-ref array. |
| 175 | + * |
| 176 | + * @param $expData SMWExpData object containing the update data |
| 177 | + * @param $masterExpElement SMWExpResource to which encountered blank nodes will be associated |
| 178 | + * @param $auxiliaryExpData array of SMWExpData |
| 179 | + * @param $expandSubject boolean controls if redirects/auxiliary data should also be sought for subject |
| 180 | + * @return SMWExpData |
| 181 | + */ |
| 182 | + protected function expandUpdateExpData( SMWExpData $expData, SMWExpResource $masterExpElement, array &$auxiliaryExpData, $expandSubject ) { |
| 183 | + $subjectExpResource = $expData->getSubject(); |
| 184 | + if ( $expandSubject ) { |
| 185 | + $expandedExpElement = $this->expandUpdateExpElement( $subjectExpResource, $masterExpElement, $auxiliaryExpData ); |
| 186 | + if ( $expandedExpElement instanceof SMWExpData ) { |
| 187 | + $newExpData = $expandedExpElement; |
| 188 | + } else { // instanceof SMWExpResource |
| 189 | + $newExpData = new SMWExpData( $subjectExpResource ); |
| 190 | + } |
| 191 | + } else { |
| 192 | + $newExpData = new SMWExpData( $subjectExpResource ); |
| 193 | + } |
| 194 | + |
92 | 195 | foreach ( $expData->getProperties() as $propertyResource ) { |
93 | | - $propertyTarget = $this->getSparqlRedirectTarget( $propertyResource, $exists ); |
94 | | - if ( !$exists && ( $propertyResource->getDataItem() instanceof SMWDIWikiPage ) ) { |
95 | | - $diWikiPage = $propertyResource->getDataItem(); |
96 | | - if ( ( $diWikiPage !== null ) && !array_key_exists( $diWikiPage->gethash(), $result ) ) { |
97 | | - $result[$diWikiPage->gethash()] = SMWExporter::makeExportDataForSubject( $diWikiPage, null, true ); |
98 | | - } |
99 | | - } |
| 196 | + $propertyTarget = $this->expandUpdateExpElement( $propertyResource, $masterExpElement, $auxiliaryExpData ); |
100 | 197 | foreach ( $expData->getValues( $propertyResource ) as $expElement ) { |
101 | | - if ( $expElement instanceof SMWExpNsResource ) { |
102 | | - $elementTarget = $this->getSparqlRedirectTarget( $expElement, $exists ); |
103 | | - if ( !$exists && ( $expElement->getDataItem() instanceof SMWDIWikiPage ) ) { |
104 | | - $diWikiPage = $expElement->getDataItem(); |
105 | | - if ( ( $diWikiPage !== null ) && !array_key_exists( $diWikiPage->gethash(), $result ) ) { |
106 | | - $result[$diWikiPage->gethash()] = SMWExporter::makeExportDataForSubject( $diWikiPage, null, true ); |
107 | | - } |
108 | | - } |
109 | | - } else { |
110 | | - $elementTarget = $expElement; |
111 | | - } |
| 198 | + $elementTarget = $this->expandUpdateExpElement( $expElement, $masterExpElement, $auxiliaryExpData ); |
112 | 199 | $newExpData->addPropertyObjectValue( $propertyTarget, $elementTarget ); |
113 | 200 | } |
114 | 201 | } |
115 | | - $result[] = $newExpData; |
116 | | - return $result; |
| 202 | + |
| 203 | + return $newExpData; |
117 | 204 | } |
118 | 205 | |
119 | 206 | /** |
— | — | @@ -122,10 +209,16 @@ |
123 | 210 | * used for making a resource with a prefix). |
124 | 211 | * |
125 | 212 | * @param $expNsResource string URI to check |
126 | | - * @param $exists boolean that is set to true if $expNsResource is in the store |
| 213 | + * @param $exists boolean that is set to true if $expNsResource is in the |
| 214 | + * store; always false for blank nodes |
127 | 215 | * @return SMWExpNsResource |
128 | 216 | */ |
129 | 217 | protected function getSparqlRedirectTarget( SMWExpNsResource $expNsResource, &$exists ) { |
| 218 | + if ( $expNsResource->isBlankNode() ) { |
| 219 | + $exists = false; |
| 220 | + return $expNsResource; |
| 221 | + } |
| 222 | + |
130 | 223 | $resourceUri = SMWTurtleSerializer::getTurtleNameForExpElement( $expNsResource ); |
131 | 224 | $rediUri = SMWTurtleSerializer::getTurtleNameForExpElement( SMWExporter::getSpecialPropertyResource( '_REDI' ) ); |
132 | 225 | $skeyUri = SMWTurtleSerializer::getTurtleNameForExpElement( SMWExporter::getSpecialPropertyResource( '_SKEY' ) ); |
— | — | @@ -143,7 +236,7 @@ |
144 | 237 | $exists = true; |
145 | 238 | $rediTargetElement = $firstRow[1]; |
146 | 239 | $rediTargetUri = $rediTargetElement->getUri(); |
147 | | - $wikiNamespace = Exporter::expandUri( '$wiki;' ); |
| 240 | + $wikiNamespace = Exporter::getNamespaceUri( 'wiki' ); |
148 | 241 | if ( strpos( $rediTargetUri, $wikiNamespace ) === 0 ) { |
149 | 242 | return new SMWExpNsResource( substr( $rediTargetUri, 0, strlen( $wikiNamespace ) ) , $wikiNamespace, 'wiki' ); |
150 | 243 | } else { |
Index: trunk/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php |
— | — | @@ -31,7 +31,7 @@ |
32 | 32 | * @param boolean $noDuplicates stating if duplicate data should be avoided |
33 | 33 | */ |
34 | 34 | public function __construct( $noDuplicates = true ) { |
35 | | - $subject = new SMWDIWikiPage( 'SMWInternalObject', NS_SPECIAL, '' ); // dummy subject |
| 35 | + $subject = SMWExporter::getInternalObjectDiPage(); |
36 | 36 | parent::__construct( $subject, $noDuplicates ); |
37 | 37 | } |
38 | 38 | |