Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / rdf / base / src / nsRDFContentSink.cpp
blob1501c6f2d515e3675ce20b850fcfe71069964e0f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
41 An implementation for an NGLayout-style content sink that knows how
42 to build an RDF content model from XML-serialized RDF.
44 For more information on the RDF/XML syntax,
45 see http://www.w3.org/TR/REC-rdf-syntax/
47 This code is based on the final W3C Recommendation,
48 http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
50 Open Issues ------------------
52 1) factoring code with nsXMLContentSink - There's some amount of
53 common code between this and the HTML content sink. This will
54 increase as we support more and more HTML elements. How can code
55 from XML/HTML be factored?
57 2) We don't support the `parseType' attribute on the Description
58 tag; therefore, it is impossible to "inline" raw XML in this
59 implemenation.
61 3) We don't build the reifications at parse time due to the
62 footprint overhead it would incur for large RDF documents. (It
63 may be possible to attach a "reification" wrapper datasource that
64 would present this information at query-time.) Because of this,
65 the `bagID' attribute is not processed correctly.
67 4) No attempt is made to `resolve URIs' to a canonical form (the
68 specification hints that an implementation should do this). This
69 is omitted for the obvious reason that we can ill afford to
70 resolve each URI reference.
74 #include "nsCOMPtr.h"
75 #include "nsInterfaceHashtable.h"
76 #include "nsIContentSink.h"
77 #include "nsIRDFContainer.h"
78 #include "nsIRDFContainerUtils.h"
79 #include "nsIRDFContentSink.h"
80 #include "nsIRDFNode.h"
81 #include "nsIRDFService.h"
82 #include "nsIRDFXMLSink.h"
83 #include "nsIServiceManager.h"
84 #include "nsIURL.h"
85 #include "nsIXMLContentSink.h"
86 #include "nsRDFCID.h"
87 #include "nsTArray.h"
88 #include "nsXPIDLString.h"
89 #include "prlog.h"
90 #include "prmem.h"
91 #include "rdf.h"
92 #include "rdfutil.h"
93 #include "nsReadableUtils.h"
94 #include "nsIExpatSink.h"
95 #include "nsCRT.h"
96 #include "nsIAtom.h"
97 #include "nsStaticAtom.h"
98 #include "nsIScriptError.h"
99 #include "nsIDTD.h"
101 ////////////////////////////////////////////////////////////////////////
102 // XPCOM IIDs
104 static NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID); // XXX grr...
105 static NS_DEFINE_IID(kIExpatSinkIID, NS_IEXPATSINK_IID);
106 static NS_DEFINE_IID(kIRDFServiceIID, NS_IRDFSERVICE_IID);
107 static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
108 static NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID);
109 static NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID);
111 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
112 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
114 ////////////////////////////////////////////////////////////////////////
116 #ifdef PR_LOGGING
117 static PRLogModuleInfo* gLog;
118 #endif
120 ///////////////////////////////////////////////////////////////////////
122 enum RDFContentSinkState {
123 eRDFContentSinkState_InProlog,
124 eRDFContentSinkState_InDocumentElement,
125 eRDFContentSinkState_InDescriptionElement,
126 eRDFContentSinkState_InContainerElement,
127 eRDFContentSinkState_InPropertyElement,
128 eRDFContentSinkState_InMemberElement,
129 eRDFContentSinkState_InEpilog
132 enum RDFContentSinkParseMode {
133 eRDFContentSinkParseMode_Resource,
134 eRDFContentSinkParseMode_Literal,
135 eRDFContentSinkParseMode_Int,
136 eRDFContentSinkParseMode_Date
139 typedef
140 NS_STDCALL_FUNCPROTO(nsresult,
141 nsContainerTestFn,
142 nsIRDFContainerUtils, IsAlt,
143 (nsIRDFDataSource*, nsIRDFResource*, PRBool*));
145 typedef
146 NS_STDCALL_FUNCPROTO(nsresult,
147 nsMakeContainerFn,
148 nsIRDFContainerUtils, MakeAlt,
149 (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**));
151 class RDFContentSinkImpl : public nsIRDFContentSink,
152 public nsIExpatSink
154 public:
155 RDFContentSinkImpl();
156 virtual ~RDFContentSinkImpl();
158 // nsISupports
159 NS_DECL_ISUPPORTS
160 NS_DECL_NSIEXPATSINK
162 // nsIContentSink
163 NS_IMETHOD WillParse(void);
164 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
165 NS_IMETHOD DidBuildModel(PRBool aTerminated);
166 NS_IMETHOD WillInterrupt(void);
167 NS_IMETHOD WillResume(void);
168 NS_IMETHOD SetParser(nsIParser* aParser);
169 virtual void FlushPendingNotifications(mozFlushType aType) { }
170 NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
171 virtual nsISupports *GetTarget() { return nsnull; }
173 // nsIRDFContentSink
174 NS_IMETHOD Init(nsIURI* aURL);
175 NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource);
176 NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource);
178 // pseudo constants
179 static PRInt32 gRefCnt;
180 static nsIRDFService* gRDFService;
181 static nsIRDFContainerUtils* gRDFContainerUtils;
182 static nsIRDFResource* kRDF_type;
183 static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type
184 static nsIRDFResource* kRDF_Alt;
185 static nsIRDFResource* kRDF_Bag;
186 static nsIRDFResource* kRDF_Seq;
187 static nsIRDFResource* kRDF_nextVal;
189 #define RDF_ATOM(name_, value_) static nsIAtom* name_;
190 #include "nsRDFContentSinkAtomList.h"
191 #undef RDF_ATOM
193 typedef struct ContainerInfo {
194 nsIRDFResource** mType;
195 nsContainerTestFn mTestFn;
196 nsMakeContainerFn mMakeFn;
197 } ContainerInfo;
199 protected:
200 // Text management
201 void ParseText(nsIRDFNode **aResult);
203 nsresult FlushText();
204 nsresult AddText(const PRUnichar* aText, PRInt32 aLength);
206 // RDF-specific parsing
207 nsresult OpenRDF(const PRUnichar* aName);
208 nsresult OpenObject(const PRUnichar* aName ,const PRUnichar** aAttributes);
209 nsresult OpenProperty(const PRUnichar* aName, const PRUnichar** aAttributes);
210 nsresult OpenMember(const PRUnichar* aName, const PRUnichar** aAttributes);
211 nsresult OpenValue(const PRUnichar* aName, const PRUnichar** aAttributes);
213 nsresult GetIdAboutAttribute(const PRUnichar** aAttributes, nsIRDFResource** aResource, PRBool* aIsAnonymous = nsnull);
214 nsresult GetResourceAttribute(const PRUnichar** aAttributes, nsIRDFResource** aResource);
215 nsresult AddProperties(const PRUnichar** aAttributes, nsIRDFResource* aSubject, PRInt32* aCount = nsnull);
216 void SetParseMode(const PRUnichar **aAttributes);
218 PRUnichar* mText;
219 PRInt32 mTextLength;
220 PRInt32 mTextSize;
223 * From the set of given attributes, this method extracts the
224 * namespace definitions and feeds them to the datasource.
225 * These can then be suggested to the serializer to be used again.
226 * Hopefully, this will keep namespace definitions intact in a
227 * parse - serialize cycle.
229 void RegisterNamespaces(const PRUnichar **aAttributes);
232 * Extracts the localname from aExpatName, the name that the Expat parser
233 * passes us.
234 * aLocalName will contain the localname in aExpatName.
235 * The return value is a dependent string containing just the namespace.
237 const nsDependentSubstring SplitExpatName(const PRUnichar *aExpatName,
238 nsIAtom **aLocalName);
240 enum eContainerType { eBag, eSeq, eAlt };
241 nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
242 nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
244 // The datasource in which we're assigning assertions
245 nsCOMPtr<nsIRDFDataSource> mDataSource;
247 // A hash of all the node IDs referred to
248 nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap;
250 // The current state of the content sink
251 RDFContentSinkState mState;
252 RDFContentSinkParseMode mParseMode;
254 // content stack management
255 PRInt32
256 PushContext(nsIRDFResource *aContext,
257 RDFContentSinkState aState,
258 RDFContentSinkParseMode aParseMode);
260 nsresult
261 PopContext(nsIRDFResource *&aContext,
262 RDFContentSinkState &aState,
263 RDFContentSinkParseMode &aParseMode);
265 nsIRDFResource* GetContextElement(PRInt32 ancestor = 0);
268 struct RDFContextStackElement {
269 nsCOMPtr<nsIRDFResource> mResource;
270 RDFContentSinkState mState;
271 RDFContentSinkParseMode mParseMode;
274 nsAutoTArray<RDFContextStackElement, 8>* mContextStack;
276 nsIURI* mDocumentURL;
279 PRInt32 RDFContentSinkImpl::gRefCnt = 0;
280 nsIRDFService* RDFContentSinkImpl::gRDFService;
281 nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils;
282 nsIRDFResource* RDFContentSinkImpl::kRDF_type;
283 nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf;
284 nsIRDFResource* RDFContentSinkImpl::kRDF_Alt;
285 nsIRDFResource* RDFContentSinkImpl::kRDF_Bag;
286 nsIRDFResource* RDFContentSinkImpl::kRDF_Seq;
287 nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal;
289 ////////////////////////////////////////////////////////////////////////
291 #define RDF_ATOM(name_, value_) nsIAtom* RDFContentSinkImpl::name_;
292 #include "nsRDFContentSinkAtomList.h"
293 #undef RDF_ATOM
295 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
296 #include "nsRDFContentSinkAtomList.h"
297 #undef RDF_ATOM
299 static const nsStaticAtom rdf_atoms[] = {
300 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &RDFContentSinkImpl::name_),
301 #include "nsRDFContentSinkAtomList.h"
302 #undef RDF_ATOM
305 RDFContentSinkImpl::RDFContentSinkImpl()
306 : mText(nsnull),
307 mTextLength(0),
308 mTextSize(0),
309 mState(eRDFContentSinkState_InProlog),
310 mParseMode(eRDFContentSinkParseMode_Literal),
311 mContextStack(nsnull),
312 mDocumentURL(nsnull)
314 if (gRefCnt++ == 0) {
315 nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
317 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
318 if (NS_SUCCEEDED(rv)) {
319 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
320 &kRDF_type);
321 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
322 &kRDF_instanceOf);
323 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
324 &kRDF_Alt);
325 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
326 &kRDF_Bag);
327 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
328 &kRDF_Seq);
329 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
330 &kRDF_nextVal);
334 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
336 NS_RegisterStaticAtoms(rdf_atoms, NS_ARRAY_LENGTH(rdf_atoms));
339 mNodeIDMap.Init();
341 #ifdef PR_LOGGING
342 if (! gLog)
343 gLog = PR_NewLogModule("nsRDFContentSink");
344 #endif
348 RDFContentSinkImpl::~RDFContentSinkImpl()
350 #ifdef DEBUG_REFS
351 --gInstanceCount;
352 fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount);
353 #endif
355 NS_IF_RELEASE(mDocumentURL);
357 if (mContextStack) {
358 PR_LOG(gLog, PR_LOG_WARNING,
359 ("rdfxml: warning! unclosed tag"));
361 // XXX we should never need to do this, but, we'll write the
362 // code all the same. If someone left the content stack dirty,
363 // pop all the elements off the stack and release them.
364 PRInt32 i = mContextStack->Length();
365 while (0 < i--) {
366 nsIRDFResource* resource = nsnull;
367 RDFContentSinkState state;
368 RDFContentSinkParseMode parseMode;
369 PopContext(resource, state, parseMode);
371 #ifdef PR_LOGGING
372 // print some fairly useless debugging info
373 // XXX we should save line numbers on the context stack: this'd
374 // be about 1000x more helpful.
375 if (resource) {
376 nsXPIDLCString uri;
377 resource->GetValue(getter_Copies(uri));
378 PR_LOG(gLog, PR_LOG_NOTICE,
379 ("rdfxml: uri=%s", (const char*) uri));
381 #endif
383 NS_IF_RELEASE(resource);
386 delete mContextStack;
388 PR_FREEIF(mText);
391 if (--gRefCnt == 0) {
392 NS_IF_RELEASE(gRDFService);
393 NS_IF_RELEASE(gRDFContainerUtils);
394 NS_IF_RELEASE(kRDF_type);
395 NS_IF_RELEASE(kRDF_instanceOf);
396 NS_IF_RELEASE(kRDF_Alt);
397 NS_IF_RELEASE(kRDF_Bag);
398 NS_IF_RELEASE(kRDF_Seq);
399 NS_IF_RELEASE(kRDF_nextVal);
403 ////////////////////////////////////////////////////////////////////////
404 // nsISupports interface
406 NS_IMPL_ADDREF(RDFContentSinkImpl)
407 NS_IMPL_RELEASE(RDFContentSinkImpl)
409 NS_IMETHODIMP
410 RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
412 NS_PRECONDITION(result, "null ptr");
413 if (! result)
414 return NS_ERROR_NULL_POINTER;
416 *result = nsnull;
417 if (iid.Equals(kIRDFContentSinkIID) ||
418 iid.Equals(kIXMLContentSinkIID) ||
419 iid.Equals(kIContentSinkIID) ||
420 iid.Equals(kISupportsIID)) {
421 *result = static_cast<nsIXMLContentSink*>(this);
422 AddRef();
423 return NS_OK;
425 else if (iid.Equals(kIExpatSinkIID)) {
426 *result = static_cast<nsIExpatSink*>(this);
427 AddRef();
428 return NS_OK;
430 return NS_NOINTERFACE;
433 NS_IMETHODIMP
434 RDFContentSinkImpl::HandleStartElement(const PRUnichar *aName,
435 const PRUnichar **aAtts,
436 PRUint32 aAttsCount,
437 PRInt32 aIndex,
438 PRUint32 aLineNumber)
440 FlushText();
442 nsresult rv = NS_ERROR_UNEXPECTED; // XXX
444 RegisterNamespaces(aAtts);
446 switch (mState) {
447 case eRDFContentSinkState_InProlog:
448 rv = OpenRDF(aName);
449 break;
451 case eRDFContentSinkState_InDocumentElement:
452 rv = OpenObject(aName,aAtts);
453 break;
455 case eRDFContentSinkState_InDescriptionElement:
456 rv = OpenProperty(aName,aAtts);
457 break;
459 case eRDFContentSinkState_InContainerElement:
460 rv = OpenMember(aName,aAtts);
461 break;
463 case eRDFContentSinkState_InPropertyElement:
464 case eRDFContentSinkState_InMemberElement:
465 rv = OpenValue(aName,aAtts);
466 break;
468 case eRDFContentSinkState_InEpilog:
469 PR_LOG(gLog, PR_LOG_WARNING,
470 ("rdfxml: unexpected content in epilog at line %d",
471 aLineNumber));
472 break;
475 return rv;
478 NS_IMETHODIMP
479 RDFContentSinkImpl::HandleEndElement(const PRUnichar *aName)
481 FlushText();
483 nsIRDFResource* resource;
484 if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
485 // XXX parser didn't catch unmatched tags?
486 #ifdef PR_LOGGING
487 if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) {
488 nsAutoString tagStr(aName);
489 char* tagCStr = ToNewCString(tagStr);
491 PR_LogPrint
492 ("rdfxml: extra close tag '%s' at line %d",
493 tagCStr, 0/*XXX fix me */);
495 NS_Free(tagCStr);
497 #endif
499 return NS_ERROR_UNEXPECTED; // XXX
502 // If we've just popped a member or property element, _now_ is the
503 // time to add that element to the graph.
504 switch (mState) {
505 case eRDFContentSinkState_InMemberElement:
507 nsCOMPtr<nsIRDFContainer> container;
508 NS_NewRDFContainer(getter_AddRefs(container));
509 container->Init(mDataSource, GetContextElement(1));
510 container->AppendElement(resource);
512 break;
514 case eRDFContentSinkState_InPropertyElement:
516 mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, PR_TRUE);
517 } break;
518 default:
519 break;
522 if (mContextStack->IsEmpty())
523 mState = eRDFContentSinkState_InEpilog;
525 NS_IF_RELEASE(resource);
526 return NS_OK;
529 NS_IMETHODIMP
530 RDFContentSinkImpl::HandleComment(const PRUnichar *aName)
532 return NS_OK;
535 NS_IMETHODIMP
536 RDFContentSinkImpl::HandleCDataSection(const PRUnichar *aData,
537 PRUint32 aLength)
539 return aData ? AddText(aData, aLength) : NS_OK;
542 NS_IMETHODIMP
543 RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
544 const nsAString & aName,
545 const nsAString & aSystemId,
546 const nsAString & aPublicId,
547 nsISupports* aCatalogData)
549 return NS_OK;
552 NS_IMETHODIMP
553 RDFContentSinkImpl::HandleCharacterData(const PRUnichar *aData,
554 PRUint32 aLength)
556 return aData ? AddText(aData, aLength) : NS_OK;
559 NS_IMETHODIMP
560 RDFContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget,
561 const PRUnichar *aData)
563 return NS_OK;
566 NS_IMETHODIMP
567 RDFContentSinkImpl::HandleXMLDeclaration(const PRUnichar *aVersion,
568 const PRUnichar *aEncoding,
569 PRInt32 aStandalone)
571 return NS_OK;
574 NS_IMETHODIMP
575 RDFContentSinkImpl::ReportError(const PRUnichar* aErrorText,
576 const PRUnichar* aSourceText,
577 nsIScriptError *aError,
578 PRBool *_retval)
580 NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
582 // The expat driver should report the error.
583 *_retval = PR_TRUE;
584 return NS_OK;
587 ////////////////////////////////////////////////////////////////////////
588 // nsIContentSink interface
590 NS_IMETHODIMP
591 RDFContentSinkImpl::WillParse(void)
593 return NS_OK;
597 NS_IMETHODIMP
598 RDFContentSinkImpl::WillBuildModel(nsDTDMode)
600 if (mDataSource) {
601 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
602 if (sink)
603 return sink->BeginLoad();
605 return NS_OK;
608 NS_IMETHODIMP
609 RDFContentSinkImpl::DidBuildModel(PRBool aTerminated)
611 if (mDataSource) {
612 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
613 if (sink)
614 return sink->EndLoad();
616 return NS_OK;
619 NS_IMETHODIMP
620 RDFContentSinkImpl::WillInterrupt(void)
622 if (mDataSource) {
623 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
624 if (sink)
625 return sink->Interrupt();
627 return NS_OK;
630 NS_IMETHODIMP
631 RDFContentSinkImpl::WillResume(void)
633 if (mDataSource) {
634 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
635 if (sink)
636 return sink->Resume();
638 return NS_OK;
641 NS_IMETHODIMP
642 RDFContentSinkImpl::SetParser(nsIParser* aParser)
644 return NS_OK;
647 ////////////////////////////////////////////////////////////////////////
648 // nsIRDFContentSink interface
650 NS_IMETHODIMP
651 RDFContentSinkImpl::Init(nsIURI* aURL)
653 NS_PRECONDITION(aURL != nsnull, "null ptr");
654 if (! aURL)
655 return NS_ERROR_NULL_POINTER;
657 mDocumentURL = aURL;
658 NS_ADDREF(aURL);
660 mState = eRDFContentSinkState_InProlog;
661 return NS_OK;
664 NS_IMETHODIMP
665 RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource)
667 NS_PRECONDITION(aDataSource != nsnull, "SetDataSource null ptr");
668 mDataSource = aDataSource;
669 NS_ASSERTION(mDataSource != nsnull,"Couldn't QI RDF DataSource");
670 return NS_OK;
674 NS_IMETHODIMP
675 RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource)
677 aDataSource = mDataSource;
678 NS_IF_ADDREF(aDataSource);
679 return NS_OK;
682 ////////////////////////////////////////////////////////////////////////
683 // Text buffering
685 static PRBool
686 rdf_IsDataInBuffer(PRUnichar* buffer, PRInt32 length)
688 for (PRInt32 i = 0; i < length; ++i) {
689 if (buffer[i] == ' ' ||
690 buffer[i] == '\t' ||
691 buffer[i] == '\n' ||
692 buffer[i] == '\r')
693 continue;
695 return PR_TRUE;
697 return PR_FALSE;
700 void
701 RDFContentSinkImpl::ParseText(nsIRDFNode **aResult)
703 // XXXwaterson wasteful, but we'd need to make a copy anyway to be
704 // able to call nsIRDFService::Get[Resource|Literal|...]().
705 nsAutoString value;
706 value.Append(mText, mTextLength);
707 value.Trim(" \t\n\r");
709 switch (mParseMode) {
710 case eRDFContentSinkParseMode_Literal:
712 nsIRDFLiteral *result;
713 gRDFService->GetLiteral(value.get(), &result);
714 *aResult = result;
716 break;
718 case eRDFContentSinkParseMode_Resource:
720 nsIRDFResource *result;
721 gRDFService->GetUnicodeResource(value, &result);
722 *aResult = result;
724 break;
726 case eRDFContentSinkParseMode_Int:
728 PRInt32 i, err;
729 i = value.ToInteger(&err);
730 nsIRDFInt *result;
731 gRDFService->GetIntLiteral(i, &result);
732 *aResult = result;
734 break;
736 case eRDFContentSinkParseMode_Date:
738 PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length()));
739 nsIRDFDate *result;
740 gRDFService->GetDateLiteral(t, &result);
741 *aResult = result;
743 break;
745 default:
746 NS_NOTREACHED("unknown parse type");
747 break;
751 nsresult
752 RDFContentSinkImpl::FlushText()
754 nsresult rv = NS_OK;
755 if (0 != mTextLength) {
756 if (rdf_IsDataInBuffer(mText, mTextLength)) {
757 // XXX if there's anything but whitespace, then we'll
758 // create a text node.
760 switch (mState) {
761 case eRDFContentSinkState_InMemberElement: {
762 nsCOMPtr<nsIRDFNode> node;
763 ParseText(getter_AddRefs(node));
765 nsCOMPtr<nsIRDFContainer> container;
766 NS_NewRDFContainer(getter_AddRefs(container));
767 container->Init(mDataSource, GetContextElement(1));
769 container->AppendElement(node);
770 } break;
772 case eRDFContentSinkState_InPropertyElement: {
773 nsCOMPtr<nsIRDFNode> node;
774 ParseText(getter_AddRefs(node));
776 mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, PR_TRUE);
777 } break;
779 default:
780 // just ignore it
781 break;
784 mTextLength = 0;
786 return rv;
790 nsresult
791 RDFContentSinkImpl::AddText(const PRUnichar* aText, PRInt32 aLength)
793 // Create buffer when we first need it
794 if (0 == mTextSize) {
795 mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * 4096);
796 if (!mText) {
797 return NS_ERROR_OUT_OF_MEMORY;
799 mTextSize = 4096;
802 // Copy data from string into our buffer; grow the buffer as needed.
803 // It never shrinks, but since the content sink doesn't stick around,
804 // this shouldn't be a bloat issue.
805 PRInt32 amount = mTextSize - mTextLength;
806 if (amount < aLength) {
807 // Grow the buffer by at least a factor of two to prevent thrashing.
808 // Since PR_REALLOC will leave mText intact if the call fails,
809 // don't clobber mText or mTextSize until the new mem is allocated.
810 PRInt32 newSize = (2 * mTextSize > (mTextSize + aLength)) ?
811 (2 * mTextSize) : (mTextSize + aLength);
812 PRUnichar* newText =
813 (PRUnichar *) PR_REALLOC(mText, sizeof(PRUnichar) * newSize);
814 if (!newText)
815 return NS_ERROR_OUT_OF_MEMORY;
816 mTextSize = newSize;
817 mText = newText;
819 memcpy(&mText[mTextLength], aText, sizeof(PRUnichar) * aLength);
820 mTextLength += aLength;
822 return NS_OK;
825 PRBool
826 rdf_RequiresAbsoluteURI(const nsString& uri)
828 // cheap shot at figuring out if this requires an absolute url translation
829 return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) ||
830 StringBeginsWith(uri, NS_LITERAL_STRING("chrome:")));
833 nsresult
834 RDFContentSinkImpl::GetIdAboutAttribute(const PRUnichar** aAttributes,
835 nsIRDFResource** aResource,
836 PRBool* aIsAnonymous)
838 // This corresponds to the dirty work of production [6.5]
839 nsresult rv = NS_OK;
841 nsAutoString nodeID;
843 nsCOMPtr<nsIAtom> localName;
844 for (; *aAttributes; aAttributes += 2) {
845 const nsDependentSubstring& nameSpaceURI =
846 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
848 // We'll accept either `ID' or `rdf:ID' (ibid with `about' or
849 // `rdf:about') in the spirit of being liberal towards the
850 // input that we receive.
851 if (!nameSpaceURI.IsEmpty() &&
852 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
853 continue;
856 // XXX you can't specify both, but we'll just pick up the
857 // first thing that was specified and ignore the other.
859 if (localName == kAboutAtom) {
860 if (aIsAnonymous)
861 *aIsAnonymous = PR_FALSE;
863 nsAutoString relURI(aAttributes[1]);
864 if (rdf_RequiresAbsoluteURI(relURI)) {
865 nsCAutoString uri;
866 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
867 if (NS_FAILED(rv)) return rv;
869 return gRDFService->GetResource(uri,
870 aResource);
872 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
873 aResource);
875 else if (localName == kIdAtom) {
876 if (aIsAnonymous)
877 *aIsAnonymous = PR_FALSE;
878 // In the spirit of leniency, we do not bother trying to
879 // enforce that this be a valid "XML Name" (see
880 // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
881 // 6.21. If we wanted to, this would be where to do it.
883 // Construct an in-line resource whose URI is the
884 // document's URI plus the XML name specified in the ID
885 // attribute.
886 nsCAutoString name;
887 nsCAutoString ref('#');
888 AppendUTF16toUTF8(aAttributes[1], ref);
890 rv = mDocumentURL->Resolve(ref, name);
891 if (NS_FAILED(rv)) return rv;
893 return gRDFService->GetResource(name, aResource);
895 else if (localName == kNodeIdAtom) {
896 nodeID.Assign(aAttributes[1]);
898 else if (localName == kAboutEachAtom) {
899 // XXX we don't deal with aboutEach...
900 //PR_LOG(gLog, PR_LOG_WARNING,
901 // ("rdfxml: ignoring aboutEach at line %d",
902 // aNode.GetSourceLineNumber()));
906 // Otherwise, we couldn't find anything, so just gensym one...
907 if (aIsAnonymous)
908 *aIsAnonymous = PR_TRUE;
910 // If nodeID is present, check if we already know about it. If we've seen
911 // the nodeID before, use the same resource, otherwise generate a new one.
912 if (!nodeID.IsEmpty()) {
913 mNodeIDMap.Get(nodeID,aResource);
915 if (!*aResource) {
916 rv = gRDFService->GetAnonymousResource(aResource);
917 mNodeIDMap.Put(nodeID,*aResource);
920 else {
921 rv = gRDFService->GetAnonymousResource(aResource);
924 return rv;
927 nsresult
928 RDFContentSinkImpl::GetResourceAttribute(const PRUnichar** aAttributes,
929 nsIRDFResource** aResource)
931 nsCOMPtr<nsIAtom> localName;
933 nsAutoString nodeID;
935 for (; *aAttributes; aAttributes += 2) {
936 const nsDependentSubstring& nameSpaceURI =
937 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
939 // We'll accept `resource' or `rdf:resource', under the spirit
940 // that we should be liberal towards the input that we
941 // receive.
942 if (!nameSpaceURI.IsEmpty() &&
943 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
944 continue;
947 // XXX you can't specify both, but we'll just pick up the
948 // first thing that was specified and ignore the other.
950 if (localName == kResourceAtom) {
951 // XXX Take the URI and make it fully qualified by
952 // sticking it into the document's URL. This may not be
953 // appropriate...
954 nsAutoString relURI(aAttributes[1]);
955 if (rdf_RequiresAbsoluteURI(relURI)) {
956 nsresult rv;
957 nsCAutoString uri;
959 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
960 if (NS_FAILED(rv)) return rv;
962 return gRDFService->GetResource(uri, aResource);
964 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
965 aResource);
967 else if (localName == kNodeIdAtom) {
968 nodeID.Assign(aAttributes[1]);
972 // If nodeID is present, check if we already know about it. If we've seen
973 // the nodeID before, use the same resource, otherwise generate a new one.
974 if (!nodeID.IsEmpty()) {
975 mNodeIDMap.Get(nodeID,aResource);
977 if (!*aResource) {
978 nsresult rv;
979 rv = gRDFService->GetAnonymousResource(aResource);
980 if (NS_FAILED(rv)) {
981 return rv;
983 mNodeIDMap.Put(nodeID,*aResource);
985 return NS_OK;
988 return NS_ERROR_FAILURE;
991 nsresult
992 RDFContentSinkImpl::AddProperties(const PRUnichar** aAttributes,
993 nsIRDFResource* aSubject,
994 PRInt32* aCount)
996 if (aCount)
997 *aCount = 0;
999 nsCOMPtr<nsIAtom> localName;
1000 for (; *aAttributes; aAttributes += 2) {
1001 const nsDependentSubstring& nameSpaceURI =
1002 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
1004 // skip 'xmlns' directives, these are "meta" information
1005 if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) {
1006 continue;
1009 // skip `about', `ID', `resource', and 'nodeID' attributes (either with or
1010 // without the `rdf:' prefix); these are all "special" and
1011 // should've been dealt with by the caller.
1012 if (localName == kAboutAtom || localName == kIdAtom ||
1013 localName == kResourceAtom || localName == kNodeIdAtom) {
1014 if (nameSpaceURI.IsEmpty() ||
1015 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI))
1016 continue;
1019 // Skip `parseType', `RDF:parseType', and `NC:parseType'. This
1020 // is meta-information that will be handled in SetParseMode.
1021 if (localName == kParseTypeAtom) {
1022 if (nameSpaceURI.IsEmpty() ||
1023 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
1024 nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
1025 continue;
1029 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
1030 propertyStr.Append(nsAtomCString(localName));
1032 // Add the assertion to RDF
1033 nsCOMPtr<nsIRDFResource> property;
1034 gRDFService->GetResource(propertyStr, getter_AddRefs(property));
1036 nsCOMPtr<nsIRDFLiteral> target;
1037 gRDFService->GetLiteral(aAttributes[1],
1038 getter_AddRefs(target));
1040 mDataSource->Assert(aSubject, property, target, PR_TRUE);
1042 return NS_OK;
1045 void
1046 RDFContentSinkImpl::SetParseMode(const PRUnichar **aAttributes)
1048 nsCOMPtr<nsIAtom> localName;
1049 for (; *aAttributes; aAttributes += 2) {
1050 const nsDependentSubstring& nameSpaceURI =
1051 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
1053 if (localName == kParseTypeAtom) {
1054 nsDependentString v(aAttributes[1]);
1056 if (nameSpaceURI.IsEmpty() ||
1057 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
1058 if (v.EqualsLiteral("Resource"))
1059 mParseMode = eRDFContentSinkParseMode_Resource;
1061 break;
1063 else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
1064 if (v.EqualsLiteral("Date"))
1065 mParseMode = eRDFContentSinkParseMode_Date;
1066 else if (v.EqualsLiteral("Integer"))
1067 mParseMode = eRDFContentSinkParseMode_Int;
1069 break;
1075 ////////////////////////////////////////////////////////////////////////
1076 // RDF-specific routines used to build the model
1078 nsresult
1079 RDFContentSinkImpl::OpenRDF(const PRUnichar* aName)
1081 // ensure that we're actually reading RDF by making sure that the
1082 // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever
1083 // they've declared the standard RDF namespace to be.
1084 nsCOMPtr<nsIAtom> localName;
1085 const nsDependentSubstring& nameSpaceURI =
1086 SplitExpatName(aName, getter_AddRefs(localName));
1088 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) {
1089 // PR_LOG(gLog, PR_LOG_ALWAYS,
1090 // ("rdfxml: expected RDF:RDF at line %d",
1091 // aNode.GetSourceLineNumber()));
1093 return NS_ERROR_UNEXPECTED;
1096 PushContext(nsnull, mState, mParseMode);
1097 mState = eRDFContentSinkState_InDocumentElement;
1098 return NS_OK;
1101 nsresult
1102 RDFContentSinkImpl::OpenObject(const PRUnichar* aName,
1103 const PRUnichar** aAttributes)
1105 // an "object" non-terminal is either a "description", a "typed
1106 // node", or a "container", so this change the content sink's
1107 // state appropriately.
1108 nsCOMPtr<nsIAtom> localName;
1109 const nsDependentSubstring& nameSpaceURI =
1110 SplitExpatName(aName, getter_AddRefs(localName));
1112 // Figure out the URI of this object, and create an RDF node for it.
1113 nsCOMPtr<nsIRDFResource> source;
1114 GetIdAboutAttribute(aAttributes, getter_AddRefs(source));
1116 // If there is no `ID' or `about', then there's not much we can do.
1117 if (! source)
1118 return NS_ERROR_FAILURE;
1120 // Push the element onto the context stack
1121 PushContext(source, mState, mParseMode);
1123 // Now figure out what kind of state transition we need to
1124 // make. We'll either be going into a mode where we parse a
1125 // description or a container.
1126 PRBool isaTypedNode = PR_TRUE;
1128 if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
1129 isaTypedNode = PR_FALSE;
1131 if (localName == kDescriptionAtom) {
1132 // it's a description
1133 mState = eRDFContentSinkState_InDescriptionElement;
1135 else if (localName == kBagAtom) {
1136 // it's a bag container
1137 InitContainer(kRDF_Bag, source);
1138 mState = eRDFContentSinkState_InContainerElement;
1140 else if (localName == kSeqAtom) {
1141 // it's a seq container
1142 InitContainer(kRDF_Seq, source);
1143 mState = eRDFContentSinkState_InContainerElement;
1145 else if (localName == kAltAtom) {
1146 // it's an alt container
1147 InitContainer(kRDF_Alt, source);
1148 mState = eRDFContentSinkState_InContainerElement;
1150 else {
1151 // heh, that's not *in* the RDF namespace: just treat it
1152 // like a typed node
1153 isaTypedNode = PR_TRUE;
1157 if (isaTypedNode) {
1158 NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI);
1159 typeStr.Append(nsAtomCString(localName));
1161 nsCOMPtr<nsIRDFResource> type;
1162 nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type));
1163 if (NS_FAILED(rv)) return rv;
1165 rv = mDataSource->Assert(source, kRDF_type, type, PR_TRUE);
1166 if (NS_FAILED(rv)) return rv;
1168 mState = eRDFContentSinkState_InDescriptionElement;
1171 AddProperties(aAttributes, source);
1172 return NS_OK;
1175 nsresult
1176 RDFContentSinkImpl::OpenProperty(const PRUnichar* aName, const PRUnichar** aAttributes)
1178 nsresult rv;
1180 // an "object" non-terminal is either a "description", a "typed
1181 // node", or a "container", so this change the content sink's
1182 // state appropriately.
1183 nsCOMPtr<nsIAtom> localName;
1184 const nsDependentSubstring& nameSpaceURI =
1185 SplitExpatName(aName, getter_AddRefs(localName));
1187 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
1188 propertyStr.Append(nsAtomCString(localName));
1190 nsCOMPtr<nsIRDFResource> property;
1191 rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property));
1192 if (NS_FAILED(rv)) return rv;
1194 // See if they've specified a 'resource' attribute, in which case
1195 // they mean *that* to be the object of this property.
1196 nsCOMPtr<nsIRDFResource> target;
1197 GetResourceAttribute(aAttributes, getter_AddRefs(target));
1199 PRBool isAnonymous = PR_FALSE;
1201 if (! target) {
1202 // See if an 'ID' attribute has been specified, in which case
1203 // this corresponds to the fourth form of [6.12].
1205 // XXX strictly speaking, we should reject the RDF/XML as
1206 // invalid if they've specified both an 'ID' and a 'resource'
1207 // attribute. Bah.
1209 // XXX strictly speaking, 'about=' isn't allowed here, but
1210 // what the hell.
1211 GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous);
1214 if (target) {
1215 // They specified an inline resource for the value of this
1216 // property. Create an RDF resource for the inline resource
1217 // URI, add the properties to it, and attach the inline
1218 // resource to its parent.
1219 PRInt32 count;
1220 rv = AddProperties(aAttributes, target, &count);
1221 NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties");
1222 if (NS_FAILED(rv)) return rv;
1224 if (count || !isAnonymous) {
1225 // If the resource was "anonymous" (i.e., they hadn't
1226 // explicitly set an ID or resource attribute), then we'll
1227 // only assert this property from the context element *if*
1228 // there were properties specified on the anonymous
1229 // resource.
1230 rv = mDataSource->Assert(GetContextElement(0), property, target, PR_TRUE);
1231 if (NS_FAILED(rv)) return rv;
1234 // XXX Technically, we should _not_ fall through here and push
1235 // the element onto the stack: this is supposed to be a closed
1236 // node. But right now I'm lazy and the code will just Do The
1237 // Right Thing so long as the RDF is well-formed.
1240 // Push the element onto the context stack and change state.
1241 PushContext(property, mState, mParseMode);
1242 mState = eRDFContentSinkState_InPropertyElement;
1243 SetParseMode(aAttributes);
1245 return NS_OK;
1248 nsresult
1249 RDFContentSinkImpl::OpenMember(const PRUnichar* aName,
1250 const PRUnichar** aAttributes)
1252 // ensure that we're actually reading a member element by making
1253 // sure that the opening tag is <rdf:li>, where "rdf:" corresponds
1254 // to whatever they've declared the standard RDF namespace to be.
1255 nsresult rv;
1257 nsCOMPtr<nsIAtom> localName;
1258 const nsDependentSubstring& nameSpaceURI =
1259 SplitExpatName(aName, getter_AddRefs(localName));
1261 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
1262 localName != kLiAtom) {
1263 PR_LOG(gLog, PR_LOG_ALWAYS,
1264 ("rdfxml: expected RDF:li at line %d",
1265 -1)); // XXX pass in line number
1267 return NS_ERROR_UNEXPECTED;
1270 // The parent element is the container.
1271 nsIRDFResource* container = GetContextElement(0);
1272 if (! container)
1273 return NS_ERROR_NULL_POINTER;
1275 nsIRDFResource* resource;
1276 if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) {
1277 // Okay, this node has an RDF:resource="..." attribute. That
1278 // means that it's a "referenced item," as covered in [6.29].
1279 nsCOMPtr<nsIRDFContainer> c;
1280 NS_NewRDFContainer(getter_AddRefs(c));
1281 c->Init(mDataSource, container);
1282 c->AppendElement(resource);
1284 // XXX Technically, we should _not_ fall through here and push
1285 // the element onto the stack: this is supposed to be a closed
1286 // node. But right now I'm lazy and the code will just Do The
1287 // Right Thing so long as the RDF is well-formed.
1288 NS_RELEASE(resource);
1291 // Change state. Pushing a null context element is a bit weird,
1292 // but the idea is that there really is _no_ context "property".
1293 // The contained element will use nsIRDFContainer::AppendElement() to add
1294 // the element to the container, which requires only the container
1295 // and the element to be added.
1296 PushContext(nsnull, mState, mParseMode);
1297 mState = eRDFContentSinkState_InMemberElement;
1298 SetParseMode(aAttributes);
1300 return NS_OK;
1304 nsresult
1305 RDFContentSinkImpl::OpenValue(const PRUnichar* aName, const PRUnichar** aAttributes)
1307 // a "value" can either be an object or a string: we'll only get
1308 // *here* if it's an object, as raw text is added as a leaf.
1309 return OpenObject(aName,aAttributes);
1312 ////////////////////////////////////////////////////////////////////////
1313 // namespace resolution
1314 void
1315 RDFContentSinkImpl::RegisterNamespaces(const PRUnichar **aAttributes)
1317 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
1318 if (!sink) {
1319 return;
1321 NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/");
1322 for (; *aAttributes; aAttributes += 2) {
1323 // check the namespace
1324 const PRUnichar* attr = aAttributes[0];
1325 const PRUnichar* xmlnsP = xmlns.BeginReading();
1326 while (*attr == *xmlnsP) {
1327 ++attr;
1328 ++xmlnsP;
1330 if (*attr != 0xFFFF ||
1331 xmlnsP != xmlns.EndReading()) {
1332 continue;
1334 // get the localname (or "xmlns" for the default namespace)
1335 const PRUnichar* endLocal = ++attr;
1336 while (*endLocal && *endLocal != 0xFFFF) {
1337 ++endLocal;
1339 nsDependentSubstring lname(attr, endLocal);
1340 nsCOMPtr<nsIAtom> preferred = do_GetAtom(lname);
1341 if (preferred == kXMLNSAtom) {
1342 preferred = nsnull;
1344 sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
1348 ////////////////////////////////////////////////////////////////////////
1349 // Qualified name resolution
1351 const nsDependentSubstring
1352 RDFContentSinkImpl::SplitExpatName(const PRUnichar *aExpatName,
1353 nsIAtom **aLocalName)
1356 * Expat can send the following:
1357 * localName
1358 * namespaceURI<separator>localName
1359 * namespaceURI<separator>localName<separator>prefix
1361 * and we use 0xFFFF for the <separator>.
1365 const PRUnichar *uriEnd = aExpatName;
1366 const PRUnichar *nameStart = aExpatName;
1367 const PRUnichar *pos;
1368 for (pos = aExpatName; *pos; ++pos) {
1369 if (*pos == 0xFFFF) {
1370 if (uriEnd != aExpatName) {
1371 break;
1374 uriEnd = pos;
1375 nameStart = pos + 1;
1379 const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd);
1380 *aLocalName = NS_NewAtom(Substring(nameStart, pos));
1381 return nameSpaceURI;
1384 nsresult
1385 RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
1387 // Do the right kind of initialization based on the container
1388 // 'type' resource, and the state of the container (i.e., 'make' a
1389 // new container vs. 'reinitialize' the container).
1390 nsresult rv;
1392 static const ContainerInfo gContainerInfo[] = {
1393 { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt },
1394 { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag },
1395 { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq },
1396 { 0, 0, 0 },
1399 for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) {
1400 if (*info->mType != aContainerType)
1401 continue;
1403 PRBool isContainer;
1404 rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer);
1405 if (isContainer) {
1406 rv = ReinitContainer(aContainerType, aContainer);
1408 else {
1409 rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nsnull);
1411 return rv;
1414 NS_NOTREACHED("not an RDF container type");
1415 return NS_ERROR_FAILURE;
1420 nsresult
1421 RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
1423 // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is
1424 // idempotent, and as such, containers will have state (e.g.,
1425 // RDF:nextVal) maintained in the graph across loads. This
1426 // re-initializes each container's RDF:nextVal to '1', and 'marks'
1427 // the container as such.
1428 nsresult rv;
1430 nsCOMPtr<nsIRDFLiteral> one;
1431 rv = gRDFService->GetLiteral(NS_LITERAL_STRING("1").get(), getter_AddRefs(one));
1432 if (NS_FAILED(rv)) return rv;
1434 // Re-initialize the 'nextval' property
1435 nsCOMPtr<nsIRDFNode> nextval;
1436 rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextval));
1437 if (NS_FAILED(rv)) return rv;
1439 rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one);
1440 if (NS_FAILED(rv)) return rv;
1442 // Re-mark as a container. XXX should be kRDF_type
1443 rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, PR_TRUE);
1444 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such");
1445 if (NS_FAILED(rv)) return rv;
1447 return NS_OK;
1450 ////////////////////////////////////////////////////////////////////////
1451 // Content stack management
1453 nsIRDFResource*
1454 RDFContentSinkImpl::GetContextElement(PRInt32 ancestor /* = 0 */)
1456 if ((nsnull == mContextStack) ||
1457 (PRUint32(ancestor) >= mContextStack->Length())) {
1458 return nsnull;
1461 return mContextStack->ElementAt(
1462 mContextStack->Length()-ancestor-1).mResource;
1465 PRInt32
1466 RDFContentSinkImpl::PushContext(nsIRDFResource *aResource,
1467 RDFContentSinkState aState,
1468 RDFContentSinkParseMode aParseMode)
1470 if (! mContextStack) {
1471 mContextStack = new nsAutoTArray<RDFContextStackElement, 8>();
1472 if (! mContextStack)
1473 return 0;
1476 RDFContextStackElement* e = mContextStack->AppendElement();
1477 if (! e)
1478 return mContextStack->Length();
1480 e->mResource = aResource;
1481 e->mState = aState;
1482 e->mParseMode = aParseMode;
1484 return mContextStack->Length();
1487 nsresult
1488 RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource,
1489 RDFContentSinkState &aState,
1490 RDFContentSinkParseMode &aParseMode)
1492 if ((nsnull == mContextStack) ||
1493 (mContextStack->IsEmpty())) {
1494 return NS_ERROR_NULL_POINTER;
1497 PRUint32 i = mContextStack->Length() - 1;
1498 RDFContextStackElement &e = mContextStack->ElementAt(i);
1500 aResource = e.mResource;
1501 NS_IF_ADDREF(aResource);
1502 aState = e.mState;
1503 aParseMode = e.mParseMode;
1505 mContextStack->RemoveElementAt(i);
1506 return NS_OK;
1510 ////////////////////////////////////////////////////////////////////////
1512 nsresult
1513 NS_NewRDFContentSink(nsIRDFContentSink** aResult)
1515 NS_PRECONDITION(aResult != nsnull, "null ptr");
1516 if (! aResult)
1517 return NS_ERROR_NULL_POINTER;
1519 RDFContentSinkImpl* sink = new RDFContentSinkImpl();
1520 if (! sink)
1521 return NS_ERROR_OUT_OF_MEMORY;
1523 NS_ADDREF(sink);
1524 *aResult = sink;
1525 return NS_OK;