Re: Cannot call RegisterDataSource on a own datasource

mcben <[email protected]> Mon, 4 Feb 2008 05:28:08 -0800 (PST)
Newsgroups gmane.comp.mozilla.devel.rdf
Organization http://groups.google.com
Message-ID <689b0c65-b77d-4857-bf26-8fa62d322d46@e10g2000prf.googlegroups.com>
On 4 fév, 09:27, Axel Hecht <[email protected]> wrote:
> mcben wrote:
> > Hi folks,
>
> > Here is quite a long post about a problem with a own datasource
> > implementation based on a xml-datasource.
>
> > I'm looking for any clues or links that could help me to achieve my
> > first goal : a custom datasource that i could fill from a CSV file.
>
> > Thanks in advance for reading me :)
>
> > I'm creating a javascript component that fetches a CSV file with
> > XMLHttpRequest, parses it, then adds lines as RDF resources in a RDF
> > datasource.
> > Its contractid is like "@mozilla.org/rdf/datasource;1?name=mydatasource"
> > so as to get it in the chrome with the "rdf:mydatasource" keyword.
>
> > I started this work by using an in-memory-datasource as the __proto__
> > properties of my javascript prototype.
> > I could fetch my file and appends data in the datasource : the result
> > was shown in a rdf tree.
>
> > The first problem with this design was that i was not able to overwrite
> > some nsIRDFDataSource methods (not a real problem in fact), nor extends
> > the in-memory-datasource component with other interfaces : the QI method
> > of my component was never called, so i got a NS_ERROR_NO_INTERFACE
> > error, when i QI another interface (eg: nsIDOMListener to adds
> > XMLHttpRequest callback in my chrome).
>
> > Then i took a look a the rdf:localstore C++ implementation
> > (nsLocalStore.cpp) and i'm quite happy with its design : it works like a
> > wrapper around a xml-datasource. It "justs" load data from
> > localstore.rdf in the profile directory or creates it if it does not exist.
> > Then with that, I could even read and store a cached version of my CSV
> > file on disk.
>
> > So, I've made the same design in my javascript component, all
> > nsIRDFDataSource related methods are forwarded to a "mInner"
> > xml-datasource property.
>
> > But when i try to call RegisterDataSource to register my datasource in
> > the constructor of my component, it throws a NS_ERROR_FAILURE :
> > Here is the code :
>
> > this._loadLocalData();
> > RDF.RegisterDataSource(this, false);
> > this._loadRemoteData();
>
> > If i log "this" the console service returns me that is an Object.
>
> > That's the problem ?
>
> > In fact I get from the chrome : [xpconnect wrapped (nsISupports,
> > nsIRDFDataSource, nsIRDFRemoteDataSource)] after the registration, even
> > if the registration fails.
> > But, the nsIRDFRemoteDatasource Init method is called at every calls
> > from chrome.
>
> > Maybe the real problem comes from the xml-datasource initialization.
> > The following code is called by the constructor of my component :
>
> > this.mInner = Components
> >        .classes["@mozilla.org/rdf/datasource;1?name=xml-datasource"]
> >        .createInstance(Components.interfaces.nsIRDFDataSource);
> > var remote = this.mInner
> >        .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
> > remote.Init(filespec);
>
> > The "Init" call throws an exception with the following code
> > "0x804b000a". Its related with nsISocketTransport and means
> > STATUS_WAITING_FOR. Ok, why not.
> > Then remote.loaded returns false, and calling remote.Refresh(false)
> > throws a NS_ERROR_FAILURE
>
> > What is wrong with that ?
>
> > So i catch this Init exception and get my remote datasource with the RDF
> > Service like this :
> > remote = RDF.GetDataSource(filespec)
> >           .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
> > this.mInner = remote
> >           .QueryInterface(Components.interfaces.nsIRDFDataSource);
>
> > In that case no problem with remote ds, but the RegisterDataSource go on
> > failing.
>
> > And later, in the chrome, when i call a method or property that is
> > forwarded to the inner, i got back a undefined result.
> > And i cant look any result in my xul tree.
>
> > I can post the full source code of the component if needed.
>
> I suspect that it has something to do with your move from an in-memory
> datasource as inner to an rdf/xml as inner. I didn't get why you made
> that move, was that intentional?

Hi Axel,
Thank you for your answer.

To begin, you can grab my component source code here, with the "second
design" implementation (with inner):
http://pastebin.mozilla.org/319272

And the xul tree that should show my results look quite simple :
<tree id="itemtypes-tree" flex="1"
        datasources="rdf:bi-itemtypes" ref="bi:itemtypes-list">
  <treecols>
    <treecol id="col-name" primary="true" label="Name" flex="1"/>
  </treecols>
  <template>
    <treechildren>
      <treeitem uri="rdf:*">
        <treerow>
          <treecell label="rdf:*" />
        </treerow>
      </treeitem>
    </treechildren>
  </template>
</tree>


Hum. yes, the move was partially intentional :

 * My first try with an in-memory datasource was with the instance set
as __proto__ property in the prototype of my component. That is to
say, that it was not with the "inner" design.
The main problem with this design was that i could not extends my
component by implementing other public interface : the QI function of
my component was never called, nor any method that i though they
overrode existing methods of the in-mem. The only thing that i could
improve in this design was the init phase of my component (by filling
in-mem with XMLHttpRequest) nothing else.

Is it normal that with the following code, the QueryInterface method
is never called, that's why i cant implement any other interfaces ? (i
think that the __proto__ QI is called instead).

function myComponentImplementation() {
}
myComponentImplementation.prototype {
    __proto__ : Components.classes["@mozilla.org/rdf/datasource;1?
name=in-memory-datasource"]
                         .createInstance(Components.interfaces.nsIRDFDataSource),

    QueryInterface : function(iid) {
        if (!iid.equals(C_i.nsIDOMEventListener) &&
            !iid.equals(C_i.nsIRDFDataSource) &&
            !iid.equals(C_i.nsIRDFRemoteDataSource) &&
            !iid.equals(C_i.nsISupports))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        ConsoleService.logStringMessage("QI called:" + iid);
        return this;
    }
}


 * With the "inner" design, i immediatly use a xml-datasource, just
like the nsLocalStore implementation. With that, i could load a cached
version of the rdf file from disk, and then update it with
XMLHttpRequest only if it really needed.

Since, you are thinking that the problem can come from the xml-
datasource, i took the same component, and just replaced the xml-
datasource inner, with an in-memory-datasource.
Indeed, it seems there is no major failure with initialization, i can
register the datasource, but the content that i downloaded with
XMLHttpRequest and that i put in the ds is not reflected in my chrome
xul tree, nor i can query my datasource for any target that sould
exists.

More precisely, about any errors, i had to create the container (the
one to wich I append downloaded resources) with this.mInner (the in-
mem ds) and not my component. Is it normal ?
Look:
this._container = RDFContainer.MakeSeq(this.mInner,
RDF.GetResource(this._struct.ref));
Even though my component should implement nsIRDFDataSource, that's why
i thought i could do that :
this._container = RDFContainer.MakeSeq(this,
RDF.GetResource(this._struct.ref));

But it seems that append does not work anyway.


* Finally, why I made the move to xml-datasource ?
(I thought) I could parse a local rdf file at init, and flushes the
modified results at destruction. I even could observe the download
progress from chrome (eg: with a progressbar), etc.

So any clue ?
What do you think is the best implementation ?

The first design could be perfect if i could override and extends
methods and interfaces of the __proto__ instance.

Thank you for reading me.