Back to index

lightning-sunbird  0.9+nobinonly
nsRDFXMLDataSource.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.org 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   A data source that can read itself from and write itself to an
00042   RDF/XML stream.
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 
00051   TO DO
00052   -----
00053 
00054   1) Right now, the only kind of stream data sources that are _really_
00055      writable are "file:" URIs. (In fact, _all_ "file:" URIs are
00056      writable, modulo file system permissions; this may lead to some
00057      surprising behavior.) Eventually, it'd be great if we could open
00058      an arbitrary nsIOutputStream on *any* URL, and Netlib could just
00059      do the magic.
00060 
00061   2) Implement a more terse output for "typed" nodes; that is, instead
00062      of "RDF:Description type='ns:foo'", just output "ns:foo".
00063 
00064   3) When re-serializing, we "cheat" for Descriptions that talk about
00065      inline resources (i.e.., using the `ID' attribute specified in
00066      [6.21]). Instead of writing an `ID="foo"' for the first instance,
00067      and then `about="#foo"' for each subsequent instance, we just
00068      _always_ write `about="#foo"'.
00069 
00070      We do this so that we can handle the case where an RDF container
00071      has been assigned arbitrary properties: the spec says we can't
00072      dangle the attributes directly off the container, so we need to
00073      refer to it. Of course, with a little cleverness, we could fix
00074      this. But who cares?
00075 
00076   4) When re-serializing containers. We have to cheat on some
00077      containers, and use an illegal "about=" construct. We do this to
00078      handle containers that have been assigned URIs outside of the
00079      local document.
00080 
00081 
00082   Logging
00083   -------
00084 
00085   To turn on logging for this module, set
00086 
00087     NSPR_LOG_MODULES=nsRDFXMLDataSource:5
00088 
00089  */
00090 
00091 #include "nsIFileStreams.h"
00092 #include "nsIOutputStream.h"
00093 #include "nsIFile.h"
00094 #include "nsIFileChannel.h"
00095 #include "nsIDTD.h"
00096 #include "nsIRDFPurgeableDataSource.h"
00097 #include "nsIInputStream.h"
00098 #include "nsIOutputStream.h"
00099 #include "nsIRDFContainerUtils.h"
00100 #include "nsIRDFNode.h"
00101 #include "nsIRDFRemoteDataSource.h"
00102 #include "nsIRDFService.h"
00103 #include "nsIRDFXMLParser.h"
00104 #include "nsIRDFXMLSerializer.h"
00105 #include "nsIRDFXMLSink.h"
00106 #include "nsIRDFXMLSource.h"
00107 #include "nsIServiceManager.h"
00108 #include "nsIStreamListener.h"
00109 #include "nsIURL.h"
00110 #include "nsIFileURL.h"
00111 #include "nsNetUtil.h"
00112 #include "nsIChannel.h"
00113 #include "nsRDFCID.h"
00114 #include "nsRDFBaseDataSources.h"
00115 #include "nsCOMArray.h"
00116 #include "nsXPIDLString.h"
00117 #include "plstr.h"
00118 #include "prio.h"
00119 #include "prthread.h"
00120 #include "rdf.h"
00121 #include "rdfutil.h"
00122 #include "prlog.h"
00123 #include "nsNameSpaceMap.h"
00124 #include "nsCRT.h"
00125 
00126 #include "rdfIDataSource.h"
00127 
00128 //----------------------------------------------------------------------
00129 
00130 static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
00131 static NS_DEFINE_CID(kRDFServiceCID,            NS_RDFSERVICE_CID);
00132 
00133 #ifdef PR_LOGGING
00134 static PRLogModuleInfo* gLog;
00135 #endif
00136 
00137 //----------------------------------------------------------------------
00138 //
00139 // ProxyStream
00140 //
00141 //   A silly little stub class that lets us do blocking parses
00142 //
00143 
00144 class ProxyStream : public nsIInputStream
00145 {
00146 private:
00147     const char* mBuffer;
00148     PRUint32    mSize;
00149     PRUint32    mIndex;
00150 
00151 public:
00152     ProxyStream(void) : mBuffer(nsnull)
00153     {
00154     }
00155 
00156     virtual ~ProxyStream(void) {
00157     }
00158 
00159     // nsISupports
00160     NS_DECL_ISUPPORTS
00161 
00162     // nsIBaseStream
00163     NS_IMETHOD Close(void) {
00164         return NS_OK;
00165     }
00166 
00167     // nsIInputStream
00168     NS_IMETHOD Available(PRUint32 *aLength) {
00169         *aLength = mSize - mIndex;
00170         return NS_OK;
00171     }
00172 
00173     NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount) {
00174         PRUint32 readCount = PR_MIN(aCount, (mSize-mIndex));
00175         
00176         memcpy(aBuf, mBuffer+mIndex, readCount);
00177         mIndex += readCount;
00178         
00179         *aReadCount = readCount;
00180         
00181         return NS_OK;
00182     }
00183 
00184     NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval) {
00185         PRUint32 readCount = PR_MIN(count, (mSize-mIndex));
00186 
00187         *_retval = 0;
00188         nsresult rv = writer (this, closure, mBuffer+mIndex, mIndex, readCount, _retval);
00189         if (NS_SUCCEEDED(rv))
00190             mIndex += *_retval;
00191 
00192         // do not propogate errors returned from writer!
00193         return NS_OK;
00194     }
00195 
00196     NS_IMETHOD IsNonBlocking(PRBool *aNonBlocking) {
00197         *aNonBlocking = PR_TRUE;
00198         return NS_OK;
00199     }
00200 
00201     // Implementation
00202     void SetBuffer(const char* aBuffer, PRUint32 aSize) {
00203         mBuffer = aBuffer;
00204         mSize = aSize;
00205         mIndex = 0;
00206     }
00207 };
00208 
00209 NS_IMPL_ISUPPORTS1(ProxyStream, nsIInputStream)
00210 
00211 //----------------------------------------------------------------------
00212 //
00213 // RDFXMLDataSourceImpl
00214 //
00215 
00216 class RDFXMLDataSourceImpl : public nsIRDFDataSource,
00217                              public nsIRDFRemoteDataSource,
00218                              public nsIRDFXMLSink,
00219                              public nsIRDFXMLSource,
00220                              public nsIStreamListener,
00221                              public rdfIDataSource
00222 {
00223 protected:
00224     enum LoadState {
00225         eLoadState_Unloaded,
00226         eLoadState_Pending,
00227         eLoadState_Loading,
00228         eLoadState_Loaded
00229     };
00230 
00231     nsIRDFDataSource*   mInner;         // OWNER
00232     PRPackedBool        mIsWritable;    // true if the document can be written back
00233     PRPackedBool        mIsDirty;       // true if the document should be written back
00234     LoadState           mLoadState;     // what we're doing now
00235     nsCOMArray<nsIRDFXMLSinkObserver> mObservers;
00236     nsCOMPtr<nsIURI>    mURL;
00237     nsCOMPtr<nsIStreamListener> mListener;
00238     nsNameSpaceMap      mNameSpaces;
00239 
00240     // pseudo-constants
00241     static PRInt32 gRefCnt;
00242     static nsIRDFService* gRDFService;
00243 
00244     nsresult Init();
00245     RDFXMLDataSourceImpl(void);
00246     virtual ~RDFXMLDataSourceImpl(void);
00247     nsresult rdfXMLFlush(nsIURI *aURI);
00248 
00249     friend nsresult
00250     NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult);
00251 
00252     inline PRBool IsLoading() {
00253         return (mLoadState == eLoadState_Pending) || 
00254                (mLoadState == eLoadState_Loading);
00255     }
00256 
00257 public:
00258     // nsISupports
00259     NS_DECL_ISUPPORTS
00260 
00261     // nsIRDFDataSource
00262     NS_IMETHOD GetURI(char* *uri);
00263 
00264     NS_IMETHOD GetSource(nsIRDFResource* property,
00265                          nsIRDFNode* target,
00266                          PRBool tv,
00267                          nsIRDFResource** source) {
00268         return mInner->GetSource(property, target, tv, source);
00269     }
00270 
00271     NS_IMETHOD GetSources(nsIRDFResource* property,
00272                           nsIRDFNode* target,
00273                           PRBool tv,
00274                           nsISimpleEnumerator** sources) {
00275         return mInner->GetSources(property, target, tv, sources);
00276     }
00277 
00278     NS_IMETHOD GetTarget(nsIRDFResource* source,
00279                          nsIRDFResource* property,
00280                          PRBool tv,
00281                          nsIRDFNode** target) {
00282         return mInner->GetTarget(source, property, tv, target);
00283     }
00284 
00285     NS_IMETHOD GetTargets(nsIRDFResource* source,
00286                           nsIRDFResource* property,
00287                           PRBool tv,
00288                           nsISimpleEnumerator** targets) {
00289         return mInner->GetTargets(source, property, tv, targets);
00290     }
00291 
00292     NS_IMETHOD Assert(nsIRDFResource* aSource,
00293                       nsIRDFResource* aProperty,
00294                       nsIRDFNode* aTarget,
00295                       PRBool tv);
00296 
00297     NS_IMETHOD Unassert(nsIRDFResource* source,
00298                         nsIRDFResource* property,
00299                         nsIRDFNode* target);
00300 
00301     NS_IMETHOD Change(nsIRDFResource* aSource,
00302                       nsIRDFResource* aProperty,
00303                       nsIRDFNode* aOldTarget,
00304                       nsIRDFNode* aNewTarget);
00305 
00306     NS_IMETHOD Move(nsIRDFResource* aOldSource,
00307                     nsIRDFResource* aNewSource,
00308                     nsIRDFResource* aProperty,
00309                     nsIRDFNode* aTarget);
00310 
00311     NS_IMETHOD HasAssertion(nsIRDFResource* source,
00312                             nsIRDFResource* property,
00313                             nsIRDFNode* target,
00314                             PRBool tv,
00315                             PRBool* hasAssertion) {
00316         return mInner->HasAssertion(source, property, target, tv, hasAssertion);
00317     }
00318 
00319     NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) {
00320         return mInner->AddObserver(aObserver);
00321     }
00322 
00323     NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) {
00324         return mInner->RemoveObserver(aObserver);
00325     }
00326 
00327     NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *_retval) {
00328         return mInner->HasArcIn(aNode, aArc, _retval);
00329     }
00330 
00331     NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, PRBool *_retval) {
00332         return mInner->HasArcOut(aSource, aArc, _retval);
00333     }
00334 
00335     NS_IMETHOD ArcLabelsIn(nsIRDFNode* node,
00336                            nsISimpleEnumerator** labels) {
00337         return mInner->ArcLabelsIn(node, labels);
00338     }
00339 
00340     NS_IMETHOD ArcLabelsOut(nsIRDFResource* source,
00341                             nsISimpleEnumerator** labels) {
00342         return mInner->ArcLabelsOut(source, labels);
00343     }
00344 
00345     NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) {
00346         return mInner->GetAllResources(aResult);
00347     }
00348 
00349     NS_IMETHOD GetAllCmds(nsIRDFResource* source,
00350                               nsISimpleEnumerator/*<nsIRDFResource>*/** commands) {
00351         return mInner->GetAllCmds(source, commands);
00352     }
00353 
00354     NS_IMETHOD IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
00355                                 nsIRDFResource*   aCommand,
00356                                 nsISupportsArray/*<nsIRDFResource>*/* aArguments,
00357                                 PRBool* aResult) {
00358         return mInner->IsCommandEnabled(aSources, aCommand, aArguments, aResult);
00359     }
00360 
00361     NS_IMETHOD DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
00362                          nsIRDFResource*   aCommand,
00363                          nsISupportsArray/*<nsIRDFResource>*/* aArguments) {
00364         // XXX Uh oh, this could cause problems wrt. the "dirty" flag
00365         // if it changes the in-memory store's internal state.
00366         return mInner->DoCommand(aSources, aCommand, aArguments);
00367     }
00368 
00369     NS_IMETHOD BeginUpdateBatch() {
00370         return mInner->BeginUpdateBatch();
00371     }
00372 
00373     NS_IMETHOD EndUpdateBatch() {
00374         return mInner->EndUpdateBatch();
00375     }
00376 
00377     // nsIRDFRemoteDataSource interface
00378     NS_DECL_NSIRDFREMOTEDATASOURCE
00379 
00380     // nsIRDFXMLSink interface
00381     NS_DECL_NSIRDFXMLSINK
00382 
00383     // nsIRDFXMLSource interface
00384     NS_DECL_NSIRDFXMLSOURCE
00385 
00386     // nsIRequestObserver
00387     NS_DECL_NSIREQUESTOBSERVER
00388 
00389     // nsIStreamListener
00390     NS_DECL_NSISTREAMLISTENER
00391 
00392     // rdfIDataSource
00393     NS_IMETHOD VisitAllSubjects(rdfITripleVisitor *aVisitor) {
00394         nsresult rv;
00395         nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
00396         if (NS_FAILED(rv)) return rv;
00397         return rdfds->VisitAllSubjects(aVisitor);
00398     } 
00399 
00400     NS_IMETHOD VisitAllTriples(rdfITripleVisitor *aVisitor) {
00401         nsresult rv;
00402         nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
00403         if (NS_FAILED(rv)) return rv;
00404         return rdfds->VisitAllTriples(aVisitor);
00405     } 
00406 
00407     // Implementation methods
00408     PRBool
00409     MakeQName(nsIRDFResource* aResource,
00410               nsString& property,
00411               nsString& nameSpacePrefix,
00412               nsString& nameSpaceURI);
00413 
00414     nsresult
00415     SerializeAssertion(nsIOutputStream* aStream,
00416                        nsIRDFResource* aResource,
00417                        nsIRDFResource* aProperty,
00418                        nsIRDFNode* aValue);
00419 
00420     nsresult
00421     SerializeProperty(nsIOutputStream* aStream,
00422                       nsIRDFResource* aResource,
00423                       nsIRDFResource* aProperty);
00424 
00425     PRBool
00426     IsContainerProperty(nsIRDFResource* aProperty);
00427 
00428     nsresult
00429     SerializeDescription(nsIOutputStream* aStream,
00430                          nsIRDFResource* aResource);
00431 
00432     nsresult
00433     SerializeMember(nsIOutputStream* aStream,
00434                     nsIRDFResource* aContainer,
00435                     nsIRDFNode* aMember);
00436 
00437     nsresult
00438     SerializeContainer(nsIOutputStream* aStream,
00439                        nsIRDFResource* aContainer);
00440 
00441     nsresult
00442     SerializePrologue(nsIOutputStream* aStream);
00443 
00444     nsresult
00445     SerializeEpilogue(nsIOutputStream* aStream);
00446 
00447     PRBool
00448     IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
00449 
00450 protected:
00451     nsresult
00452     BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer);
00453 };
00454 
00455 PRInt32         RDFXMLDataSourceImpl::gRefCnt = 0;
00456 nsIRDFService*  RDFXMLDataSourceImpl::gRDFService;
00457 
00458 static const char kFileURIPrefix[] = "file:";
00459 static const char kResourceURIPrefix[] = "resource:";
00460 
00461 
00462 //----------------------------------------------------------------------
00463 
00464 nsresult
00465 NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult)
00466 {
00467     NS_PRECONDITION(aResult != nsnull, "null ptr");
00468     if (! aResult)
00469         return NS_ERROR_NULL_POINTER;
00470 
00471     RDFXMLDataSourceImpl* datasource = new RDFXMLDataSourceImpl();
00472     if (! datasource)
00473         return NS_ERROR_OUT_OF_MEMORY;
00474 
00475     nsresult rv;
00476     rv = datasource->Init();
00477 
00478     if (NS_FAILED(rv)) {
00479         delete datasource;
00480         return rv;
00481     }
00482 
00483     NS_ADDREF(datasource);
00484     *aResult = datasource;
00485     return NS_OK;
00486 }
00487 
00488 
00489 RDFXMLDataSourceImpl::RDFXMLDataSourceImpl(void)
00490     : mInner(nsnull),
00491       mIsWritable(PR_TRUE),
00492       mIsDirty(PR_FALSE),
00493       mLoadState(eLoadState_Unloaded)
00494 {
00495 #ifdef PR_LOGGING
00496     if (! gLog)
00497         gLog = PR_NewLogModule("nsRDFXMLDataSource");
00498 #endif
00499 }
00500 
00501 
00502 nsresult
00503 RDFXMLDataSourceImpl::Init()
00504 {
00505     nsresult rv;
00506     rv = CallCreateInstance(kRDFInMemoryDataSourceCID, &mInner);
00507     if (NS_FAILED(rv)) return rv;
00508 
00509     if (gRefCnt++ == 0) {
00510         rv = CallGetService(kRDFServiceCID, &gRDFService);
00511 
00512         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
00513         if (NS_FAILED(rv)) return rv;
00514     }
00515 
00516     return NS_OK;
00517 }
00518 
00519 
00520 RDFXMLDataSourceImpl::~RDFXMLDataSourceImpl(void)
00521 {
00522     nsresult rv;
00523 
00524     // Unregister first so that nobody else tries to get us.
00525     rv = gRDFService->UnregisterDataSource(this);
00526 
00527     // Now flush contents
00528     rv = Flush();
00529 
00530     // Release RDF/XML sink observers
00531     mObservers.Clear();
00532 
00533     NS_RELEASE(mInner);
00534 
00535     if (--gRefCnt == 0)
00536         NS_IF_RELEASE(gRDFService);
00537 }
00538 
00539 
00540 NS_IMPL_ISUPPORTS7(RDFXMLDataSourceImpl,
00541                    nsIRDFDataSource,
00542                    nsIRDFRemoteDataSource,
00543                    nsIRDFXMLSink,
00544                    nsIRDFXMLSource,
00545                    nsIRequestObserver,
00546                    nsIStreamListener,
00547                    rdfIDataSource)
00548 
00549 
00550 nsresult
00551 RDFXMLDataSourceImpl::BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer)
00552 {
00553     nsresult rv;
00554 
00555     // XXX I really hate the way that we're spoon-feeding this stuff
00556     // to the parser: it seems like this is something that netlib
00557     // should be able to do by itself.
00558     
00559     nsCOMPtr<nsIChannel> channel;
00560     nsCOMPtr<nsIRequest> request;
00561 
00562     // Null LoadGroup ?
00563     rv = NS_NewChannel(getter_AddRefs(channel), aURL, nsnull);
00564     if (NS_FAILED(rv)) return rv;
00565     nsIInputStream* in;
00566     PRUint32 sourceOffset = 0;
00567     rv = channel->Open(&in);
00568 
00569     // Report success if the file doesn't exist, but propagate other errors.
00570     if (rv == NS_ERROR_FILE_NOT_FOUND) return NS_OK;
00571     if (NS_FAILED(rv)) return rv;
00572 
00573     NS_ASSERTION(in != nsnull, "no input stream");
00574     if (! in) return NS_ERROR_FAILURE;
00575 
00576     rv = NS_ERROR_OUT_OF_MEMORY;
00577     ProxyStream* proxy = new ProxyStream();
00578     if (! proxy)
00579         goto done;
00580 
00581     // Notify load observers
00582     PRInt32 i;
00583     for (i = mObservers.Count() - 1; i >= 0; --i) {
00584         // Make sure to hold a strong reference to the observer so
00585         // that it doesn't go away in this call if it removes itself
00586         // as an observer
00587         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
00588 
00589         if (obs) {
00590             obs->OnBeginLoad(this);
00591         }
00592     }
00593 
00594     request = do_QueryInterface(channel);
00595 
00596     aConsumer->OnStartRequest(request, nsnull);
00597     while (PR_TRUE) {
00598         char buf[4096];
00599         PRUint32 readCount;
00600 
00601         if (NS_FAILED(rv = in->Read(buf, sizeof(buf), &readCount)))
00602             break; // error
00603 
00604         if (readCount == 0)
00605             break; // eof
00606 
00607         proxy->SetBuffer(buf, readCount);
00608 
00609         rv = aConsumer->OnDataAvailable(request, nsnull, proxy, sourceOffset, readCount);
00610         sourceOffset += readCount;
00611         if (NS_FAILED(rv))
00612             break;
00613     }
00614 
00615     aConsumer->OnStopRequest(channel, nsnull, rv);
00616 
00617     // Notify load observers
00618     for (i = mObservers.Count() - 1; i >= 0; --i) {
00619         // Make sure to hold a strong reference to the observer so
00620         // that it doesn't go away in this call if it removes itself
00621         // as an observer
00622         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
00623 
00624         if (obs) {
00625             if (NS_FAILED(rv))
00626                 obs->OnError(this, rv, nsnull);
00627 
00628             obs->OnEndLoad(this);
00629         }
00630     }
00631 
00632        // don't leak proxy!
00633        proxy->Close();
00634        delete proxy;
00635        proxy = nsnull;
00636 
00637 done:
00638     NS_RELEASE(in);
00639     return rv;
00640 }
00641 
00642 NS_IMETHODIMP
00643 RDFXMLDataSourceImpl::GetLoaded(PRBool* _result)
00644 {
00645     *_result = (mLoadState == eLoadState_Loaded);
00646     return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP
00650 RDFXMLDataSourceImpl::Init(const char* uri)
00651 {
00652     NS_PRECONDITION(mInner != nsnull, "not initialized");
00653     if (! mInner)
00654         return NS_ERROR_OUT_OF_MEMORY;
00655 
00656     nsresult rv;
00657 
00658     rv = NS_NewURI(getter_AddRefs(mURL), nsDependentCString(uri));
00659     if (NS_FAILED(rv)) return rv;
00660 
00661     // XXX this is a hack: any "file:" URI is considered writable. All
00662     // others are considered read-only.
00663     if ((PL_strncmp(uri, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
00664         (PL_strncmp(uri, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0)) {
00665         mIsWritable = PR_FALSE;
00666     }
00667 
00668     rv = gRDFService->RegisterDataSource(this, PR_FALSE);
00669     if (NS_FAILED(rv)) return rv;
00670 
00671     return NS_OK;
00672 }
00673 
00674 
00675 NS_IMETHODIMP
00676 RDFXMLDataSourceImpl::GetURI(char* *aURI)
00677 {
00678     *aURI = nsnull;
00679     if (!mURL) {
00680         return NS_OK;
00681     }
00682     
00683     nsCAutoString spec;
00684     mURL->GetSpec(spec);
00685     *aURI = ToNewCString(spec);
00686     if (!*aURI) {
00687         return NS_ERROR_OUT_OF_MEMORY;
00688     }
00689     
00690     return NS_OK;
00691 }
00692 
00693 NS_IMETHODIMP
00694 RDFXMLDataSourceImpl::Assert(nsIRDFResource* aSource,
00695                              nsIRDFResource* aProperty,
00696                              nsIRDFNode* aTarget,
00697                              PRBool aTruthValue)
00698 {
00699     // We don't accept assertions unless we're writable (except in the
00700     // case that we're actually _reading_ the datasource in).
00701     nsresult rv;
00702 
00703     if (IsLoading()) {
00704         PRBool hasAssertion = PR_FALSE;
00705 
00706         nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
00707         if (gcable) {
00708             rv = gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &hasAssertion);
00709             if (NS_FAILED(rv)) return rv;
00710         }
00711 
00712         rv = NS_RDF_ASSERTION_ACCEPTED;
00713 
00714         if (! hasAssertion) {
00715             rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
00716 
00717             if (NS_SUCCEEDED(rv) && gcable) {
00718                 // Now mark the new assertion, so it doesn't get
00719                 // removed when we sweep. Ignore rv, because we want
00720                 // to return what mInner->Assert() gave us.
00721                 PRBool didMark;
00722                 (void) gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &didMark);
00723             }
00724 
00725             if (NS_FAILED(rv)) return rv;
00726         }
00727 
00728         return rv;
00729     }
00730     else if (mIsWritable) {
00731         rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
00732 
00733         if (rv == NS_RDF_ASSERTION_ACCEPTED)
00734             mIsDirty = PR_TRUE;
00735 
00736         return rv;
00737     }
00738     else {
00739         return NS_RDF_ASSERTION_REJECTED;
00740     }
00741 }
00742 
00743 
00744 NS_IMETHODIMP
00745 RDFXMLDataSourceImpl::Unassert(nsIRDFResource* source,
00746                                nsIRDFResource* property,
00747                                nsIRDFNode* target)
00748 {
00749     // We don't accept assertions unless we're writable (except in the
00750     // case that we're actually _reading_ the datasource in).
00751     nsresult rv;
00752 
00753     if (IsLoading() || mIsWritable) {
00754         rv = mInner->Unassert(source, property, target);
00755         if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
00756             mIsDirty = PR_TRUE;
00757     }
00758     else {
00759         rv = NS_RDF_ASSERTION_REJECTED;
00760     }
00761 
00762     return rv;
00763 }
00764 
00765 NS_IMETHODIMP
00766 RDFXMLDataSourceImpl::Change(nsIRDFResource* aSource,
00767                              nsIRDFResource* aProperty,
00768                              nsIRDFNode* aOldTarget,
00769                              nsIRDFNode* aNewTarget)
00770 {
00771     nsresult rv;
00772 
00773     if (IsLoading() || mIsWritable) {
00774         rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
00775 
00776         if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
00777             mIsDirty = PR_TRUE;
00778     }
00779     else {
00780         rv = NS_RDF_ASSERTION_REJECTED;
00781     }
00782 
00783     return rv;
00784 }
00785 
00786 NS_IMETHODIMP
00787 RDFXMLDataSourceImpl::Move(nsIRDFResource* aOldSource,
00788                            nsIRDFResource* aNewSource,
00789                            nsIRDFResource* aProperty,
00790                            nsIRDFNode* aTarget)
00791 {
00792     nsresult rv;
00793 
00794     if (IsLoading() || mIsWritable) {
00795         rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
00796         if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
00797             mIsDirty = PR_TRUE;
00798     }
00799     else {
00800         rv = NS_RDF_ASSERTION_REJECTED;
00801     }
00802 
00803     return rv;
00804 }
00805 
00806 
00807 nsresult
00808 RDFXMLDataSourceImpl::rdfXMLFlush(nsIURI *aURI)
00809 {
00810 
00811     nsresult rv;
00812 
00813     {
00814         // Quick and dirty check to see if we're in XPCOM shutdown. If
00815         // we are, we're screwed: it's too late to serialize because
00816         // many of the services that we'll need to acquire to properly
00817         // write the file will be unaquirable.
00818         nsCOMPtr<nsIRDFService> dummy = do_GetService(kRDFServiceCID, &rv);
00819         if (NS_FAILED(rv)) {
00820             NS_WARNING("unable to Flush() diry datasource during XPCOM shutdown");
00821             return rv;
00822         }
00823     }
00824 
00825     // Is it a file? If so, we can write to it. Some day, it'd be nice
00826     // if we didn't care what kind of stream this was...
00827     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI);
00828     
00829     if (fileURL) {
00830         nsCOMPtr<nsIFile> file;
00831         fileURL->GetFile(getter_AddRefs(file));
00832         if (file) {
00833             // get a safe output stream, so we don't clobber the datasource file unless
00834             // all the writes succeeded.
00835             nsCOMPtr<nsIOutputStream> out;
00836             rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
00837                                                  file,
00838                                                  PR_WRONLY | PR_CREATE_FILE,
00839                                                  /*octal*/ 0666,
00840                                                  0);
00841             if (NS_FAILED(rv)) return rv;
00842 
00843             nsCOMPtr<nsIOutputStream> bufferedOut;
00844             rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 4096);
00845             if (NS_FAILED(rv)) return rv;
00846 
00847             rv = Serialize(bufferedOut);
00848             if (NS_FAILED(rv)) return rv;
00849             
00850             // All went ok. Maybe except for problems in Write(), but the stream detects
00851             // that for us
00852             nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOut, &rv);
00853             if (NS_FAILED(rv)) return rv;
00854 
00855             rv = safeStream->Finish();
00856             if (NS_FAILED(rv)) {
00857                 NS_WARNING("failed to save datasource file! possible dataloss");
00858                 return rv;
00859             }
00860         }
00861     }
00862 
00863     return NS_OK;
00864 }
00865 
00866 
00867 NS_IMETHODIMP
00868 RDFXMLDataSourceImpl::FlushTo(const char *aURI)
00869 {
00870     NS_PRECONDITION(aURI != nsnull, "not initialized");
00871     if (!aURI)
00872         return NS_ERROR_NULL_POINTER;
00873 
00874     // XXX this is a hack: any "file:" URI is considered writable. All
00875     // others are considered read-only.
00876     if ((PL_strncmp(aURI, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
00877         (PL_strncmp(aURI, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0))
00878     {
00879         return NS_ERROR_ILLEGAL_VALUE;
00880     }
00881 
00882     nsCOMPtr<nsIURI>  url;
00883     nsresult rv = NS_NewURI(getter_AddRefs(url), aURI);
00884     if (NS_FAILED(rv))
00885       return rv;
00886     rv = rdfXMLFlush(url);
00887     return rv;
00888 }
00889 
00890 
00891 NS_IMETHODIMP
00892 RDFXMLDataSourceImpl::Flush(void)
00893 {
00894     if (!mIsWritable || !mIsDirty)
00895         return NS_OK;
00896 
00897     // while it is not fatal if mURL is not set,
00898     // indicate failure since we can't flush back to an unknown origin
00899     if (! mURL)
00900         return NS_ERROR_NOT_INITIALIZED;
00901 
00902 #ifdef PR_LOGGING
00903     nsCAutoString spec;
00904     mURL->GetSpec(spec);
00905     PR_LOG(gLog, PR_LOG_NOTICE,
00906            ("rdfxml[%p] flush(%s)", this, spec.get()));
00907 #endif
00908 
00909     nsresult rv;
00910     if (NS_SUCCEEDED(rv = rdfXMLFlush(mURL)))
00911     {
00912       mIsDirty = PR_FALSE;
00913     }
00914     return rv;
00915 }
00916 
00917 
00918 //----------------------------------------------------------------------
00919 //
00920 // nsIRDFXMLDataSource methods
00921 //
00922 
00923 NS_IMETHODIMP
00924 RDFXMLDataSourceImpl::GetReadOnly(PRBool* aIsReadOnly)
00925 {
00926     *aIsReadOnly = !mIsWritable;
00927     return NS_OK;
00928 }
00929 
00930 
00931 NS_IMETHODIMP
00932 RDFXMLDataSourceImpl::SetReadOnly(PRBool aIsReadOnly)
00933 {
00934     if (mIsWritable && aIsReadOnly)
00935         mIsWritable = PR_FALSE;
00936 
00937     return NS_OK;
00938 }
00939 
00940 #include "nsITimelineService.h"
00941 
00942 NS_IMETHODIMP
00943 RDFXMLDataSourceImpl::Refresh(PRBool aBlocking)
00944 {
00945 #ifdef PR_LOGGING
00946     nsCAutoString spec;
00947     if (mURL) {
00948         mURL->GetSpec(spec);
00949     }
00950     PR_LOG(gLog, PR_LOG_NOTICE,
00951            ("rdfxml[%p] refresh(%s) %sblocking", this, spec.get(), (aBlocking ? "" : "non")));
00952 #endif
00953     
00954     // If an asynchronous load is already pending, then just let it do
00955     // the honors.
00956     if (IsLoading()) {
00957         PR_LOG(gLog, PR_LOG_NOTICE,
00958                ("rdfxml[%p] refresh(%s) a load was pending", this, spec.get()));
00959 
00960         if (aBlocking) {
00961             NS_WARNING("blocking load requested when async load pending");
00962             return NS_ERROR_FAILURE;
00963         }
00964         else {
00965             return NS_OK;
00966         }
00967     }
00968 
00969     if (! mURL)
00970         return NS_ERROR_FAILURE;
00971     nsCOMPtr<nsIRDFXMLParser> parser = do_CreateInstance("@mozilla.org/rdf/xml-parser;1");
00972     if (! parser)
00973         return NS_ERROR_FAILURE;
00974 
00975     nsresult rv = parser->ParseAsync(this, mURL, getter_AddRefs(mListener));
00976     if (NS_FAILED(rv)) return rv;
00977 
00978     if (aBlocking) {
00979         NS_TIMELINE_START_TIMER("rdf blocking parse");
00980         rv = BlockingParse(mURL, this);
00981         NS_TIMELINE_STOP_TIMER("rdf blocking parse");
00982         NS_TIMELINE_MARK_TIMER("rdf blocking parse");
00983 
00984         mListener = nsnull; // release the parser
00985 
00986         if (NS_FAILED(rv)) return rv;
00987     }
00988     else {
00989         // Null LoadGroup ?
00990         rv = NS_OpenURI(this, nsnull, mURL, nsnull);
00991         if (NS_FAILED(rv)) return rv;
00992 
00993         // So we don't try to issue two asynchronous loads at once.
00994         mLoadState = eLoadState_Pending;
00995     }
00996 
00997     return NS_OK;
00998 }
00999 
01000 NS_IMETHODIMP
01001 RDFXMLDataSourceImpl::BeginLoad(void)
01002 {
01003 #ifdef PR_LOGGING
01004     nsCAutoString spec;
01005     if (mURL) {
01006         mURL->GetSpec(spec);
01007     }
01008     PR_LOG(gLog, PR_LOG_NOTICE,
01009            ("rdfxml[%p] begin-load(%s)", this, spec.get()));
01010 #endif
01011     
01012     mLoadState = eLoadState_Loading;
01013     for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
01014         // Make sure to hold a strong reference to the observer so
01015         // that it doesn't go away in this call if it removes itself
01016         // as an observer
01017         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
01018 
01019         if (obs) {
01020             obs->OnBeginLoad(this);
01021         }
01022     }
01023     return NS_OK;
01024 }
01025 
01026 NS_IMETHODIMP
01027 RDFXMLDataSourceImpl::Interrupt(void)
01028 {
01029 #ifdef PR_LOGGING
01030     nsCAutoString spec;
01031     if (mURL) {
01032         mURL->GetSpec(spec);
01033     }
01034     PR_LOG(gLog, PR_LOG_NOTICE,
01035            ("rdfxml[%p] interrupt(%s)", this, spec.get()));
01036 #endif
01037     
01038     for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
01039         // Make sure to hold a strong reference to the observer so
01040         // that it doesn't go away in this call if it removes itself
01041         // as an observer
01042         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
01043 
01044         if (obs) {
01045             obs->OnInterrupt(this);
01046         }
01047     }
01048     return NS_OK;
01049 }
01050 
01051 NS_IMETHODIMP
01052 RDFXMLDataSourceImpl::Resume(void)
01053 {
01054 #ifdef PR_LOGGING
01055     nsCAutoString spec;
01056     if (mURL) {
01057         mURL->GetSpec(spec);
01058     }
01059     PR_LOG(gLog, PR_LOG_NOTICE,
01060            ("rdfxml[%p] resume(%s)", this, spec.get()));
01061 #endif
01062     
01063     for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
01064         // Make sure to hold a strong reference to the observer so
01065         // that it doesn't go away in this call if it removes itself
01066         // as an observer
01067         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
01068 
01069         if (obs) {
01070             obs->OnResume(this);
01071         }
01072     }
01073     return NS_OK;
01074 }
01075 
01076 NS_IMETHODIMP
01077 RDFXMLDataSourceImpl::EndLoad(void)
01078 {
01079 #ifdef PR_LOGGING
01080     nsCAutoString spec;
01081     if (mURL) {
01082         mURL->GetSpec(spec);
01083     }
01084     PR_LOG(gLog, PR_LOG_NOTICE,
01085            ("rdfxml[%p] end-load(%s)", this, spec.get()));
01086 #endif
01087     
01088     mLoadState = eLoadState_Loaded;
01089 
01090     // Clear out any unmarked assertions from the datasource.
01091     nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
01092     if (gcable) {
01093         gcable->Sweep();
01094     }
01095 
01096     // Notify load observers
01097     for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
01098         // Make sure to hold a strong reference to the observer so
01099         // that it doesn't go away in this call if it removes itself
01100         // as an observer
01101         nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
01102 
01103         if (obs) {
01104             obs->OnEndLoad(this);
01105         }
01106     }
01107     return NS_OK;
01108 }
01109 
01110 NS_IMETHODIMP
01111 RDFXMLDataSourceImpl::AddNameSpace(nsIAtom* aPrefix, const nsString& aURI)
01112 {
01113     mNameSpaces.Put(aURI, aPrefix);
01114     return NS_OK;
01115 }
01116 
01117 
01118 NS_IMETHODIMP
01119 RDFXMLDataSourceImpl::AddXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
01120 {
01121     if (! aObserver)
01122         return NS_ERROR_NULL_POINTER;
01123 
01124     mObservers.AppendObject(aObserver);
01125     return NS_OK;
01126 }
01127 
01128 NS_IMETHODIMP
01129 RDFXMLDataSourceImpl::RemoveXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
01130 {
01131     if (! aObserver)
01132         return NS_ERROR_NULL_POINTER;
01133 
01134     mObservers.RemoveObject(aObserver);
01135 
01136     return NS_OK;
01137 }
01138 
01139 
01140 //----------------------------------------------------------------------
01141 //
01142 // nsIRequestObserver
01143 //
01144 
01145 NS_IMETHODIMP
01146 RDFXMLDataSourceImpl::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
01147 {
01148     return mListener->OnStartRequest(request, ctxt);
01149 }
01150 
01151 NS_IMETHODIMP
01152 RDFXMLDataSourceImpl::OnStopRequest(nsIRequest *request,
01153                                     nsISupports *ctxt,
01154                                     nsresult status)
01155 {
01156     if (NS_FAILED(status)) {
01157         for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
01158             // Make sure to hold a strong reference to the observer so
01159             // that it doesn't go away in this call if it removes
01160             // itself as an observer
01161             nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
01162 
01163             if (obs) {
01164                 obs->OnError(this, status, nsnull);
01165             }
01166         }
01167     }
01168 
01169     nsresult rv;
01170     rv = mListener->OnStopRequest(request, ctxt, status);
01171 
01172     mListener = nsnull; // release the parser
01173 
01174     return rv;
01175 }
01176 
01177 //----------------------------------------------------------------------
01178 //
01179 // nsIStreamListener
01180 //
01181 
01182 NS_IMETHODIMP
01183 RDFXMLDataSourceImpl::OnDataAvailable(nsIRequest *request,
01184                                       nsISupports *ctxt,
01185                                       nsIInputStream *inStr,
01186                                       PRUint32 sourceOffset,
01187                                       PRUint32 count)
01188 {
01189     return mListener->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
01190 }
01191 
01192 //----------------------------------------------------------------------
01193 //
01194 // nsIRDFXMLSource
01195 //
01196 
01197 NS_IMETHODIMP
01198 RDFXMLDataSourceImpl::Serialize(nsIOutputStream* aStream)
01199 {
01200     nsresult rv;
01201     nsCOMPtr<nsIRDFXMLSerializer> serializer
01202         = do_CreateInstance("@mozilla.org/rdf/xml-serializer;1", &rv);
01203 
01204     if (! serializer)
01205         return rv;
01206 
01207     rv = serializer->Init(this);
01208     if (NS_FAILED(rv)) return rv;
01209 
01210     // Add any namespace information that we picked up when reading
01211     // the RDF/XML
01212     nsNameSpaceMap::const_iterator last = mNameSpaces.last();
01213     for (nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
01214          iter != last; ++iter) {
01215         // We might wanna change nsIRDFXMLSerializer to nsACString and
01216         // use a heap allocated buffer here in the future.
01217         NS_ConvertUTF8toUTF16 uri(iter->mURI);
01218         serializer->AddNameSpace(iter->mPrefix, uri);
01219     }
01220 
01221     // Serialize!
01222     nsCOMPtr<nsIRDFXMLSource> source = do_QueryInterface(serializer);
01223     if (! source)
01224         return NS_ERROR_FAILURE;
01225 
01226     return source->Serialize(aStream);
01227 }