Back to index

lightning-sunbird  0.9+nobinonly
nsRDFContentSink.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040 
00041   An implementation for an NGLayout-style content sink that knows how
00042   to build an RDF content model from XML-serialized RDF.
00043 
00044   For more information on the RDF/XML syntax,
00045   see http://www.w3.org/TR/REC-rdf-syntax/
00046 
00047   This code is based on the final W3C Recommendation,
00048   http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
00049 
00050   Open Issues ------------------
00051 
00052   1) factoring code with nsXMLContentSink - There's some amount of
00053      common code between this and the HTML content sink. This will
00054      increase as we support more and more HTML elements. How can code
00055      from XML/HTML be factored?
00056 
00057   2) We don't support the `parseType' attribute on the Description
00058      tag; therefore, it is impossible to "inline" raw XML in this
00059      implemenation.
00060 
00061   3) We don't build the reifications at parse time due to the
00062      footprint overhead it would incur for large RDF documents. (It
00063      may be possible to attach a "reification" wrapper datasource that
00064      would present this information at query-time.) Because of this,
00065      the `bagID' attribute is not processed correctly.
00066 
00067   4) No attempt is made to `resolve URIs' to a canonical form (the
00068      specification hints that an implementation should do this). This
00069      is omitted for the obvious reason that we can ill afford to
00070      resolve each URI reference.
00071 
00072 */
00073 
00074 #include "nsCOMPtr.h"
00075 #include "nsInterfaceHashtable.h"
00076 #include "nsIContentSink.h"
00077 #include "nsIRDFContainer.h"
00078 #include "nsIRDFContainerUtils.h"
00079 #include "nsIRDFContentSink.h"
00080 #include "nsIRDFNode.h"
00081 #include "nsIRDFService.h"
00082 #include "nsIRDFXMLSink.h"
00083 #include "nsIServiceManager.h"
00084 #include "nsIURL.h"
00085 #include "nsIXMLContentSink.h"
00086 #include "nsRDFCID.h"
00087 #include "nsRDFParserUtils.h"
00088 #include "nsVoidArray.h"
00089 #include "nsXPIDLString.h"
00090 #include "prlog.h"
00091 #include "prmem.h"
00092 #include "rdf.h"
00093 #include "rdfutil.h"
00094 #include "nsReadableUtils.h"
00095 #include "nsIExpatSink.h"
00096 #include "nsCRT.h"
00097 #include "nsIAtom.h"
00098 #include "nsStaticAtom.h"
00099 
00101 // XPCOM IIDs
00102 
00103 static NS_DEFINE_IID(kIContentSinkIID,         NS_ICONTENT_SINK_IID); // XXX grr...
00104 static NS_DEFINE_IID(kIExpatSinkIID,           NS_IEXPATSINK_IID);
00105 static NS_DEFINE_IID(kIRDFServiceIID,          NS_IRDFSERVICE_IID);
00106 static NS_DEFINE_IID(kISupportsIID,            NS_ISUPPORTS_IID);
00107 static NS_DEFINE_IID(kIXMLContentSinkIID,      NS_IXMLCONTENT_SINK_IID);
00108 static NS_DEFINE_IID(kIRDFContentSinkIID,      NS_IRDFCONTENTSINK_IID);
00109 
00110 static NS_DEFINE_CID(kRDFServiceCID,            NS_RDFSERVICE_CID);
00111 static NS_DEFINE_CID(kRDFContainerUtilsCID,     NS_RDFCONTAINERUTILS_CID);
00112 
00114 
00115 #ifdef PR_LOGGING
00116 static PRLogModuleInfo* gLog;
00117 #endif
00118 
00120 
00121 enum RDFContentSinkState {
00122     eRDFContentSinkState_InProlog,
00123     eRDFContentSinkState_InDocumentElement,
00124     eRDFContentSinkState_InDescriptionElement,
00125     eRDFContentSinkState_InContainerElement,
00126     eRDFContentSinkState_InPropertyElement,
00127     eRDFContentSinkState_InMemberElement,
00128     eRDFContentSinkState_InEpilog
00129 };
00130 
00131 enum RDFContentSinkParseMode {
00132     eRDFContentSinkParseMode_Resource,
00133     eRDFContentSinkParseMode_Literal,
00134     eRDFContentSinkParseMode_Int,
00135     eRDFContentSinkParseMode_Date
00136 };
00137 
00138 MOZ_DECL_CTOR_COUNTER(RDFContentSinkImpl::NameSpaceEntry)
00139 
00140 typedef
00141 NS_STDCALL_FUNCPROTO(nsresult,
00142                      nsContainerTestFn,
00143                      nsIRDFContainerUtils, IsAlt,
00144                      (nsIRDFDataSource*, nsIRDFResource*, PRBool*));
00145 
00146 typedef
00147 NS_STDCALL_FUNCPROTO(nsresult,
00148                      nsMakeContainerFn,
00149                      nsIRDFContainerUtils, MakeAlt,
00150                      (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**));
00151 
00152 class RDFContentSinkImpl : public nsIRDFContentSink,
00153                            public nsIExpatSink
00154 {
00155 public:
00156     RDFContentSinkImpl();
00157     virtual ~RDFContentSinkImpl();
00158 
00159     // nsISupports
00160     NS_DECL_ISUPPORTS
00161     NS_DECL_NSIEXPATSINK
00162 
00163     // nsIContentSink
00164     NS_IMETHOD WillBuildModel(void);
00165     NS_IMETHOD DidBuildModel(void);
00166     NS_IMETHOD WillInterrupt(void);
00167     NS_IMETHOD WillResume(void);
00168     NS_IMETHOD SetParser(nsIParser* aParser);  
00169     virtual void FlushPendingNotifications(mozFlushType aType) { }
00170     NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
00171     virtual nsISupports *GetTarget() { return nsnull; }
00172 
00173     // nsIRDFContentSink
00174     NS_IMETHOD Init(nsIURI* aURL);
00175     NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource);
00176     NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource);
00177 
00178     // pseudo constants
00179     static PRInt32 gRefCnt;
00180     static nsIRDFService* gRDFService;
00181     static nsIRDFContainerUtils* gRDFContainerUtils;
00182     static nsIRDFResource* kRDF_type;
00183     static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type
00184     static nsIRDFResource* kRDF_Alt;
00185     static nsIRDFResource* kRDF_Bag;
00186     static nsIRDFResource* kRDF_Seq;
00187     static nsIRDFResource* kRDF_nextVal;
00188 
00189     static nsIAtom* kAboutAtom;
00190     static nsIAtom* kIdAtom;
00191     static nsIAtom* kNodeIdAtom;
00192     static nsIAtom* kAboutEachAtom;
00193     static nsIAtom* kResourceAtom;
00194     static nsIAtom* kRDFAtom;
00195     static nsIAtom* kDescriptionAtom;
00196     static nsIAtom* kBagAtom;
00197     static nsIAtom* kSeqAtom;
00198     static nsIAtom* kAltAtom;
00199     static nsIAtom* kLiAtom;
00200     static nsIAtom* kXMLNSAtom;
00201     static nsIAtom* kParseTypeAtom;
00202 
00203 
00204     typedef struct ContainerInfo {
00205         nsIRDFResource**  mType;
00206         nsContainerTestFn mTestFn;
00207         nsMakeContainerFn mMakeFn;
00208     } ContainerInfo;
00209 
00210 protected:
00211     // Text management
00212     void ParseText(nsIRDFNode **aResult);
00213 
00214     nsresult FlushText(PRBool aCreateTextNode=PR_TRUE,
00215                        PRBool* aDidFlush=nsnull);
00216     nsresult AddText(const PRUnichar* aText, PRInt32 aLength);
00217 
00218     // RDF-specific parsing
00219     nsresult OpenRDF(const PRUnichar* aName);
00220     nsresult OpenObject(const PRUnichar* aName ,const PRUnichar** aAttributes);
00221     nsresult OpenProperty(const PRUnichar* aName, const PRUnichar** aAttributes);
00222     nsresult OpenMember(const PRUnichar* aName, const PRUnichar** aAttributes);
00223     nsresult OpenValue(const PRUnichar* aName, const PRUnichar** aAttributes);
00224     
00225     nsresult GetIdAboutAttribute(const PRUnichar** aAttributes, nsIRDFResource** aResource, PRBool* aIsAnonymous = nsnull);
00226     nsresult GetResourceAttribute(const PRUnichar** aAttributes, nsIRDFResource** aResource);
00227     nsresult AddProperties(const PRUnichar** aAttributes, nsIRDFResource* aSubject, PRInt32* aCount = nsnull);
00228     void SetParseMode(const PRUnichar **aAttributes);
00229 
00230     PRUnichar* mText;
00231     PRInt32 mTextLength;
00232     PRInt32 mTextSize;
00233 
00241     void RegisterNamespaces(const PRUnichar **aAttributes);
00242 
00249     const nsDependentSubstring SplitExpatName(const PRUnichar *aExpatName,
00250                                               nsIAtom **aLocalName);
00251 
00252     enum eContainerType { eBag, eSeq, eAlt };
00253     nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
00254     nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
00255 
00256     // The datasource in which we're assigning assertions
00257     nsCOMPtr<nsIRDFDataSource> mDataSource;
00258 
00259     // A hash of all the node IDs referred to
00260     nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap;
00261 
00262     // The current state of the content sink
00263     RDFContentSinkState mState;
00264     RDFContentSinkParseMode mParseMode;
00265 
00266     // content stack management
00267     PRInt32         
00268     PushContext(nsIRDFResource *aContext,
00269                 RDFContentSinkState aState,
00270                 RDFContentSinkParseMode aParseMode);
00271 
00272     nsresult
00273     PopContext(nsIRDFResource         *&aContext,
00274                RDFContentSinkState     &aState,
00275                RDFContentSinkParseMode &aParseMode);
00276 
00277     nsIRDFResource* GetContextElement(PRInt32 ancestor = 0);
00278 
00279     nsAutoVoidArray* mContextStack;
00280 
00281     nsIURI*      mDocumentURL;
00282 };
00283 
00284 PRInt32         RDFContentSinkImpl::gRefCnt = 0;
00285 nsIRDFService*  RDFContentSinkImpl::gRDFService;
00286 nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils;
00287 nsIRDFResource* RDFContentSinkImpl::kRDF_type;
00288 nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf;
00289 nsIRDFResource* RDFContentSinkImpl::kRDF_Alt;
00290 nsIRDFResource* RDFContentSinkImpl::kRDF_Bag;
00291 nsIRDFResource* RDFContentSinkImpl::kRDF_Seq;
00292 nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal;
00293 
00294 nsIAtom* RDFContentSinkImpl::kAboutAtom;
00295 nsIAtom* RDFContentSinkImpl::kIdAtom;
00296 nsIAtom* RDFContentSinkImpl::kNodeIdAtom;
00297 nsIAtom* RDFContentSinkImpl::kAboutEachAtom;
00298 nsIAtom* RDFContentSinkImpl::kResourceAtom;
00299 nsIAtom* RDFContentSinkImpl::kRDFAtom;
00300 nsIAtom* RDFContentSinkImpl::kDescriptionAtom;
00301 nsIAtom* RDFContentSinkImpl::kBagAtom;
00302 nsIAtom* RDFContentSinkImpl::kSeqAtom;
00303 nsIAtom* RDFContentSinkImpl::kAltAtom;
00304 nsIAtom* RDFContentSinkImpl::kLiAtom;
00305 nsIAtom* RDFContentSinkImpl::kXMLNSAtom;
00306 nsIAtom* RDFContentSinkImpl::kParseTypeAtom;
00307 
00309 static const nsStaticAtom rdf_atoms[] = {
00310     { "about", &RDFContentSinkImpl::kAboutAtom },
00311     { "ID", &RDFContentSinkImpl::kIdAtom },
00312     { "nodeID", &RDFContentSinkImpl::kNodeIdAtom },
00313     { "aboutEach", &RDFContentSinkImpl::kAboutEachAtom },
00314     { "resource", &RDFContentSinkImpl::kResourceAtom },
00315     { "RDF", &RDFContentSinkImpl::kRDFAtom },
00316     { "Description", &RDFContentSinkImpl::kDescriptionAtom },
00317     { "Bag", &RDFContentSinkImpl::kBagAtom },
00318     { "Seq", &RDFContentSinkImpl::kSeqAtom },
00319     { "Alt", &RDFContentSinkImpl::kAltAtom },
00320     { "li", &RDFContentSinkImpl::kLiAtom },
00321     { "xmlns", &RDFContentSinkImpl::kXMLNSAtom },
00322     { "parseType", &RDFContentSinkImpl::kParseTypeAtom },
00323 };
00324 
00325 RDFContentSinkImpl::RDFContentSinkImpl()
00326     : mText(nsnull),
00327       mTextLength(0),
00328       mTextSize(0),
00329       mState(eRDFContentSinkState_InProlog),
00330       mParseMode(eRDFContentSinkParseMode_Literal),
00331       mContextStack(nsnull),
00332       mDocumentURL(nsnull)
00333 {
00334     if (gRefCnt++ == 0) {
00335         nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
00336 
00337         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
00338         if (NS_SUCCEEDED(rv)) {
00339             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
00340                                           &kRDF_type);
00341             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
00342                                           &kRDF_instanceOf);
00343             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
00344                                           &kRDF_Alt);
00345             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
00346                                           &kRDF_Bag);
00347             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
00348                                           &kRDF_Seq);
00349             rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
00350                                           &kRDF_nextVal);
00351         }
00352 
00353 
00354         rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
00355 
00356         NS_RegisterStaticAtoms(rdf_atoms, NS_ARRAY_LENGTH(rdf_atoms));
00357     }
00358 
00359     mNodeIDMap.Init();
00360 
00361 #ifdef PR_LOGGING
00362     if (! gLog)
00363         gLog = PR_NewLogModule("nsRDFContentSink");
00364 #endif
00365 }
00366 
00367 
00368 RDFContentSinkImpl::~RDFContentSinkImpl()
00369 {
00370 #ifdef DEBUG_REFS
00371     --gInstanceCount;
00372     fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount);
00373 #endif
00374 
00375     NS_IF_RELEASE(mDocumentURL);
00376 
00377     if (mContextStack) {
00378         PR_LOG(gLog, PR_LOG_WARNING,
00379                ("rdfxml: warning! unclosed tag"));
00380 
00381         // XXX we should never need to do this, but, we'll write the
00382         // code all the same. If someone left the content stack dirty,
00383         // pop all the elements off the stack and release them.
00384         PRInt32 i = mContextStack->Count();
00385         while (0 < i--) {
00386             nsIRDFResource* resource;
00387             RDFContentSinkState state;
00388             RDFContentSinkParseMode parseMode;
00389             PopContext(resource, state, parseMode);
00390 
00391 #ifdef PR_LOGGING
00392             // print some fairly useless debugging info
00393             // XXX we should save line numbers on the context stack: this'd
00394             // be about 1000x more helpful.
00395             if (resource) {
00396                 nsXPIDLCString uri;
00397                 resource->GetValue(getter_Copies(uri));
00398                 PR_LOG(gLog, PR_LOG_NOTICE,
00399                        ("rdfxml:   uri=%s", (const char*) uri));
00400             }
00401 #endif
00402 
00403             NS_IF_RELEASE(resource);
00404         }
00405 
00406         delete mContextStack;
00407     }
00408     PR_FREEIF(mText);
00409 
00410 
00411     if (--gRefCnt == 0) {
00412         NS_IF_RELEASE(gRDFService);
00413         NS_IF_RELEASE(gRDFContainerUtils);
00414         NS_IF_RELEASE(kRDF_type);
00415         NS_IF_RELEASE(kRDF_instanceOf);
00416         NS_IF_RELEASE(kRDF_Alt);
00417         NS_IF_RELEASE(kRDF_Bag);
00418         NS_IF_RELEASE(kRDF_Seq);
00419         NS_IF_RELEASE(kRDF_nextVal);
00420     }
00421 }
00422 
00424 // nsISupports interface
00425 
00426 NS_IMPL_ADDREF(RDFContentSinkImpl)
00427 NS_IMPL_RELEASE(RDFContentSinkImpl)
00428 
00429 NS_IMETHODIMP
00430 RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
00431 {
00432     NS_PRECONDITION(result, "null ptr");
00433     if (! result)
00434         return NS_ERROR_NULL_POINTER;
00435 
00436     *result = nsnull;
00437     if (iid.Equals(kIRDFContentSinkIID) ||
00438         iid.Equals(kIXMLContentSinkIID) ||
00439         iid.Equals(kIContentSinkIID) ||
00440         iid.Equals(kISupportsIID)) {
00441         *result = NS_STATIC_CAST(nsIXMLContentSink*, this);
00442         AddRef();
00443         return NS_OK;
00444     }
00445     else if (iid.Equals(kIExpatSinkIID)) {
00446       *result = NS_STATIC_CAST(nsIExpatSink*, this);
00447        AddRef();
00448        return NS_OK;
00449     }
00450     return NS_NOINTERFACE;
00451 }
00452 
00453 NS_IMETHODIMP 
00454 RDFContentSinkImpl::HandleStartElement(const PRUnichar *aName, 
00455                                        const PRUnichar **aAtts, 
00456                                        PRUint32 aAttsCount, 
00457                                        PRInt32 aIndex, 
00458                                        PRUint32 aLineNumber)
00459 {
00460   FlushText();
00461 
00462   nsresult rv = NS_ERROR_UNEXPECTED; // XXX
00463 
00464   RegisterNamespaces(aAtts);
00465 
00466   switch (mState) {
00467   case eRDFContentSinkState_InProlog:
00468       rv = OpenRDF(aName);
00469       break;
00470 
00471   case eRDFContentSinkState_InDocumentElement:
00472       rv = OpenObject(aName,aAtts);
00473       break;
00474 
00475   case eRDFContentSinkState_InDescriptionElement:
00476       rv = OpenProperty(aName,aAtts);
00477       break;
00478 
00479   case eRDFContentSinkState_InContainerElement:
00480       rv = OpenMember(aName,aAtts);
00481       break;
00482 
00483   case eRDFContentSinkState_InPropertyElement:
00484   case eRDFContentSinkState_InMemberElement:
00485       rv = OpenValue(aName,aAtts);
00486       break;
00487 
00488   case eRDFContentSinkState_InEpilog:
00489       PR_LOG(gLog, PR_LOG_WARNING,
00490              ("rdfxml: unexpected content in epilog at line %d",
00491               aLineNumber));
00492       break;
00493   }
00494 
00495   return rv;
00496 }
00497 
00498 NS_IMETHODIMP 
00499 RDFContentSinkImpl::HandleEndElement(const PRUnichar *aName)
00500 {
00501   FlushText();
00502 
00503   nsIRDFResource* resource;
00504   if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
00505       // XXX parser didn't catch unmatched tags?
00506 #ifdef PR_LOGGING
00507       if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) {
00508           nsAutoString tagStr(aName);
00509           char* tagCStr = ToNewCString(tagStr);
00510 
00511           PR_LogPrint
00512                  ("rdfxml: extra close tag '%s' at line %d",
00513                   tagCStr, 0/*XXX fix me */);
00514 
00515           NS_Free(tagCStr);
00516       }
00517 #endif
00518 
00519       return NS_ERROR_UNEXPECTED; // XXX
00520   }
00521 
00522   // If we've just popped a member or property element, _now_ is the
00523   // time to add that element to the graph.
00524   switch (mState) {
00525     case eRDFContentSinkState_InMemberElement: 
00526       {
00527         nsCOMPtr<nsIRDFContainer> container;
00528         NS_NewRDFContainer(getter_AddRefs(container));
00529         container->Init(mDataSource, GetContextElement(1));
00530         container->AppendElement(resource);
00531       } 
00532       break;
00533 
00534     case eRDFContentSinkState_InPropertyElement: 
00535       {
00536         mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, PR_TRUE);                                          
00537       } break;
00538     default:
00539       break;
00540   }
00541   
00542   PRInt32 nestLevel = mContextStack->Count();
00543   if (nestLevel == 0)
00544       mState = eRDFContentSinkState_InEpilog;
00545 
00546   NS_IF_RELEASE(resource);
00547   return NS_OK;
00548 }
00549  
00550 NS_IMETHODIMP 
00551 RDFContentSinkImpl::HandleComment(const PRUnichar *aName)
00552 {
00553     return NS_OK;
00554 }
00555 
00556 NS_IMETHODIMP 
00557 RDFContentSinkImpl::HandleCDataSection(const PRUnichar *aData, 
00558                                        PRUint32 aLength)
00559 {
00560   return aData ?  AddText(aData, aLength) : NS_OK;
00561 }
00562 
00563 NS_IMETHODIMP 
00564 RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, 
00565                                       const nsAString & aName, 
00566                                       const nsAString & aSystemId, 
00567                                       const nsAString & aPublicId,
00568                                       nsISupports* aCatalogData)
00569 {
00570     return NS_OK;
00571 }
00572 
00573 NS_IMETHODIMP 
00574 RDFContentSinkImpl::HandleCharacterData(const PRUnichar *aData, 
00575                                         PRUint32 aLength)
00576 {
00577   return aData ?  AddText(aData, aLength) : NS_OK;
00578 }
00579 
00580 NS_IMETHODIMP 
00581 RDFContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget, 
00582                                                 const PRUnichar *aData)
00583 {
00584     return NS_OK;
00585 }
00586 
00587 NS_IMETHODIMP 
00588 RDFContentSinkImpl::HandleXMLDeclaration(const PRUnichar *aVersion,
00589                                          const PRUnichar *aEncoding,
00590                                          PRInt32 aStandalone)
00591 {
00592     return NS_OK;
00593 }
00594 
00595 NS_IMETHODIMP
00596 RDFContentSinkImpl::ReportError(const PRUnichar* aErrorText, 
00597                                 const PRUnichar* aSourceText)
00598 {
00599   return NS_OK;
00600 }
00601 
00603 // nsIContentSink interface
00604 
00605 NS_IMETHODIMP 
00606 RDFContentSinkImpl::WillBuildModel(void)
00607 {
00608     if (mDataSource) {
00609         nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
00610         if (sink) 
00611             return sink->BeginLoad();
00612     }
00613     return NS_OK;
00614 }
00615 
00616 NS_IMETHODIMP 
00617 RDFContentSinkImpl::DidBuildModel(void)
00618 {
00619     if (mDataSource) {
00620         nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
00621         if (sink)
00622             return sink->EndLoad();
00623     }
00624     return NS_OK;
00625 }
00626 
00627 NS_IMETHODIMP 
00628 RDFContentSinkImpl::WillInterrupt(void)
00629 {
00630     if (mDataSource) {
00631         nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
00632         if (sink)
00633             return sink->Interrupt();
00634     }
00635     return NS_OK;
00636 }
00637 
00638 NS_IMETHODIMP 
00639 RDFContentSinkImpl::WillResume(void)
00640 {
00641     if (mDataSource) {
00642         nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
00643         if (sink)
00644             return sink->Resume();
00645     }
00646     return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP 
00650 RDFContentSinkImpl::SetParser(nsIParser* aParser)
00651 {
00652     return NS_OK;
00653 }
00654 
00656 // nsIRDFContentSink interface
00657 
00658 NS_IMETHODIMP
00659 RDFContentSinkImpl::Init(nsIURI* aURL)
00660 {
00661     NS_PRECONDITION(aURL != nsnull, "null ptr");
00662     if (! aURL)
00663         return NS_ERROR_NULL_POINTER;
00664 
00665     mDocumentURL = aURL;
00666     NS_ADDREF(aURL);
00667 
00668     mState = eRDFContentSinkState_InProlog;
00669     return NS_OK;
00670 }
00671 
00672 NS_IMETHODIMP
00673 RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource)
00674 {
00675     NS_PRECONDITION(aDataSource != nsnull, "SetDataSource null ptr");
00676     mDataSource = aDataSource;
00677     NS_ASSERTION(mDataSource != nsnull,"Couldn't QI RDF DataSource");
00678     return NS_OK;
00679 }
00680 
00681 
00682 NS_IMETHODIMP
00683 RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource)
00684 {
00685     aDataSource = mDataSource;
00686     NS_IF_ADDREF(aDataSource);
00687     return NS_OK;
00688 }
00689 
00691 // Text buffering
00692 
00693 static PRBool
00694 rdf_IsDataInBuffer(PRUnichar* buffer, PRInt32 length)
00695 {
00696     for (PRInt32 i = 0; i < length; ++i) {
00697         if (buffer[i] == ' ' ||
00698             buffer[i] == '\t' ||
00699             buffer[i] == '\n' ||
00700             buffer[i] == '\r')
00701             continue;
00702 
00703         return PR_TRUE;
00704     }
00705     return PR_FALSE;
00706 }
00707 
00708 void
00709 RDFContentSinkImpl::ParseText(nsIRDFNode **aResult)
00710 {
00711     // XXXwaterson wasteful, but we'd need to make a copy anyway to be
00712     // able to call nsIRDFService::Get[Resource|Literal|...]().
00713     nsAutoString value;
00714     value.Append(mText, mTextLength);
00715     value.Trim(" \t\n\r");
00716 
00717     switch (mParseMode) {
00718     case eRDFContentSinkParseMode_Literal:
00719         {
00720             nsIRDFLiteral *result;
00721             gRDFService->GetLiteral(value.get(), &result);
00722             *aResult = result;
00723         }
00724         break;
00725 
00726     case eRDFContentSinkParseMode_Resource:
00727         {
00728             nsIRDFResource *result;
00729             gRDFService->GetUnicodeResource(value, &result);
00730             *aResult = result;
00731         }
00732         break;
00733 
00734     case eRDFContentSinkParseMode_Int:
00735         {
00736             PRInt32 i, err;
00737             i = value.ToInteger(&err);
00738             nsIRDFInt *result;
00739             gRDFService->GetIntLiteral(i, &result);
00740             *aResult = result;
00741         }
00742         break;
00743 
00744     case eRDFContentSinkParseMode_Date:
00745         {
00746             PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUCS2toASCII(value).get(), value.Length()));
00747             nsIRDFDate *result;
00748             gRDFService->GetDateLiteral(t, &result);
00749             *aResult = result;
00750         }
00751         break;
00752 
00753     default:
00754         NS_NOTREACHED("unknown parse type");
00755         break;
00756     }
00757 }
00758 
00759 nsresult
00760 RDFContentSinkImpl::FlushText(PRBool aCreateTextNode, PRBool* aDidFlush)
00761 {
00762     nsresult rv = NS_OK;
00763     PRBool didFlush = PR_FALSE;
00764     if (0 != mTextLength) {
00765         if (aCreateTextNode && rdf_IsDataInBuffer(mText, mTextLength)) {
00766             // XXX if there's anything but whitespace, then we'll
00767             // create a text node.
00768 
00769             switch (mState) {
00770             case eRDFContentSinkState_InMemberElement: {
00771                 nsCOMPtr<nsIRDFNode> node;
00772                 ParseText(getter_AddRefs(node));
00773 
00774                 nsCOMPtr<nsIRDFContainer> container;
00775                 NS_NewRDFContainer(getter_AddRefs(container));
00776                 container->Init(mDataSource, GetContextElement(1));
00777 
00778                 container->AppendElement(node);
00779             } break;
00780 
00781             case eRDFContentSinkState_InPropertyElement: {
00782                 nsCOMPtr<nsIRDFNode> node;
00783                 ParseText(getter_AddRefs(node));
00784 
00785                 mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, PR_TRUE);
00786             } break;
00787 
00788             default:
00789                 // just ignore it
00790                 break;
00791             }
00792         }
00793         mTextLength = 0;
00794         didFlush = PR_TRUE;
00795     }
00796     if (nsnull != aDidFlush) {
00797         *aDidFlush = didFlush;
00798     }
00799     return rv;
00800 }
00801 
00802 
00803 nsresult
00804 RDFContentSinkImpl::AddText(const PRUnichar* aText, PRInt32 aLength)
00805 {
00806     // Create buffer when we first need it
00807     if (0 == mTextSize) {
00808         mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * 4096);
00809         if (!mText) {
00810             return NS_ERROR_OUT_OF_MEMORY;
00811         }
00812         mTextSize = 4096;
00813     }
00814 
00815     // Copy data from string into our buffer; grow the buffer as needed.
00816     // It never shrinks, but since the content sink doesn't stick around,
00817     // this shouldn't be a bloat issue.
00818     PRInt32 amount = mTextSize - mTextLength;
00819     if (amount < aLength) {
00820         // Grow the buffer by at least a factor of two to prevent thrashing.
00821         // Since PR_REALLOC will leave mText intact if the call fails,
00822         // don't clobber mText or mTextSize until the new mem is allocated.
00823         PRInt32 newSize = (2 * mTextSize > (mTextSize + aLength)) ?
00824                           (2 * mTextSize) : (mTextSize + aLength);
00825         PRUnichar* newText = 
00826             (PRUnichar *) PR_REALLOC(mText, sizeof(PRUnichar) * newSize);
00827         if (!newText)
00828             return NS_ERROR_OUT_OF_MEMORY;
00829         mTextSize = newSize;
00830         mText = newText;
00831     }
00832     memcpy(&mText[mTextLength], aText, sizeof(PRUnichar) * aLength);
00833     mTextLength += aLength;
00834 
00835     return NS_OK;
00836 }
00837 
00838 nsresult
00839 RDFContentSinkImpl::GetIdAboutAttribute(const PRUnichar** aAttributes,
00840                                         nsIRDFResource** aResource,
00841                                         PRBool* aIsAnonymous)
00842 {
00843     // This corresponds to the dirty work of production [6.5]
00844     nsresult rv = NS_OK;
00845 
00846     nsCAutoString docURI;
00847     rv = mDocumentURL->GetSpec(docURI);
00848     if (NS_FAILED(rv)) return rv;
00849 
00850     nsAutoString nodeID;
00851 
00852     nsCOMPtr<nsIAtom> localName;
00853     for (; *aAttributes; aAttributes += 2) {
00854         const nsDependentSubstring& nameSpaceURI =
00855             SplitExpatName(aAttributes[0], getter_AddRefs(localName));
00856 
00857         // We'll accept either `ID' or `rdf:ID' (ibid with `about' or
00858         // `rdf:about') in the spirit of being liberal towards the
00859         // input that we receive.
00860         if (!nameSpaceURI.IsEmpty() &&
00861             !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
00862           continue;
00863         }
00864 
00865         // XXX you can't specify both, but we'll just pick up the
00866         // first thing that was specified and ignore the other.
00867       
00868         if (localName == kAboutAtom) {
00869             if (aIsAnonymous)
00870                 *aIsAnonymous = PR_FALSE;
00871 
00872             nsAutoString uri(aAttributes[1]);
00873             nsRDFParserUtils::StripAndConvert(uri);
00874 
00875             rdf_MakeAbsoluteURI(NS_ConvertUTF8toUCS2(docURI), uri);
00876 
00877             return gRDFService->GetUnicodeResource(uri, aResource);
00878         }
00879         else if (localName == kIdAtom) {
00880             if (aIsAnonymous)
00881                 *aIsAnonymous = PR_FALSE;
00882 
00883             nsAutoString name(aAttributes[1]);
00884             nsRDFParserUtils::StripAndConvert(name);
00885 
00886             // In the spirit of leniency, we do not bother trying to
00887             // enforce that this be a valid "XML Name" (see
00888             // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
00889             // 6.21. If we wanted to, this would be where to do it.
00890 
00891             // Construct an in-line resource whose URI is the
00892             // document's URI plus the XML name specified in the ID
00893             // attribute.
00894             name.Insert(PRUnichar('#'), 0);
00895           
00896             rdf_MakeAbsoluteURI(NS_ConvertUTF8toUCS2(docURI), name);
00897 
00898             return gRDFService->GetUnicodeResource(name, aResource);
00899         }
00900         else if (localName == kNodeIdAtom) {
00901             nodeID.Assign(aAttributes[1]);
00902         }
00903         else if (localName == kAboutEachAtom) {
00904             // XXX we don't deal with aboutEach...
00905             //PR_LOG(gLog, PR_LOG_WARNING,
00906             //       ("rdfxml: ignoring aboutEach at line %d",
00907             //        aNode.GetSourceLineNumber()));
00908         }
00909     }
00910 
00911     // Otherwise, we couldn't find anything, so just gensym one...
00912     if (aIsAnonymous)
00913         *aIsAnonymous = PR_TRUE;
00914 
00915     // If nodeID is present, check if we already know about it. If we've seen
00916     // the nodeID before, use the same resource, otherwise generate a new one.
00917     if (!nodeID.IsEmpty()) {
00918         mNodeIDMap.Get(nodeID,aResource);
00919 
00920         if (!*aResource) {
00921             rv = gRDFService->GetAnonymousResource(aResource);
00922             mNodeIDMap.Put(nodeID,*aResource);
00923         }
00924     }
00925     else {
00926         rv = gRDFService->GetAnonymousResource(aResource);
00927     }
00928 
00929     return rv;
00930 }
00931 
00932 nsresult
00933 RDFContentSinkImpl::GetResourceAttribute(const PRUnichar** aAttributes,
00934                                          nsIRDFResource** aResource)
00935 {
00936   nsCOMPtr<nsIAtom> localName;
00937 
00938   nsAutoString nodeID;
00939 
00940   for (; *aAttributes; aAttributes += 2) {
00941       const nsDependentSubstring& nameSpaceURI =
00942           SplitExpatName(aAttributes[0], getter_AddRefs(localName));
00943 
00944       // We'll accept `resource' or `rdf:resource', under the spirit
00945       // that we should be liberal towards the input that we
00946       // receive.
00947       if (!nameSpaceURI.IsEmpty() &&
00948           !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
00949           continue;
00950       }
00951 
00952       // XXX you can't specify both, but we'll just pick up the
00953       // first thing that was specified and ignore the other.
00954 
00955       if (localName == kResourceAtom) {
00956           nsAutoString uri(aAttributes[1]);
00957           nsRDFParserUtils::StripAndConvert(uri);
00958 
00959           // XXX Take the URI and make it fully qualified by
00960           // sticking it into the document's URL. This may not be
00961           // appropriate...
00962           nsCAutoString documentURL;
00963           mDocumentURL->GetSpec(documentURL);
00964           rdf_MakeAbsoluteURI(NS_ConvertUTF8toUCS2(documentURL), uri);
00965 
00966           return gRDFService->GetUnicodeResource(uri, aResource);
00967       }
00968       else if (localName == kNodeIdAtom) {
00969           nodeID.Assign(aAttributes[1]);
00970       }
00971   }
00972 
00973   // If nodeID is present, check if we already know about it. If we've seen
00974   // the nodeID before, use the same resource, otherwise generate a new one.
00975   if (!nodeID.IsEmpty()) {
00976       mNodeIDMap.Get(nodeID,aResource);
00977 
00978       if (!*aResource) {
00979           mNodeIDMap.Put(nodeID,*aResource);
00980           return gRDFService->GetAnonymousResource(aResource);
00981       }
00982       return NS_OK;
00983   }
00984 
00985   return NS_ERROR_FAILURE;
00986 }
00987 
00988 nsresult
00989 RDFContentSinkImpl::AddProperties(const PRUnichar** aAttributes,
00990                                   nsIRDFResource* aSubject,
00991                                   PRInt32* aCount)
00992 {
00993   if (aCount)
00994       *aCount = 0;
00995 
00996   nsCOMPtr<nsIAtom> localName;
00997   for (; *aAttributes; aAttributes += 2) {
00998       const nsDependentSubstring& nameSpaceURI =
00999           SplitExpatName(aAttributes[0], getter_AddRefs(localName));
01000 
01001       // skip 'xmlns' directives, these are "meta" information
01002       if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) {
01003         continue;
01004       }
01005 
01006       // skip `about', `ID', `resource', and 'nodeID' attributes (either with or
01007       // without the `rdf:' prefix); these are all "special" and
01008       // should've been dealt with by the caller.
01009       if (localName == kAboutAtom || localName == kIdAtom ||
01010           localName == kResourceAtom || localName == kNodeIdAtom) {
01011           if (nameSpaceURI.IsEmpty() ||
01012               nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI))
01013               continue;
01014       }
01015 
01016       // Skip `parseType', `RDF:parseType', and `NC:parseType'. This
01017       // is meta-information that will be handled in SetParseMode.
01018       if (localName == kParseTypeAtom) {
01019           if (nameSpaceURI.IsEmpty() ||
01020               nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
01021               nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
01022               continue;
01023           }
01024       }
01025 
01026       nsAutoString v(aAttributes[1]);
01027       nsRDFParserUtils::StripAndConvert(v);
01028 
01029       const char* attrName;
01030       localName->GetUTF8String(&attrName);
01031 
01032       NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);    
01033       propertyStr.Append(attrName);
01034 
01035       // Add the assertion to RDF
01036       nsCOMPtr<nsIRDFResource> property;
01037       gRDFService->GetResource(propertyStr, getter_AddRefs(property));
01038 
01039       nsCOMPtr<nsIRDFLiteral> target;
01040       gRDFService->GetLiteral(v.get(), getter_AddRefs(target));
01041 
01042       mDataSource->Assert(aSubject, property, target, PR_TRUE);
01043   }
01044   return NS_OK;
01045 }
01046 
01047 void
01048 RDFContentSinkImpl::SetParseMode(const PRUnichar **aAttributes)
01049 {
01050     nsCOMPtr<nsIAtom> localName;
01051     for (; *aAttributes; aAttributes += 2) {
01052         const nsDependentSubstring& nameSpaceURI =
01053             SplitExpatName(aAttributes[0], getter_AddRefs(localName));
01054 
01055         if (localName == kParseTypeAtom) {
01056             nsAutoString v(aAttributes[1]);
01057             nsRDFParserUtils::StripAndConvert(v);
01058 
01059             if (nameSpaceURI.IsEmpty() ||
01060                 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
01061                 if (v.EqualsLiteral("Resource"))
01062                     mParseMode = eRDFContentSinkParseMode_Resource;
01063 
01064                 break;
01065             }
01066             else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
01067                 if (v.EqualsLiteral("Date"))
01068                     mParseMode = eRDFContentSinkParseMode_Date;
01069                 else if (v.EqualsLiteral("Integer"))
01070                     mParseMode = eRDFContentSinkParseMode_Int;
01071 
01072                 break;
01073             }
01074         }
01075     }
01076 }
01077 
01079 // RDF-specific routines used to build the model
01080 
01081 nsresult
01082 RDFContentSinkImpl::OpenRDF(const PRUnichar* aName)
01083 {
01084     // ensure that we're actually reading RDF by making sure that the
01085     // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever
01086     // they've declared the standard RDF namespace to be.
01087     nsCOMPtr<nsIAtom> localName;
01088     const nsDependentSubstring& nameSpaceURI =
01089         SplitExpatName(aName, getter_AddRefs(localName));
01090 
01091     if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) {
01092        // PR_LOG(gLog, PR_LOG_ALWAYS,
01093        //        ("rdfxml: expected RDF:RDF at line %d",
01094        //         aNode.GetSourceLineNumber()));
01095 
01096         return NS_ERROR_UNEXPECTED;
01097     }
01098 
01099     PushContext(nsnull, mState, mParseMode);
01100     mState = eRDFContentSinkState_InDocumentElement;
01101     return NS_OK;
01102 }
01103 
01104 nsresult
01105 RDFContentSinkImpl::OpenObject(const PRUnichar* aName, 
01106                                const PRUnichar** aAttributes)
01107 {
01108     // an "object" non-terminal is either a "description", a "typed
01109     // node", or a "container", so this change the content sink's
01110     // state appropriately.
01111     nsCOMPtr<nsIAtom> localName;
01112     const nsDependentSubstring& nameSpaceURI =
01113         SplitExpatName(aName, getter_AddRefs(localName));
01114 
01115     // Figure out the URI of this object, and create an RDF node for it.
01116     nsCOMPtr<nsIRDFResource> source;
01117     GetIdAboutAttribute(aAttributes, getter_AddRefs(source));
01118 
01119     // If there is no `ID' or `about', then there's not much we can do.
01120     if (! source)
01121         return NS_ERROR_FAILURE;
01122 
01123     // Push the element onto the context stack
01124     PushContext(source, mState, mParseMode);
01125 
01126     // Now figure out what kind of state transition we need to
01127     // make. We'll either be going into a mode where we parse a
01128     // description or a container.
01129     PRBool isaTypedNode = PR_TRUE;
01130 
01131     if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
01132         isaTypedNode = PR_FALSE;
01133 
01134         if (localName == kDescriptionAtom) {
01135             // it's a description
01136             mState = eRDFContentSinkState_InDescriptionElement;
01137         }
01138         else if (localName == kBagAtom) {
01139             // it's a bag container
01140             InitContainer(kRDF_Bag, source);
01141             mState = eRDFContentSinkState_InContainerElement;
01142         }
01143         else if (localName == kSeqAtom) {
01144             // it's a seq container
01145             InitContainer(kRDF_Seq, source);
01146             mState = eRDFContentSinkState_InContainerElement;
01147         }
01148         else if (localName == kAltAtom) {
01149             // it's an alt container
01150             InitContainer(kRDF_Alt, source);
01151             mState = eRDFContentSinkState_InContainerElement;
01152         }
01153         else {
01154             // heh, that's not *in* the RDF namespace: just treat it
01155             // like a typed node
01156             isaTypedNode = PR_TRUE;
01157         }
01158     }
01159 
01160     if (isaTypedNode) {
01161         const char* attrName;
01162         localName->GetUTF8String(&attrName);
01163 
01164         NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI);
01165         typeStr.Append(attrName);
01166 
01167         nsCOMPtr<nsIRDFResource> type;
01168         nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type));
01169         if (NS_FAILED(rv)) return rv;
01170 
01171         rv = mDataSource->Assert(source, kRDF_type, type, PR_TRUE);
01172         if (NS_FAILED(rv)) return rv;
01173 
01174         mState = eRDFContentSinkState_InDescriptionElement;
01175     }
01176 
01177     AddProperties(aAttributes, source);
01178     return NS_OK;
01179 }
01180 
01181 nsresult
01182 RDFContentSinkImpl::OpenProperty(const PRUnichar* aName, const PRUnichar** aAttributes)
01183 {
01184     nsresult rv;
01185 
01186     // an "object" non-terminal is either a "description", a "typed
01187     // node", or a "container", so this change the content sink's
01188     // state appropriately.
01189     nsCOMPtr<nsIAtom> localName;
01190     const nsDependentSubstring& nameSpaceURI =
01191         SplitExpatName(aName, getter_AddRefs(localName));
01192 
01193     const char* attrName;
01194     localName->GetUTF8String(&attrName);
01195 
01196     NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
01197     propertyStr.Append(attrName);
01198 
01199     nsCOMPtr<nsIRDFResource> property;
01200     rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property));
01201     if (NS_FAILED(rv)) return rv;
01202 
01203     // See if they've specified a 'resource' attribute, in which case
01204     // they mean *that* to be the object of this property.
01205     nsCOMPtr<nsIRDFResource> target;
01206     GetResourceAttribute(aAttributes, getter_AddRefs(target));
01207 
01208     PRBool isAnonymous = PR_FALSE;
01209 
01210     if (! target) {
01211         // See if an 'ID' attribute has been specified, in which case
01212         // this corresponds to the fourth form of [6.12].
01213 
01214         // XXX strictly speaking, we should reject the RDF/XML as
01215         // invalid if they've specified both an 'ID' and a 'resource'
01216         // attribute. Bah.
01217 
01218         // XXX strictly speaking, 'about=' isn't allowed here, but
01219         // what the hell.
01220         GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous);
01221     }
01222 
01223     if (target) {
01224         // They specified an inline resource for the value of this
01225         // property. Create an RDF resource for the inline resource
01226         // URI, add the properties to it, and attach the inline
01227         // resource to its parent.
01228         PRInt32 count;
01229         rv = AddProperties(aAttributes, target, &count);
01230         NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties");
01231         if (NS_FAILED(rv)) return rv;
01232 
01233         if (count || !isAnonymous) {
01234             // If the resource was "anonymous" (i.e., they hadn't
01235             // explicitly set an ID or resource attribute), then we'll
01236             // only assert this property from the context element *if*
01237             // there were properties specified on the anonymous
01238             // resource.
01239             rv = mDataSource->Assert(GetContextElement(0), property, target, PR_TRUE);
01240             if (NS_FAILED(rv)) return rv;
01241         }
01242 
01243         // XXX Technically, we should _not_ fall through here and push
01244         // the element onto the stack: this is supposed to be a closed
01245         // node. But right now I'm lazy and the code will just Do The
01246         // Right Thing so long as the RDF is well-formed.
01247     }
01248 
01249     // Push the element onto the context stack and change state.
01250     PushContext(property, mState, mParseMode);
01251     mState = eRDFContentSinkState_InPropertyElement;
01252     SetParseMode(aAttributes);
01253 
01254     return NS_OK;
01255 }
01256 
01257 nsresult
01258 RDFContentSinkImpl::OpenMember(const PRUnichar* aName, 
01259                                const PRUnichar** aAttributes)
01260 {
01261     // ensure that we're actually reading a member element by making
01262     // sure that the opening tag is <rdf:li>, where "rdf:" corresponds
01263     // to whatever they've declared the standard RDF namespace to be.
01264     nsresult rv;
01265 
01266     nsCOMPtr<nsIAtom> localName;
01267     const nsDependentSubstring& nameSpaceURI =
01268         SplitExpatName(aName, getter_AddRefs(localName));
01269 
01270     if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
01271         localName != kLiAtom) {
01272         PR_LOG(gLog, PR_LOG_ALWAYS,
01273                ("rdfxml: expected RDF:li at line %d",
01274                 -1)); // XXX pass in line number
01275 
01276         return NS_ERROR_UNEXPECTED;
01277     }
01278 
01279     // The parent element is the container.
01280     nsIRDFResource* container = GetContextElement(0);
01281     if (! container)
01282         return NS_ERROR_NULL_POINTER;
01283 
01284     nsIRDFResource* resource;
01285     if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) {
01286         // Okay, this node has an RDF:resource="..." attribute. That
01287         // means that it's a "referenced item," as covered in [6.29].
01288         nsCOMPtr<nsIRDFContainer> c;
01289         NS_NewRDFContainer(getter_AddRefs(c));
01290         c->Init(mDataSource, container);
01291         c->AppendElement(resource);
01292 
01293         // XXX Technically, we should _not_ fall through here and push
01294         // the element onto the stack: this is supposed to be a closed
01295         // node. But right now I'm lazy and the code will just Do The
01296         // Right Thing so long as the RDF is well-formed.
01297         NS_RELEASE(resource);
01298     }
01299 
01300     // Change state. Pushing a null context element is a bit weird,
01301     // but the idea is that there really is _no_ context "property".
01302     // The contained element will use nsIRDFContainer::AppendElement() to add
01303     // the element to the container, which requires only the container
01304     // and the element to be added.
01305     PushContext(nsnull, mState, mParseMode);
01306     mState = eRDFContentSinkState_InMemberElement;
01307     SetParseMode(aAttributes);
01308 
01309     return NS_OK;
01310 }
01311 
01312 
01313 nsresult
01314 RDFContentSinkImpl::OpenValue(const PRUnichar* aName, const PRUnichar** aAttributes)
01315 {
01316     // a "value" can either be an object or a string: we'll only get
01317     // *here* if it's an object, as raw text is added as a leaf.
01318     return OpenObject(aName,aAttributes);
01319 }
01320 
01322 // namespace resolution
01323 void
01324 RDFContentSinkImpl::RegisterNamespaces(const PRUnichar **aAttributes)
01325 {
01326     nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
01327     if (!sink) {
01328         return;
01329     }
01330     NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/");
01331     for (; *aAttributes; aAttributes += 2) {
01332         // check the namespace
01333         const PRUnichar* attr = aAttributes[0];
01334         const PRUnichar* xmlnsP = xmlns.BeginReading();
01335         while (*attr ==  *xmlnsP) {
01336             ++attr;
01337             ++xmlnsP;
01338         }
01339         if (*attr != 0xFFFF ||
01340             xmlnsP != xmlns.EndReading()) {
01341             continue;
01342         }
01343         // get the localname (or "xmlns" for the default namespace)
01344         const PRUnichar* endLocal = ++attr;
01345         while (*endLocal && *endLocal != 0xFFFF) {
01346             ++endLocal;
01347         }
01348         nsDependentSubstring lname(attr, endLocal);
01349         nsCOMPtr<nsIAtom> preferred = do_GetAtom(lname);
01350         if (preferred == kXMLNSAtom) {
01351             preferred = nsnull;
01352         }
01353         sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
01354     }
01355 }
01356 
01358 // Qualified name resolution
01359 
01360 const nsDependentSubstring
01361 RDFContentSinkImpl::SplitExpatName(const PRUnichar *aExpatName,
01362                                    nsIAtom **aLocalName)
01363 {
01374     const PRUnichar *uriEnd = aExpatName;
01375     const PRUnichar *nameStart = aExpatName;
01376     const PRUnichar *pos;
01377     for (pos = aExpatName; *pos; ++pos) {
01378         if (*pos == 0xFFFF) {
01379             if (uriEnd != aExpatName) {
01380                 break;
01381             }
01382 
01383             uriEnd = pos;
01384             nameStart = pos + 1;
01385         }
01386     }
01387 
01388     const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd);
01389     *aLocalName = NS_NewAtom(NS_ConvertUTF16toUTF8(nameStart,
01390                                                    pos - nameStart));
01391     return nameSpaceURI;
01392 }
01393 
01394 nsresult
01395 RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
01396 {
01397     // Do the right kind of initialization based on the container
01398     // 'type' resource, and the state of the container (i.e., 'make' a
01399     // new container vs. 'reinitialize' the container).
01400     nsresult rv;
01401 
01402     static const ContainerInfo gContainerInfo[] = {
01403         { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt },
01404         { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag },
01405         { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq },
01406         { 0, 0, 0 },
01407     };
01408 
01409     for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) {
01410         if (*info->mType != aContainerType)
01411             continue;
01412 
01413         PRBool isContainer;
01414         rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer);
01415         if (isContainer) {
01416             rv = ReinitContainer(aContainerType, aContainer);
01417         }
01418         else {
01419             rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nsnull);
01420         }
01421         return rv;
01422     }
01423 
01424     NS_NOTREACHED("not an RDF container type");
01425     return NS_ERROR_FAILURE;
01426 }
01427 
01428 
01429 
01430 nsresult
01431 RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
01432 {
01433     // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is
01434     // idempotent, and as such, containers will have state (e.g.,
01435     // RDF:nextVal) maintained in the graph across loads. This
01436     // re-initializes each container's RDF:nextVal to '1', and 'marks'
01437     // the container as such.
01438     nsresult rv;
01439 
01440     nsCOMPtr<nsIRDFLiteral> one;
01441     rv = gRDFService->GetLiteral(NS_LITERAL_STRING("1").get(), getter_AddRefs(one));
01442     if (NS_FAILED(rv)) return rv;
01443 
01444     // Re-initialize the 'nextval' property
01445     nsCOMPtr<nsIRDFNode> nextval;
01446     rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, PR_TRUE, getter_AddRefs(nextval));
01447     if (NS_FAILED(rv)) return rv;
01448 
01449     rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one);
01450     if (NS_FAILED(rv)) return rv;
01451 
01452     // Re-mark as a container. XXX should be kRDF_type
01453     rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, PR_TRUE);
01454     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such");
01455     if (NS_FAILED(rv)) return rv;
01456 
01457     return NS_OK;
01458 }
01459 
01461 // Content stack management
01462 
01463 struct RDFContextStackElement {
01464     nsIRDFResource*         mResource;
01465     RDFContentSinkState     mState;
01466     RDFContentSinkParseMode mParseMode;
01467 };
01468 
01469 nsIRDFResource* 
01470 RDFContentSinkImpl::GetContextElement(PRInt32 ancestor /* = 0 */)
01471 {
01472     if ((nsnull == mContextStack) ||
01473         (ancestor >= mContextStack->Count())) {
01474         return nsnull;
01475     }
01476 
01477     RDFContextStackElement* e =
01478         NS_STATIC_CAST(RDFContextStackElement*, mContextStack->ElementAt(mContextStack->Count()-ancestor-1));
01479 
01480     return e->mResource;
01481 }
01482 
01483 PRInt32 
01484 RDFContentSinkImpl::PushContext(nsIRDFResource         *aResource,
01485                                 RDFContentSinkState     aState,
01486                                 RDFContentSinkParseMode aParseMode)
01487 {
01488     if (! mContextStack) {
01489         mContextStack = new nsAutoVoidArray();
01490         if (! mContextStack)
01491             return 0;
01492     }
01493 
01494     RDFContextStackElement* e = new RDFContextStackElement;
01495     if (! e)
01496         return mContextStack->Count();
01497 
01498     NS_IF_ADDREF(aResource);
01499     e->mResource  = aResource;
01500     e->mState     = aState;
01501     e->mParseMode = aParseMode;
01502   
01503     mContextStack->AppendElement(NS_STATIC_CAST(void*, e));
01504     return mContextStack->Count();
01505 }
01506  
01507 nsresult
01508 RDFContentSinkImpl::PopContext(nsIRDFResource         *&aResource,
01509                                RDFContentSinkState     &aState,
01510                                RDFContentSinkParseMode &aParseMode)
01511 {
01512     RDFContextStackElement* e;
01513     if ((nsnull == mContextStack) ||
01514         (0 == mContextStack->Count())) {
01515         return NS_ERROR_NULL_POINTER;
01516     }
01517 
01518     PRInt32 i = mContextStack->Count() - 1;
01519     e = NS_STATIC_CAST(RDFContextStackElement*, mContextStack->ElementAt(i));
01520     mContextStack->RemoveElementAt(i);
01521 
01522     // don't bother Release()-ing: call it our implicit AddRef().
01523     aResource  = e->mResource;
01524     aState     = e->mState;
01525     aParseMode = e->mParseMode;
01526 
01527     delete e;
01528     return NS_OK;
01529 }
01530  
01531 
01533 
01534 nsresult
01535 NS_NewRDFContentSink(nsIRDFContentSink** aResult)
01536 {
01537     NS_PRECONDITION(aResult != nsnull, "null ptr");
01538     if (! aResult)
01539         return NS_ERROR_NULL_POINTER;
01540 
01541     RDFContentSinkImpl* sink = new RDFContentSinkImpl();
01542     if (! sink)
01543         return NS_ERROR_OUT_OF_MEMORY;
01544 
01545     NS_ADDREF(sink);
01546     *aResult = sink;
01547     return NS_OK;
01548 }