|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* |
|
7 |
|
8 An implementation for an NGLayout-style content sink that knows how |
|
9 to build an RDF content model from XML-serialized RDF. |
|
10 |
|
11 For more information on the RDF/XML syntax, |
|
12 see http://www.w3.org/TR/REC-rdf-syntax/ |
|
13 |
|
14 This code is based on the final W3C Recommendation, |
|
15 http://www.w3.org/TR/1999/REC-rdf-syntax-19990222. |
|
16 |
|
17 Open Issues ------------------ |
|
18 |
|
19 1) factoring code with nsXMLContentSink - There's some amount of |
|
20 common code between this and the HTML content sink. This will |
|
21 increase as we support more and more HTML elements. How can code |
|
22 from XML/HTML be factored? |
|
23 |
|
24 2) We don't support the `parseType' attribute on the Description |
|
25 tag; therefore, it is impossible to "inline" raw XML in this |
|
26 implemenation. |
|
27 |
|
28 3) We don't build the reifications at parse time due to the |
|
29 footprint overhead it would incur for large RDF documents. (It |
|
30 may be possible to attach a "reification" wrapper datasource that |
|
31 would present this information at query-time.) Because of this, |
|
32 the `bagID' attribute is not processed correctly. |
|
33 |
|
34 4) No attempt is made to `resolve URIs' to a canonical form (the |
|
35 specification hints that an implementation should do this). This |
|
36 is omitted for the obvious reason that we can ill afford to |
|
37 resolve each URI reference. |
|
38 |
|
39 */ |
|
40 |
|
41 #include "nsCOMPtr.h" |
|
42 #include "nsInterfaceHashtable.h" |
|
43 #include "nsIContentSink.h" |
|
44 #include "nsIRDFContainer.h" |
|
45 #include "nsIRDFContainerUtils.h" |
|
46 #include "nsIRDFContentSink.h" |
|
47 #include "nsIRDFNode.h" |
|
48 #include "nsIRDFService.h" |
|
49 #include "nsIRDFXMLSink.h" |
|
50 #include "nsIServiceManager.h" |
|
51 #include "nsIURL.h" |
|
52 #include "nsIXMLContentSink.h" |
|
53 #include "nsRDFCID.h" |
|
54 #include "nsTArray.h" |
|
55 #include "nsXPIDLString.h" |
|
56 #include "prlog.h" |
|
57 #include "rdf.h" |
|
58 #include "rdfutil.h" |
|
59 #include "nsReadableUtils.h" |
|
60 #include "nsIExpatSink.h" |
|
61 #include "nsCRT.h" |
|
62 #include "nsIAtom.h" |
|
63 #include "nsStaticAtom.h" |
|
64 #include "nsIScriptError.h" |
|
65 #include "nsIDTD.h" |
|
66 |
|
67 using namespace mozilla; |
|
68 |
|
69 /////////////////////////////////////////////////////////////////////// |
|
70 |
|
71 enum RDFContentSinkState { |
|
72 eRDFContentSinkState_InProlog, |
|
73 eRDFContentSinkState_InDocumentElement, |
|
74 eRDFContentSinkState_InDescriptionElement, |
|
75 eRDFContentSinkState_InContainerElement, |
|
76 eRDFContentSinkState_InPropertyElement, |
|
77 eRDFContentSinkState_InMemberElement, |
|
78 eRDFContentSinkState_InEpilog |
|
79 }; |
|
80 |
|
81 enum RDFContentSinkParseMode { |
|
82 eRDFContentSinkParseMode_Resource, |
|
83 eRDFContentSinkParseMode_Literal, |
|
84 eRDFContentSinkParseMode_Int, |
|
85 eRDFContentSinkParseMode_Date |
|
86 }; |
|
87 |
|
88 typedef |
|
89 NS_STDCALL_FUNCPROTO(nsresult, |
|
90 nsContainerTestFn, |
|
91 nsIRDFContainerUtils, IsAlt, |
|
92 (nsIRDFDataSource*, nsIRDFResource*, bool*)); |
|
93 |
|
94 typedef |
|
95 NS_STDCALL_FUNCPROTO(nsresult, |
|
96 nsMakeContainerFn, |
|
97 nsIRDFContainerUtils, MakeAlt, |
|
98 (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**)); |
|
99 |
|
100 class RDFContentSinkImpl : public nsIRDFContentSink, |
|
101 public nsIExpatSink |
|
102 { |
|
103 public: |
|
104 RDFContentSinkImpl(); |
|
105 virtual ~RDFContentSinkImpl(); |
|
106 |
|
107 // nsISupports |
|
108 NS_DECL_ISUPPORTS |
|
109 NS_DECL_NSIEXPATSINK |
|
110 |
|
111 // nsIContentSink |
|
112 NS_IMETHOD WillParse(void); |
|
113 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode); |
|
114 NS_IMETHOD DidBuildModel(bool aTerminated); |
|
115 NS_IMETHOD WillInterrupt(void); |
|
116 NS_IMETHOD WillResume(void); |
|
117 NS_IMETHOD SetParser(nsParserBase* aParser); |
|
118 virtual void FlushPendingNotifications(mozFlushType aType) { } |
|
119 NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; } |
|
120 virtual nsISupports *GetTarget() { return nullptr; } |
|
121 |
|
122 // nsIRDFContentSink |
|
123 NS_IMETHOD Init(nsIURI* aURL); |
|
124 NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource); |
|
125 NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource); |
|
126 |
|
127 // pseudo constants |
|
128 static int32_t gRefCnt; |
|
129 static nsIRDFService* gRDFService; |
|
130 static nsIRDFContainerUtils* gRDFContainerUtils; |
|
131 static nsIRDFResource* kRDF_type; |
|
132 static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type |
|
133 static nsIRDFResource* kRDF_Alt; |
|
134 static nsIRDFResource* kRDF_Bag; |
|
135 static nsIRDFResource* kRDF_Seq; |
|
136 static nsIRDFResource* kRDF_nextVal; |
|
137 |
|
138 #define RDF_ATOM(name_, value_) static nsIAtom* name_; |
|
139 #include "nsRDFContentSinkAtomList.h" |
|
140 #undef RDF_ATOM |
|
141 |
|
142 typedef struct ContainerInfo { |
|
143 nsIRDFResource** mType; |
|
144 nsContainerTestFn mTestFn; |
|
145 nsMakeContainerFn mMakeFn; |
|
146 } ContainerInfo; |
|
147 |
|
148 protected: |
|
149 // Text management |
|
150 void ParseText(nsIRDFNode **aResult); |
|
151 |
|
152 nsresult FlushText(); |
|
153 nsresult AddText(const char16_t* aText, int32_t aLength); |
|
154 |
|
155 // RDF-specific parsing |
|
156 nsresult OpenRDF(const char16_t* aName); |
|
157 nsresult OpenObject(const char16_t* aName ,const char16_t** aAttributes); |
|
158 nsresult OpenProperty(const char16_t* aName, const char16_t** aAttributes); |
|
159 nsresult OpenMember(const char16_t* aName, const char16_t** aAttributes); |
|
160 nsresult OpenValue(const char16_t* aName, const char16_t** aAttributes); |
|
161 |
|
162 nsresult GetIdAboutAttribute(const char16_t** aAttributes, nsIRDFResource** aResource, bool* aIsAnonymous = nullptr); |
|
163 nsresult GetResourceAttribute(const char16_t** aAttributes, nsIRDFResource** aResource); |
|
164 nsresult AddProperties(const char16_t** aAttributes, nsIRDFResource* aSubject, int32_t* aCount = nullptr); |
|
165 void SetParseMode(const char16_t **aAttributes); |
|
166 |
|
167 char16_t* mText; |
|
168 int32_t mTextLength; |
|
169 int32_t mTextSize; |
|
170 |
|
171 /** |
|
172 * From the set of given attributes, this method extracts the |
|
173 * namespace definitions and feeds them to the datasource. |
|
174 * These can then be suggested to the serializer to be used again. |
|
175 * Hopefully, this will keep namespace definitions intact in a |
|
176 * parse - serialize cycle. |
|
177 */ |
|
178 void RegisterNamespaces(const char16_t **aAttributes); |
|
179 |
|
180 /** |
|
181 * Extracts the localname from aExpatName, the name that the Expat parser |
|
182 * passes us. |
|
183 * aLocalName will contain the localname in aExpatName. |
|
184 * The return value is a dependent string containing just the namespace. |
|
185 */ |
|
186 const nsDependentSubstring SplitExpatName(const char16_t *aExpatName, |
|
187 nsIAtom **aLocalName); |
|
188 |
|
189 enum eContainerType { eBag, eSeq, eAlt }; |
|
190 nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer); |
|
191 nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer); |
|
192 |
|
193 // The datasource in which we're assigning assertions |
|
194 nsCOMPtr<nsIRDFDataSource> mDataSource; |
|
195 |
|
196 // A hash of all the node IDs referred to |
|
197 nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap; |
|
198 |
|
199 // The current state of the content sink |
|
200 RDFContentSinkState mState; |
|
201 RDFContentSinkParseMode mParseMode; |
|
202 |
|
203 // content stack management |
|
204 int32_t |
|
205 PushContext(nsIRDFResource *aContext, |
|
206 RDFContentSinkState aState, |
|
207 RDFContentSinkParseMode aParseMode); |
|
208 |
|
209 nsresult |
|
210 PopContext(nsIRDFResource *&aContext, |
|
211 RDFContentSinkState &aState, |
|
212 RDFContentSinkParseMode &aParseMode); |
|
213 |
|
214 nsIRDFResource* GetContextElement(int32_t ancestor = 0); |
|
215 |
|
216 |
|
217 struct RDFContextStackElement { |
|
218 nsCOMPtr<nsIRDFResource> mResource; |
|
219 RDFContentSinkState mState; |
|
220 RDFContentSinkParseMode mParseMode; |
|
221 }; |
|
222 |
|
223 nsAutoTArray<RDFContextStackElement, 8>* mContextStack; |
|
224 |
|
225 nsIURI* mDocumentURL; |
|
226 |
|
227 private: |
|
228 #ifdef PR_LOGGING |
|
229 static PRLogModuleInfo* gLog; |
|
230 #endif |
|
231 }; |
|
232 |
|
233 int32_t RDFContentSinkImpl::gRefCnt = 0; |
|
234 nsIRDFService* RDFContentSinkImpl::gRDFService; |
|
235 nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils; |
|
236 nsIRDFResource* RDFContentSinkImpl::kRDF_type; |
|
237 nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf; |
|
238 nsIRDFResource* RDFContentSinkImpl::kRDF_Alt; |
|
239 nsIRDFResource* RDFContentSinkImpl::kRDF_Bag; |
|
240 nsIRDFResource* RDFContentSinkImpl::kRDF_Seq; |
|
241 nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal; |
|
242 |
|
243 #ifdef PR_LOGGING |
|
244 PRLogModuleInfo* RDFContentSinkImpl::gLog; |
|
245 #endif |
|
246 |
|
247 //////////////////////////////////////////////////////////////////////// |
|
248 |
|
249 #define RDF_ATOM(name_, value_) nsIAtom* RDFContentSinkImpl::name_; |
|
250 #include "nsRDFContentSinkAtomList.h" |
|
251 #undef RDF_ATOM |
|
252 |
|
253 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_) |
|
254 #include "nsRDFContentSinkAtomList.h" |
|
255 #undef RDF_ATOM |
|
256 |
|
257 static const nsStaticAtom rdf_atoms[] = { |
|
258 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &RDFContentSinkImpl::name_), |
|
259 #include "nsRDFContentSinkAtomList.h" |
|
260 #undef RDF_ATOM |
|
261 }; |
|
262 |
|
263 RDFContentSinkImpl::RDFContentSinkImpl() |
|
264 : mText(nullptr), |
|
265 mTextLength(0), |
|
266 mTextSize(0), |
|
267 mState(eRDFContentSinkState_InProlog), |
|
268 mParseMode(eRDFContentSinkParseMode_Literal), |
|
269 mContextStack(nullptr), |
|
270 mDocumentURL(nullptr) |
|
271 { |
|
272 if (gRefCnt++ == 0) { |
|
273 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); |
|
274 nsresult rv = CallGetService(kRDFServiceCID, &gRDFService); |
|
275 |
|
276 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service"); |
|
277 if (NS_SUCCEEDED(rv)) { |
|
278 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), |
|
279 &kRDF_type); |
|
280 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"), |
|
281 &kRDF_instanceOf); |
|
282 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"), |
|
283 &kRDF_Alt); |
|
284 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"), |
|
285 &kRDF_Bag); |
|
286 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"), |
|
287 &kRDF_Seq); |
|
288 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"), |
|
289 &kRDF_nextVal); |
|
290 } |
|
291 |
|
292 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); |
|
293 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); |
|
294 |
|
295 NS_RegisterStaticAtoms(rdf_atoms); |
|
296 } |
|
297 |
|
298 #ifdef PR_LOGGING |
|
299 if (! gLog) |
|
300 gLog = PR_NewLogModule("nsRDFContentSink"); |
|
301 #endif |
|
302 } |
|
303 |
|
304 |
|
305 RDFContentSinkImpl::~RDFContentSinkImpl() |
|
306 { |
|
307 #ifdef DEBUG_REFS |
|
308 --gInstanceCount; |
|
309 fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount); |
|
310 #endif |
|
311 |
|
312 NS_IF_RELEASE(mDocumentURL); |
|
313 |
|
314 if (mContextStack) { |
|
315 PR_LOG(gLog, PR_LOG_WARNING, |
|
316 ("rdfxml: warning! unclosed tag")); |
|
317 |
|
318 // XXX we should never need to do this, but, we'll write the |
|
319 // code all the same. If someone left the content stack dirty, |
|
320 // pop all the elements off the stack and release them. |
|
321 int32_t i = mContextStack->Length(); |
|
322 while (0 < i--) { |
|
323 nsIRDFResource* resource = nullptr; |
|
324 RDFContentSinkState state; |
|
325 RDFContentSinkParseMode parseMode; |
|
326 PopContext(resource, state, parseMode); |
|
327 |
|
328 #ifdef PR_LOGGING |
|
329 // print some fairly useless debugging info |
|
330 // XXX we should save line numbers on the context stack: this'd |
|
331 // be about 1000x more helpful. |
|
332 if (resource) { |
|
333 nsXPIDLCString uri; |
|
334 resource->GetValue(getter_Copies(uri)); |
|
335 PR_LOG(gLog, PR_LOG_NOTICE, |
|
336 ("rdfxml: uri=%s", (const char*) uri)); |
|
337 } |
|
338 #endif |
|
339 |
|
340 NS_IF_RELEASE(resource); |
|
341 } |
|
342 |
|
343 delete mContextStack; |
|
344 } |
|
345 moz_free(mText); |
|
346 |
|
347 |
|
348 if (--gRefCnt == 0) { |
|
349 NS_IF_RELEASE(gRDFService); |
|
350 NS_IF_RELEASE(gRDFContainerUtils); |
|
351 NS_IF_RELEASE(kRDF_type); |
|
352 NS_IF_RELEASE(kRDF_instanceOf); |
|
353 NS_IF_RELEASE(kRDF_Alt); |
|
354 NS_IF_RELEASE(kRDF_Bag); |
|
355 NS_IF_RELEASE(kRDF_Seq); |
|
356 NS_IF_RELEASE(kRDF_nextVal); |
|
357 } |
|
358 } |
|
359 |
|
360 //////////////////////////////////////////////////////////////////////// |
|
361 // nsISupports interface |
|
362 |
|
363 NS_IMPL_ADDREF(RDFContentSinkImpl) |
|
364 NS_IMPL_RELEASE(RDFContentSinkImpl) |
|
365 |
|
366 NS_IMETHODIMP |
|
367 RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result) |
|
368 { |
|
369 NS_PRECONDITION(result, "null ptr"); |
|
370 if (! result) |
|
371 return NS_ERROR_NULL_POINTER; |
|
372 |
|
373 NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID); |
|
374 NS_DEFINE_IID(kIExpatSinkIID, NS_IEXPATSINK_IID); |
|
375 NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); |
|
376 NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID); |
|
377 NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID); |
|
378 |
|
379 *result = nullptr; |
|
380 if (iid.Equals(kIRDFContentSinkIID) || |
|
381 iid.Equals(kIXMLContentSinkIID) || |
|
382 iid.Equals(kIContentSinkIID) || |
|
383 iid.Equals(kISupportsIID)) { |
|
384 *result = static_cast<nsIXMLContentSink*>(this); |
|
385 AddRef(); |
|
386 return NS_OK; |
|
387 } |
|
388 else if (iid.Equals(kIExpatSinkIID)) { |
|
389 *result = static_cast<nsIExpatSink*>(this); |
|
390 AddRef(); |
|
391 return NS_OK; |
|
392 } |
|
393 return NS_NOINTERFACE; |
|
394 } |
|
395 |
|
396 NS_IMETHODIMP |
|
397 RDFContentSinkImpl::HandleStartElement(const char16_t *aName, |
|
398 const char16_t **aAtts, |
|
399 uint32_t aAttsCount, |
|
400 int32_t aIndex, |
|
401 uint32_t aLineNumber) |
|
402 { |
|
403 FlushText(); |
|
404 |
|
405 nsresult rv = NS_ERROR_UNEXPECTED; // XXX |
|
406 |
|
407 RegisterNamespaces(aAtts); |
|
408 |
|
409 switch (mState) { |
|
410 case eRDFContentSinkState_InProlog: |
|
411 rv = OpenRDF(aName); |
|
412 break; |
|
413 |
|
414 case eRDFContentSinkState_InDocumentElement: |
|
415 rv = OpenObject(aName,aAtts); |
|
416 break; |
|
417 |
|
418 case eRDFContentSinkState_InDescriptionElement: |
|
419 rv = OpenProperty(aName,aAtts); |
|
420 break; |
|
421 |
|
422 case eRDFContentSinkState_InContainerElement: |
|
423 rv = OpenMember(aName,aAtts); |
|
424 break; |
|
425 |
|
426 case eRDFContentSinkState_InPropertyElement: |
|
427 case eRDFContentSinkState_InMemberElement: |
|
428 rv = OpenValue(aName,aAtts); |
|
429 break; |
|
430 |
|
431 case eRDFContentSinkState_InEpilog: |
|
432 PR_LOG(gLog, PR_LOG_WARNING, |
|
433 ("rdfxml: unexpected content in epilog at line %d", |
|
434 aLineNumber)); |
|
435 break; |
|
436 } |
|
437 |
|
438 return rv; |
|
439 } |
|
440 |
|
441 NS_IMETHODIMP |
|
442 RDFContentSinkImpl::HandleEndElement(const char16_t *aName) |
|
443 { |
|
444 FlushText(); |
|
445 |
|
446 nsIRDFResource* resource; |
|
447 if (NS_FAILED(PopContext(resource, mState, mParseMode))) { |
|
448 // XXX parser didn't catch unmatched tags? |
|
449 #ifdef PR_LOGGING |
|
450 if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) { |
|
451 nsAutoString tagStr(aName); |
|
452 char* tagCStr = ToNewCString(tagStr); |
|
453 |
|
454 PR_LogPrint |
|
455 ("rdfxml: extra close tag '%s' at line %d", |
|
456 tagCStr, 0/*XXX fix me */); |
|
457 |
|
458 NS_Free(tagCStr); |
|
459 } |
|
460 #endif |
|
461 |
|
462 return NS_ERROR_UNEXPECTED; // XXX |
|
463 } |
|
464 |
|
465 // If we've just popped a member or property element, _now_ is the |
|
466 // time to add that element to the graph. |
|
467 switch (mState) { |
|
468 case eRDFContentSinkState_InMemberElement: |
|
469 { |
|
470 nsCOMPtr<nsIRDFContainer> container; |
|
471 NS_NewRDFContainer(getter_AddRefs(container)); |
|
472 container->Init(mDataSource, GetContextElement(1)); |
|
473 container->AppendElement(resource); |
|
474 } |
|
475 break; |
|
476 |
|
477 case eRDFContentSinkState_InPropertyElement: |
|
478 { |
|
479 mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true); |
|
480 } break; |
|
481 default: |
|
482 break; |
|
483 } |
|
484 |
|
485 if (mContextStack->IsEmpty()) |
|
486 mState = eRDFContentSinkState_InEpilog; |
|
487 |
|
488 NS_IF_RELEASE(resource); |
|
489 return NS_OK; |
|
490 } |
|
491 |
|
492 NS_IMETHODIMP |
|
493 RDFContentSinkImpl::HandleComment(const char16_t *aName) |
|
494 { |
|
495 return NS_OK; |
|
496 } |
|
497 |
|
498 NS_IMETHODIMP |
|
499 RDFContentSinkImpl::HandleCDataSection(const char16_t *aData, |
|
500 uint32_t aLength) |
|
501 { |
|
502 return aData ? AddText(aData, aLength) : NS_OK; |
|
503 } |
|
504 |
|
505 NS_IMETHODIMP |
|
506 RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, |
|
507 const nsAString & aName, |
|
508 const nsAString & aSystemId, |
|
509 const nsAString & aPublicId, |
|
510 nsISupports* aCatalogData) |
|
511 { |
|
512 return NS_OK; |
|
513 } |
|
514 |
|
515 NS_IMETHODIMP |
|
516 RDFContentSinkImpl::HandleCharacterData(const char16_t *aData, |
|
517 uint32_t aLength) |
|
518 { |
|
519 return aData ? AddText(aData, aLength) : NS_OK; |
|
520 } |
|
521 |
|
522 NS_IMETHODIMP |
|
523 RDFContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget, |
|
524 const char16_t *aData) |
|
525 { |
|
526 return NS_OK; |
|
527 } |
|
528 |
|
529 NS_IMETHODIMP |
|
530 RDFContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion, |
|
531 const char16_t *aEncoding, |
|
532 int32_t aStandalone) |
|
533 { |
|
534 return NS_OK; |
|
535 } |
|
536 |
|
537 NS_IMETHODIMP |
|
538 RDFContentSinkImpl::ReportError(const char16_t* aErrorText, |
|
539 const char16_t* aSourceText, |
|
540 nsIScriptError *aError, |
|
541 bool *_retval) |
|
542 { |
|
543 NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); |
|
544 |
|
545 // The expat driver should report the error. |
|
546 *_retval = true; |
|
547 return NS_OK; |
|
548 } |
|
549 |
|
550 //////////////////////////////////////////////////////////////////////// |
|
551 // nsIContentSink interface |
|
552 |
|
553 NS_IMETHODIMP |
|
554 RDFContentSinkImpl::WillParse(void) |
|
555 { |
|
556 return NS_OK; |
|
557 } |
|
558 |
|
559 |
|
560 NS_IMETHODIMP |
|
561 RDFContentSinkImpl::WillBuildModel(nsDTDMode) |
|
562 { |
|
563 if (mDataSource) { |
|
564 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); |
|
565 if (sink) |
|
566 return sink->BeginLoad(); |
|
567 } |
|
568 return NS_OK; |
|
569 } |
|
570 |
|
571 NS_IMETHODIMP |
|
572 RDFContentSinkImpl::DidBuildModel(bool aTerminated) |
|
573 { |
|
574 if (mDataSource) { |
|
575 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); |
|
576 if (sink) |
|
577 return sink->EndLoad(); |
|
578 } |
|
579 return NS_OK; |
|
580 } |
|
581 |
|
582 NS_IMETHODIMP |
|
583 RDFContentSinkImpl::WillInterrupt(void) |
|
584 { |
|
585 if (mDataSource) { |
|
586 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); |
|
587 if (sink) |
|
588 return sink->Interrupt(); |
|
589 } |
|
590 return NS_OK; |
|
591 } |
|
592 |
|
593 NS_IMETHODIMP |
|
594 RDFContentSinkImpl::WillResume(void) |
|
595 { |
|
596 if (mDataSource) { |
|
597 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); |
|
598 if (sink) |
|
599 return sink->Resume(); |
|
600 } |
|
601 return NS_OK; |
|
602 } |
|
603 |
|
604 NS_IMETHODIMP |
|
605 RDFContentSinkImpl::SetParser(nsParserBase* aParser) |
|
606 { |
|
607 return NS_OK; |
|
608 } |
|
609 |
|
610 //////////////////////////////////////////////////////////////////////// |
|
611 // nsIRDFContentSink interface |
|
612 |
|
613 NS_IMETHODIMP |
|
614 RDFContentSinkImpl::Init(nsIURI* aURL) |
|
615 { |
|
616 NS_PRECONDITION(aURL != nullptr, "null ptr"); |
|
617 if (! aURL) |
|
618 return NS_ERROR_NULL_POINTER; |
|
619 |
|
620 mDocumentURL = aURL; |
|
621 NS_ADDREF(aURL); |
|
622 |
|
623 mState = eRDFContentSinkState_InProlog; |
|
624 return NS_OK; |
|
625 } |
|
626 |
|
627 NS_IMETHODIMP |
|
628 RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource) |
|
629 { |
|
630 NS_PRECONDITION(aDataSource != nullptr, "SetDataSource null ptr"); |
|
631 mDataSource = aDataSource; |
|
632 NS_ASSERTION(mDataSource != nullptr,"Couldn't QI RDF DataSource"); |
|
633 return NS_OK; |
|
634 } |
|
635 |
|
636 |
|
637 NS_IMETHODIMP |
|
638 RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource) |
|
639 { |
|
640 aDataSource = mDataSource; |
|
641 NS_IF_ADDREF(aDataSource); |
|
642 return NS_OK; |
|
643 } |
|
644 |
|
645 //////////////////////////////////////////////////////////////////////// |
|
646 // Text buffering |
|
647 |
|
648 static bool |
|
649 rdf_IsDataInBuffer(char16_t* buffer, int32_t length) |
|
650 { |
|
651 for (int32_t i = 0; i < length; ++i) { |
|
652 if (buffer[i] == ' ' || |
|
653 buffer[i] == '\t' || |
|
654 buffer[i] == '\n' || |
|
655 buffer[i] == '\r') |
|
656 continue; |
|
657 |
|
658 return true; |
|
659 } |
|
660 return false; |
|
661 } |
|
662 |
|
663 void |
|
664 RDFContentSinkImpl::ParseText(nsIRDFNode **aResult) |
|
665 { |
|
666 // XXXwaterson wasteful, but we'd need to make a copy anyway to be |
|
667 // able to call nsIRDFService::Get[Resource|Literal|...](). |
|
668 nsAutoString value; |
|
669 value.Append(mText, mTextLength); |
|
670 value.Trim(" \t\n\r"); |
|
671 |
|
672 switch (mParseMode) { |
|
673 case eRDFContentSinkParseMode_Literal: |
|
674 { |
|
675 nsIRDFLiteral *result; |
|
676 gRDFService->GetLiteral(value.get(), &result); |
|
677 *aResult = result; |
|
678 } |
|
679 break; |
|
680 |
|
681 case eRDFContentSinkParseMode_Resource: |
|
682 { |
|
683 nsIRDFResource *result; |
|
684 gRDFService->GetUnicodeResource(value, &result); |
|
685 *aResult = result; |
|
686 } |
|
687 break; |
|
688 |
|
689 case eRDFContentSinkParseMode_Int: |
|
690 { |
|
691 nsresult err; |
|
692 int32_t i = value.ToInteger(&err); |
|
693 nsIRDFInt *result; |
|
694 gRDFService->GetIntLiteral(i, &result); |
|
695 *aResult = result; |
|
696 } |
|
697 break; |
|
698 |
|
699 case eRDFContentSinkParseMode_Date: |
|
700 { |
|
701 PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length())); |
|
702 nsIRDFDate *result; |
|
703 gRDFService->GetDateLiteral(t, &result); |
|
704 *aResult = result; |
|
705 } |
|
706 break; |
|
707 |
|
708 default: |
|
709 NS_NOTREACHED("unknown parse type"); |
|
710 break; |
|
711 } |
|
712 } |
|
713 |
|
714 nsresult |
|
715 RDFContentSinkImpl::FlushText() |
|
716 { |
|
717 nsresult rv = NS_OK; |
|
718 if (0 != mTextLength) { |
|
719 if (rdf_IsDataInBuffer(mText, mTextLength)) { |
|
720 // XXX if there's anything but whitespace, then we'll |
|
721 // create a text node. |
|
722 |
|
723 switch (mState) { |
|
724 case eRDFContentSinkState_InMemberElement: { |
|
725 nsCOMPtr<nsIRDFNode> node; |
|
726 ParseText(getter_AddRefs(node)); |
|
727 |
|
728 nsCOMPtr<nsIRDFContainer> container; |
|
729 NS_NewRDFContainer(getter_AddRefs(container)); |
|
730 container->Init(mDataSource, GetContextElement(1)); |
|
731 |
|
732 container->AppendElement(node); |
|
733 } break; |
|
734 |
|
735 case eRDFContentSinkState_InPropertyElement: { |
|
736 nsCOMPtr<nsIRDFNode> node; |
|
737 ParseText(getter_AddRefs(node)); |
|
738 |
|
739 mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, true); |
|
740 } break; |
|
741 |
|
742 default: |
|
743 // just ignore it |
|
744 break; |
|
745 } |
|
746 } |
|
747 mTextLength = 0; |
|
748 } |
|
749 return rv; |
|
750 } |
|
751 |
|
752 |
|
753 nsresult |
|
754 RDFContentSinkImpl::AddText(const char16_t* aText, int32_t aLength) |
|
755 { |
|
756 // Create buffer when we first need it |
|
757 if (0 == mTextSize) { |
|
758 mText = (char16_t *) moz_malloc(sizeof(char16_t) * 4096); |
|
759 if (!mText) { |
|
760 return NS_ERROR_OUT_OF_MEMORY; |
|
761 } |
|
762 mTextSize = 4096; |
|
763 } |
|
764 |
|
765 // Copy data from string into our buffer; grow the buffer as needed. |
|
766 // It never shrinks, but since the content sink doesn't stick around, |
|
767 // this shouldn't be a bloat issue. |
|
768 int32_t amount = mTextSize - mTextLength; |
|
769 if (amount < aLength) { |
|
770 // Grow the buffer by at least a factor of two to prevent thrashing. |
|
771 // Since PR_REALLOC will leave mText intact if the call fails, |
|
772 // don't clobber mText or mTextSize until the new mem is allocated. |
|
773 int32_t newSize = (2 * mTextSize > (mTextSize + aLength)) ? |
|
774 (2 * mTextSize) : (mTextSize + aLength); |
|
775 char16_t* newText = |
|
776 (char16_t *) moz_realloc(mText, sizeof(char16_t) * newSize); |
|
777 if (!newText) |
|
778 return NS_ERROR_OUT_OF_MEMORY; |
|
779 mTextSize = newSize; |
|
780 mText = newText; |
|
781 } |
|
782 memcpy(&mText[mTextLength], aText, sizeof(char16_t) * aLength); |
|
783 mTextLength += aLength; |
|
784 |
|
785 return NS_OK; |
|
786 } |
|
787 |
|
788 bool |
|
789 rdf_RequiresAbsoluteURI(const nsString& uri) |
|
790 { |
|
791 // cheap shot at figuring out if this requires an absolute url translation |
|
792 return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) || |
|
793 StringBeginsWith(uri, NS_LITERAL_STRING("chrome:"))); |
|
794 } |
|
795 |
|
796 nsresult |
|
797 RDFContentSinkImpl::GetIdAboutAttribute(const char16_t** aAttributes, |
|
798 nsIRDFResource** aResource, |
|
799 bool* aIsAnonymous) |
|
800 { |
|
801 // This corresponds to the dirty work of production [6.5] |
|
802 nsresult rv = NS_OK; |
|
803 |
|
804 nsAutoString nodeID; |
|
805 |
|
806 nsCOMPtr<nsIAtom> localName; |
|
807 for (; *aAttributes; aAttributes += 2) { |
|
808 const nsDependentSubstring& nameSpaceURI = |
|
809 SplitExpatName(aAttributes[0], getter_AddRefs(localName)); |
|
810 |
|
811 // We'll accept either `ID' or `rdf:ID' (ibid with `about' or |
|
812 // `rdf:about') in the spirit of being liberal towards the |
|
813 // input that we receive. |
|
814 if (!nameSpaceURI.IsEmpty() && |
|
815 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { |
|
816 continue; |
|
817 } |
|
818 |
|
819 // XXX you can't specify both, but we'll just pick up the |
|
820 // first thing that was specified and ignore the other. |
|
821 |
|
822 if (localName == kAboutAtom) { |
|
823 if (aIsAnonymous) |
|
824 *aIsAnonymous = false; |
|
825 |
|
826 nsAutoString relURI(aAttributes[1]); |
|
827 if (rdf_RequiresAbsoluteURI(relURI)) { |
|
828 nsAutoCString uri; |
|
829 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri); |
|
830 if (NS_FAILED(rv)) return rv; |
|
831 |
|
832 return gRDFService->GetResource(uri, |
|
833 aResource); |
|
834 } |
|
835 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]), |
|
836 aResource); |
|
837 } |
|
838 else if (localName == kIdAtom) { |
|
839 if (aIsAnonymous) |
|
840 *aIsAnonymous = false; |
|
841 // In the spirit of leniency, we do not bother trying to |
|
842 // enforce that this be a valid "XML Name" (see |
|
843 // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per |
|
844 // 6.21. If we wanted to, this would be where to do it. |
|
845 |
|
846 // Construct an in-line resource whose URI is the |
|
847 // document's URI plus the XML name specified in the ID |
|
848 // attribute. |
|
849 nsAutoCString name; |
|
850 nsAutoCString ref('#'); |
|
851 AppendUTF16toUTF8(aAttributes[1], ref); |
|
852 |
|
853 rv = mDocumentURL->Resolve(ref, name); |
|
854 if (NS_FAILED(rv)) return rv; |
|
855 |
|
856 return gRDFService->GetResource(name, aResource); |
|
857 } |
|
858 else if (localName == kNodeIdAtom) { |
|
859 nodeID.Assign(aAttributes[1]); |
|
860 } |
|
861 else if (localName == kAboutEachAtom) { |
|
862 // XXX we don't deal with aboutEach... |
|
863 //PR_LOG(gLog, PR_LOG_WARNING, |
|
864 // ("rdfxml: ignoring aboutEach at line %d", |
|
865 // aNode.GetSourceLineNumber())); |
|
866 } |
|
867 } |
|
868 |
|
869 // Otherwise, we couldn't find anything, so just gensym one... |
|
870 if (aIsAnonymous) |
|
871 *aIsAnonymous = true; |
|
872 |
|
873 // If nodeID is present, check if we already know about it. If we've seen |
|
874 // the nodeID before, use the same resource, otherwise generate a new one. |
|
875 if (!nodeID.IsEmpty()) { |
|
876 mNodeIDMap.Get(nodeID,aResource); |
|
877 |
|
878 if (!*aResource) { |
|
879 rv = gRDFService->GetAnonymousResource(aResource); |
|
880 mNodeIDMap.Put(nodeID,*aResource); |
|
881 } |
|
882 } |
|
883 else { |
|
884 rv = gRDFService->GetAnonymousResource(aResource); |
|
885 } |
|
886 |
|
887 return rv; |
|
888 } |
|
889 |
|
890 nsresult |
|
891 RDFContentSinkImpl::GetResourceAttribute(const char16_t** aAttributes, |
|
892 nsIRDFResource** aResource) |
|
893 { |
|
894 nsCOMPtr<nsIAtom> localName; |
|
895 |
|
896 nsAutoString nodeID; |
|
897 |
|
898 for (; *aAttributes; aAttributes += 2) { |
|
899 const nsDependentSubstring& nameSpaceURI = |
|
900 SplitExpatName(aAttributes[0], getter_AddRefs(localName)); |
|
901 |
|
902 // We'll accept `resource' or `rdf:resource', under the spirit |
|
903 // that we should be liberal towards the input that we |
|
904 // receive. |
|
905 if (!nameSpaceURI.IsEmpty() && |
|
906 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { |
|
907 continue; |
|
908 } |
|
909 |
|
910 // XXX you can't specify both, but we'll just pick up the |
|
911 // first thing that was specified and ignore the other. |
|
912 |
|
913 if (localName == kResourceAtom) { |
|
914 // XXX Take the URI and make it fully qualified by |
|
915 // sticking it into the document's URL. This may not be |
|
916 // appropriate... |
|
917 nsAutoString relURI(aAttributes[1]); |
|
918 if (rdf_RequiresAbsoluteURI(relURI)) { |
|
919 nsresult rv; |
|
920 nsAutoCString uri; |
|
921 |
|
922 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri); |
|
923 if (NS_FAILED(rv)) return rv; |
|
924 |
|
925 return gRDFService->GetResource(uri, aResource); |
|
926 } |
|
927 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]), |
|
928 aResource); |
|
929 } |
|
930 else if (localName == kNodeIdAtom) { |
|
931 nodeID.Assign(aAttributes[1]); |
|
932 } |
|
933 } |
|
934 |
|
935 // If nodeID is present, check if we already know about it. If we've seen |
|
936 // the nodeID before, use the same resource, otherwise generate a new one. |
|
937 if (!nodeID.IsEmpty()) { |
|
938 mNodeIDMap.Get(nodeID,aResource); |
|
939 |
|
940 if (!*aResource) { |
|
941 nsresult rv; |
|
942 rv = gRDFService->GetAnonymousResource(aResource); |
|
943 if (NS_FAILED(rv)) { |
|
944 return rv; |
|
945 } |
|
946 mNodeIDMap.Put(nodeID,*aResource); |
|
947 } |
|
948 return NS_OK; |
|
949 } |
|
950 |
|
951 return NS_ERROR_FAILURE; |
|
952 } |
|
953 |
|
954 nsresult |
|
955 RDFContentSinkImpl::AddProperties(const char16_t** aAttributes, |
|
956 nsIRDFResource* aSubject, |
|
957 int32_t* aCount) |
|
958 { |
|
959 if (aCount) |
|
960 *aCount = 0; |
|
961 |
|
962 nsCOMPtr<nsIAtom> localName; |
|
963 for (; *aAttributes; aAttributes += 2) { |
|
964 const nsDependentSubstring& nameSpaceURI = |
|
965 SplitExpatName(aAttributes[0], getter_AddRefs(localName)); |
|
966 |
|
967 // skip 'xmlns' directives, these are "meta" information |
|
968 if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) { |
|
969 continue; |
|
970 } |
|
971 |
|
972 // skip `about', `ID', `resource', and 'nodeID' attributes (either with or |
|
973 // without the `rdf:' prefix); these are all "special" and |
|
974 // should've been dealt with by the caller. |
|
975 if (localName == kAboutAtom || localName == kIdAtom || |
|
976 localName == kResourceAtom || localName == kNodeIdAtom) { |
|
977 if (nameSpaceURI.IsEmpty() || |
|
978 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) |
|
979 continue; |
|
980 } |
|
981 |
|
982 // Skip `parseType', `RDF:parseType', and `NC:parseType'. This |
|
983 // is meta-information that will be handled in SetParseMode. |
|
984 if (localName == kParseTypeAtom) { |
|
985 if (nameSpaceURI.IsEmpty() || |
|
986 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || |
|
987 nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) { |
|
988 continue; |
|
989 } |
|
990 } |
|
991 |
|
992 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI); |
|
993 propertyStr.Append(nsAtomCString(localName)); |
|
994 |
|
995 // Add the assertion to RDF |
|
996 nsCOMPtr<nsIRDFResource> property; |
|
997 gRDFService->GetResource(propertyStr, getter_AddRefs(property)); |
|
998 |
|
999 nsCOMPtr<nsIRDFLiteral> target; |
|
1000 gRDFService->GetLiteral(aAttributes[1], |
|
1001 getter_AddRefs(target)); |
|
1002 |
|
1003 mDataSource->Assert(aSubject, property, target, true); |
|
1004 } |
|
1005 return NS_OK; |
|
1006 } |
|
1007 |
|
1008 void |
|
1009 RDFContentSinkImpl::SetParseMode(const char16_t **aAttributes) |
|
1010 { |
|
1011 nsCOMPtr<nsIAtom> localName; |
|
1012 for (; *aAttributes; aAttributes += 2) { |
|
1013 const nsDependentSubstring& nameSpaceURI = |
|
1014 SplitExpatName(aAttributes[0], getter_AddRefs(localName)); |
|
1015 |
|
1016 if (localName == kParseTypeAtom) { |
|
1017 nsDependentString v(aAttributes[1]); |
|
1018 |
|
1019 if (nameSpaceURI.IsEmpty() || |
|
1020 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { |
|
1021 if (v.EqualsLiteral("Resource")) |
|
1022 mParseMode = eRDFContentSinkParseMode_Resource; |
|
1023 |
|
1024 break; |
|
1025 } |
|
1026 else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) { |
|
1027 if (v.EqualsLiteral("Date")) |
|
1028 mParseMode = eRDFContentSinkParseMode_Date; |
|
1029 else if (v.EqualsLiteral("Integer")) |
|
1030 mParseMode = eRDFContentSinkParseMode_Int; |
|
1031 |
|
1032 break; |
|
1033 } |
|
1034 } |
|
1035 } |
|
1036 } |
|
1037 |
|
1038 //////////////////////////////////////////////////////////////////////// |
|
1039 // RDF-specific routines used to build the model |
|
1040 |
|
1041 nsresult |
|
1042 RDFContentSinkImpl::OpenRDF(const char16_t* aName) |
|
1043 { |
|
1044 // ensure that we're actually reading RDF by making sure that the |
|
1045 // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever |
|
1046 // they've declared the standard RDF namespace to be. |
|
1047 nsCOMPtr<nsIAtom> localName; |
|
1048 const nsDependentSubstring& nameSpaceURI = |
|
1049 SplitExpatName(aName, getter_AddRefs(localName)); |
|
1050 |
|
1051 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) { |
|
1052 // PR_LOG(gLog, PR_LOG_ALWAYS, |
|
1053 // ("rdfxml: expected RDF:RDF at line %d", |
|
1054 // aNode.GetSourceLineNumber())); |
|
1055 |
|
1056 return NS_ERROR_UNEXPECTED; |
|
1057 } |
|
1058 |
|
1059 PushContext(nullptr, mState, mParseMode); |
|
1060 mState = eRDFContentSinkState_InDocumentElement; |
|
1061 return NS_OK; |
|
1062 } |
|
1063 |
|
1064 nsresult |
|
1065 RDFContentSinkImpl::OpenObject(const char16_t* aName, |
|
1066 const char16_t** aAttributes) |
|
1067 { |
|
1068 // an "object" non-terminal is either a "description", a "typed |
|
1069 // node", or a "container", so this change the content sink's |
|
1070 // state appropriately. |
|
1071 nsCOMPtr<nsIAtom> localName; |
|
1072 const nsDependentSubstring& nameSpaceURI = |
|
1073 SplitExpatName(aName, getter_AddRefs(localName)); |
|
1074 |
|
1075 // Figure out the URI of this object, and create an RDF node for it. |
|
1076 nsCOMPtr<nsIRDFResource> source; |
|
1077 GetIdAboutAttribute(aAttributes, getter_AddRefs(source)); |
|
1078 |
|
1079 // If there is no `ID' or `about', then there's not much we can do. |
|
1080 if (! source) |
|
1081 return NS_ERROR_FAILURE; |
|
1082 |
|
1083 // Push the element onto the context stack |
|
1084 PushContext(source, mState, mParseMode); |
|
1085 |
|
1086 // Now figure out what kind of state transition we need to |
|
1087 // make. We'll either be going into a mode where we parse a |
|
1088 // description or a container. |
|
1089 bool isaTypedNode = true; |
|
1090 |
|
1091 if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) { |
|
1092 isaTypedNode = false; |
|
1093 |
|
1094 if (localName == kDescriptionAtom) { |
|
1095 // it's a description |
|
1096 mState = eRDFContentSinkState_InDescriptionElement; |
|
1097 } |
|
1098 else if (localName == kBagAtom) { |
|
1099 // it's a bag container |
|
1100 InitContainer(kRDF_Bag, source); |
|
1101 mState = eRDFContentSinkState_InContainerElement; |
|
1102 } |
|
1103 else if (localName == kSeqAtom) { |
|
1104 // it's a seq container |
|
1105 InitContainer(kRDF_Seq, source); |
|
1106 mState = eRDFContentSinkState_InContainerElement; |
|
1107 } |
|
1108 else if (localName == kAltAtom) { |
|
1109 // it's an alt container |
|
1110 InitContainer(kRDF_Alt, source); |
|
1111 mState = eRDFContentSinkState_InContainerElement; |
|
1112 } |
|
1113 else { |
|
1114 // heh, that's not *in* the RDF namespace: just treat it |
|
1115 // like a typed node |
|
1116 isaTypedNode = true; |
|
1117 } |
|
1118 } |
|
1119 |
|
1120 if (isaTypedNode) { |
|
1121 NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI); |
|
1122 typeStr.Append(nsAtomCString(localName)); |
|
1123 |
|
1124 nsCOMPtr<nsIRDFResource> type; |
|
1125 nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type)); |
|
1126 if (NS_FAILED(rv)) return rv; |
|
1127 |
|
1128 rv = mDataSource->Assert(source, kRDF_type, type, true); |
|
1129 if (NS_FAILED(rv)) return rv; |
|
1130 |
|
1131 mState = eRDFContentSinkState_InDescriptionElement; |
|
1132 } |
|
1133 |
|
1134 AddProperties(aAttributes, source); |
|
1135 return NS_OK; |
|
1136 } |
|
1137 |
|
1138 nsresult |
|
1139 RDFContentSinkImpl::OpenProperty(const char16_t* aName, const char16_t** aAttributes) |
|
1140 { |
|
1141 nsresult rv; |
|
1142 |
|
1143 // an "object" non-terminal is either a "description", a "typed |
|
1144 // node", or a "container", so this change the content sink's |
|
1145 // state appropriately. |
|
1146 nsCOMPtr<nsIAtom> localName; |
|
1147 const nsDependentSubstring& nameSpaceURI = |
|
1148 SplitExpatName(aName, getter_AddRefs(localName)); |
|
1149 |
|
1150 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI); |
|
1151 propertyStr.Append(nsAtomCString(localName)); |
|
1152 |
|
1153 nsCOMPtr<nsIRDFResource> property; |
|
1154 rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property)); |
|
1155 if (NS_FAILED(rv)) return rv; |
|
1156 |
|
1157 // See if they've specified a 'resource' attribute, in which case |
|
1158 // they mean *that* to be the object of this property. |
|
1159 nsCOMPtr<nsIRDFResource> target; |
|
1160 GetResourceAttribute(aAttributes, getter_AddRefs(target)); |
|
1161 |
|
1162 bool isAnonymous = false; |
|
1163 |
|
1164 if (! target) { |
|
1165 // See if an 'ID' attribute has been specified, in which case |
|
1166 // this corresponds to the fourth form of [6.12]. |
|
1167 |
|
1168 // XXX strictly speaking, we should reject the RDF/XML as |
|
1169 // invalid if they've specified both an 'ID' and a 'resource' |
|
1170 // attribute. Bah. |
|
1171 |
|
1172 // XXX strictly speaking, 'about=' isn't allowed here, but |
|
1173 // what the hell. |
|
1174 GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous); |
|
1175 } |
|
1176 |
|
1177 if (target) { |
|
1178 // They specified an inline resource for the value of this |
|
1179 // property. Create an RDF resource for the inline resource |
|
1180 // URI, add the properties to it, and attach the inline |
|
1181 // resource to its parent. |
|
1182 int32_t count; |
|
1183 rv = AddProperties(aAttributes, target, &count); |
|
1184 NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties"); |
|
1185 if (NS_FAILED(rv)) return rv; |
|
1186 |
|
1187 if (count || !isAnonymous) { |
|
1188 // If the resource was "anonymous" (i.e., they hadn't |
|
1189 // explicitly set an ID or resource attribute), then we'll |
|
1190 // only assert this property from the context element *if* |
|
1191 // there were properties specified on the anonymous |
|
1192 // resource. |
|
1193 rv = mDataSource->Assert(GetContextElement(0), property, target, true); |
|
1194 if (NS_FAILED(rv)) return rv; |
|
1195 } |
|
1196 |
|
1197 // XXX Technically, we should _not_ fall through here and push |
|
1198 // the element onto the stack: this is supposed to be a closed |
|
1199 // node. But right now I'm lazy and the code will just Do The |
|
1200 // Right Thing so long as the RDF is well-formed. |
|
1201 } |
|
1202 |
|
1203 // Push the element onto the context stack and change state. |
|
1204 PushContext(property, mState, mParseMode); |
|
1205 mState = eRDFContentSinkState_InPropertyElement; |
|
1206 SetParseMode(aAttributes); |
|
1207 |
|
1208 return NS_OK; |
|
1209 } |
|
1210 |
|
1211 nsresult |
|
1212 RDFContentSinkImpl::OpenMember(const char16_t* aName, |
|
1213 const char16_t** aAttributes) |
|
1214 { |
|
1215 // ensure that we're actually reading a member element by making |
|
1216 // sure that the opening tag is <rdf:li>, where "rdf:" corresponds |
|
1217 // to whatever they've declared the standard RDF namespace to be. |
|
1218 nsresult rv; |
|
1219 |
|
1220 nsCOMPtr<nsIAtom> localName; |
|
1221 const nsDependentSubstring& nameSpaceURI = |
|
1222 SplitExpatName(aName, getter_AddRefs(localName)); |
|
1223 |
|
1224 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || |
|
1225 localName != kLiAtom) { |
|
1226 PR_LOG(gLog, PR_LOG_ALWAYS, |
|
1227 ("rdfxml: expected RDF:li at line %d", |
|
1228 -1)); // XXX pass in line number |
|
1229 |
|
1230 return NS_ERROR_UNEXPECTED; |
|
1231 } |
|
1232 |
|
1233 // The parent element is the container. |
|
1234 nsIRDFResource* container = GetContextElement(0); |
|
1235 if (! container) |
|
1236 return NS_ERROR_NULL_POINTER; |
|
1237 |
|
1238 nsIRDFResource* resource; |
|
1239 if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) { |
|
1240 // Okay, this node has an RDF:resource="..." attribute. That |
|
1241 // means that it's a "referenced item," as covered in [6.29]. |
|
1242 nsCOMPtr<nsIRDFContainer> c; |
|
1243 NS_NewRDFContainer(getter_AddRefs(c)); |
|
1244 c->Init(mDataSource, container); |
|
1245 c->AppendElement(resource); |
|
1246 |
|
1247 // XXX Technically, we should _not_ fall through here and push |
|
1248 // the element onto the stack: this is supposed to be a closed |
|
1249 // node. But right now I'm lazy and the code will just Do The |
|
1250 // Right Thing so long as the RDF is well-formed. |
|
1251 NS_RELEASE(resource); |
|
1252 } |
|
1253 |
|
1254 // Change state. Pushing a null context element is a bit weird, |
|
1255 // but the idea is that there really is _no_ context "property". |
|
1256 // The contained element will use nsIRDFContainer::AppendElement() to add |
|
1257 // the element to the container, which requires only the container |
|
1258 // and the element to be added. |
|
1259 PushContext(nullptr, mState, mParseMode); |
|
1260 mState = eRDFContentSinkState_InMemberElement; |
|
1261 SetParseMode(aAttributes); |
|
1262 |
|
1263 return NS_OK; |
|
1264 } |
|
1265 |
|
1266 |
|
1267 nsresult |
|
1268 RDFContentSinkImpl::OpenValue(const char16_t* aName, const char16_t** aAttributes) |
|
1269 { |
|
1270 // a "value" can either be an object or a string: we'll only get |
|
1271 // *here* if it's an object, as raw text is added as a leaf. |
|
1272 return OpenObject(aName,aAttributes); |
|
1273 } |
|
1274 |
|
1275 //////////////////////////////////////////////////////////////////////// |
|
1276 // namespace resolution |
|
1277 void |
|
1278 RDFContentSinkImpl::RegisterNamespaces(const char16_t **aAttributes) |
|
1279 { |
|
1280 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource); |
|
1281 if (!sink) { |
|
1282 return; |
|
1283 } |
|
1284 NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/"); |
|
1285 for (; *aAttributes; aAttributes += 2) { |
|
1286 // check the namespace |
|
1287 const char16_t* attr = aAttributes[0]; |
|
1288 const char16_t* xmlnsP = xmlns.BeginReading(); |
|
1289 while (*attr == *xmlnsP) { |
|
1290 ++attr; |
|
1291 ++xmlnsP; |
|
1292 } |
|
1293 if (*attr != 0xFFFF || |
|
1294 xmlnsP != xmlns.EndReading()) { |
|
1295 continue; |
|
1296 } |
|
1297 // get the localname (or "xmlns" for the default namespace) |
|
1298 const char16_t* endLocal = ++attr; |
|
1299 while (*endLocal && *endLocal != 0xFFFF) { |
|
1300 ++endLocal; |
|
1301 } |
|
1302 nsDependentSubstring lname(attr, endLocal); |
|
1303 nsCOMPtr<nsIAtom> preferred = do_GetAtom(lname); |
|
1304 if (preferred == kXMLNSAtom) { |
|
1305 preferred = nullptr; |
|
1306 } |
|
1307 sink->AddNameSpace(preferred, nsDependentString(aAttributes[1])); |
|
1308 } |
|
1309 } |
|
1310 |
|
1311 //////////////////////////////////////////////////////////////////////// |
|
1312 // Qualified name resolution |
|
1313 |
|
1314 const nsDependentSubstring |
|
1315 RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName, |
|
1316 nsIAtom **aLocalName) |
|
1317 { |
|
1318 /** |
|
1319 * Expat can send the following: |
|
1320 * localName |
|
1321 * namespaceURI<separator>localName |
|
1322 * namespaceURI<separator>localName<separator>prefix |
|
1323 * |
|
1324 * and we use 0xFFFF for the <separator>. |
|
1325 * |
|
1326 */ |
|
1327 |
|
1328 const char16_t *uriEnd = aExpatName; |
|
1329 const char16_t *nameStart = aExpatName; |
|
1330 const char16_t *pos; |
|
1331 for (pos = aExpatName; *pos; ++pos) { |
|
1332 if (*pos == 0xFFFF) { |
|
1333 if (uriEnd != aExpatName) { |
|
1334 break; |
|
1335 } |
|
1336 |
|
1337 uriEnd = pos; |
|
1338 nameStart = pos + 1; |
|
1339 } |
|
1340 } |
|
1341 |
|
1342 const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd); |
|
1343 *aLocalName = NS_NewAtom(Substring(nameStart, pos)).take(); |
|
1344 return nameSpaceURI; |
|
1345 } |
|
1346 |
|
1347 nsresult |
|
1348 RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer) |
|
1349 { |
|
1350 // Do the right kind of initialization based on the container |
|
1351 // 'type' resource, and the state of the container (i.e., 'make' a |
|
1352 // new container vs. 'reinitialize' the container). |
|
1353 nsresult rv; |
|
1354 |
|
1355 static const ContainerInfo gContainerInfo[] = { |
|
1356 { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt }, |
|
1357 { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag }, |
|
1358 { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq }, |
|
1359 { 0, 0, 0 }, |
|
1360 }; |
|
1361 |
|
1362 for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) { |
|
1363 if (*info->mType != aContainerType) |
|
1364 continue; |
|
1365 |
|
1366 bool isContainer; |
|
1367 rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer); |
|
1368 if (isContainer) { |
|
1369 rv = ReinitContainer(aContainerType, aContainer); |
|
1370 } |
|
1371 else { |
|
1372 rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nullptr); |
|
1373 } |
|
1374 return rv; |
|
1375 } |
|
1376 |
|
1377 NS_NOTREACHED("not an RDF container type"); |
|
1378 return NS_ERROR_FAILURE; |
|
1379 } |
|
1380 |
|
1381 |
|
1382 |
|
1383 nsresult |
|
1384 RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer) |
|
1385 { |
|
1386 // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is |
|
1387 // idempotent, and as such, containers will have state (e.g., |
|
1388 // RDF:nextVal) maintained in the graph across loads. This |
|
1389 // re-initializes each container's RDF:nextVal to '1', and 'marks' |
|
1390 // the container as such. |
|
1391 nsresult rv; |
|
1392 |
|
1393 nsCOMPtr<nsIRDFLiteral> one; |
|
1394 rv = gRDFService->GetLiteral(MOZ_UTF16("1"), getter_AddRefs(one)); |
|
1395 if (NS_FAILED(rv)) return rv; |
|
1396 |
|
1397 // Re-initialize the 'nextval' property |
|
1398 nsCOMPtr<nsIRDFNode> nextval; |
|
1399 rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, true, getter_AddRefs(nextval)); |
|
1400 if (NS_FAILED(rv)) return rv; |
|
1401 |
|
1402 rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one); |
|
1403 if (NS_FAILED(rv)) return rv; |
|
1404 |
|
1405 // Re-mark as a container. XXX should be kRDF_type |
|
1406 rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, true); |
|
1407 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such"); |
|
1408 if (NS_FAILED(rv)) return rv; |
|
1409 |
|
1410 return NS_OK; |
|
1411 } |
|
1412 |
|
1413 //////////////////////////////////////////////////////////////////////// |
|
1414 // Content stack management |
|
1415 |
|
1416 nsIRDFResource* |
|
1417 RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */) |
|
1418 { |
|
1419 if ((nullptr == mContextStack) || |
|
1420 (uint32_t(ancestor) >= mContextStack->Length())) { |
|
1421 return nullptr; |
|
1422 } |
|
1423 |
|
1424 return mContextStack->ElementAt( |
|
1425 mContextStack->Length()-ancestor-1).mResource; |
|
1426 } |
|
1427 |
|
1428 int32_t |
|
1429 RDFContentSinkImpl::PushContext(nsIRDFResource *aResource, |
|
1430 RDFContentSinkState aState, |
|
1431 RDFContentSinkParseMode aParseMode) |
|
1432 { |
|
1433 if (! mContextStack) { |
|
1434 mContextStack = new nsAutoTArray<RDFContextStackElement, 8>(); |
|
1435 if (! mContextStack) |
|
1436 return 0; |
|
1437 } |
|
1438 |
|
1439 RDFContextStackElement* e = mContextStack->AppendElement(); |
|
1440 if (! e) |
|
1441 return mContextStack->Length(); |
|
1442 |
|
1443 e->mResource = aResource; |
|
1444 e->mState = aState; |
|
1445 e->mParseMode = aParseMode; |
|
1446 |
|
1447 return mContextStack->Length(); |
|
1448 } |
|
1449 |
|
1450 nsresult |
|
1451 RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource, |
|
1452 RDFContentSinkState &aState, |
|
1453 RDFContentSinkParseMode &aParseMode) |
|
1454 { |
|
1455 if ((nullptr == mContextStack) || |
|
1456 (mContextStack->IsEmpty())) { |
|
1457 return NS_ERROR_NULL_POINTER; |
|
1458 } |
|
1459 |
|
1460 uint32_t i = mContextStack->Length() - 1; |
|
1461 RDFContextStackElement &e = mContextStack->ElementAt(i); |
|
1462 |
|
1463 aResource = e.mResource; |
|
1464 NS_IF_ADDREF(aResource); |
|
1465 aState = e.mState; |
|
1466 aParseMode = e.mParseMode; |
|
1467 |
|
1468 mContextStack->RemoveElementAt(i); |
|
1469 return NS_OK; |
|
1470 } |
|
1471 |
|
1472 |
|
1473 //////////////////////////////////////////////////////////////////////// |
|
1474 |
|
1475 nsresult |
|
1476 NS_NewRDFContentSink(nsIRDFContentSink** aResult) |
|
1477 { |
|
1478 NS_PRECONDITION(aResult != nullptr, "null ptr"); |
|
1479 if (! aResult) |
|
1480 return NS_ERROR_NULL_POINTER; |
|
1481 |
|
1482 RDFContentSinkImpl* sink = new RDFContentSinkImpl(); |
|
1483 if (! sink) |
|
1484 return NS_ERROR_OUT_OF_MEMORY; |
|
1485 |
|
1486 NS_ADDREF(sink); |
|
1487 *aResult = sink; |
|
1488 return NS_OK; |
|
1489 } |