You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@xalan.apache.org by John Gentilin <jo...@eyecatching.com> on 2000/12/22 00:26:41 UTC

Extensions, best way to handle elements ??

I am trying to implement an extension function that accepts a Document
Fragment
which the extension will  parse that fragment for its parameters.  I had
a few issues
that needed clearing up.

Using the XSL/XML below, the entry point to my function was called with
a NodeList
representing the children of my select and not the Element. When are
ExtensionFunctions
called with Elements instead of NodeLists.

The NodeList I received is populated with stree ElemeentImpl because I
used the
standard command line function but I am assuming  if the parser was
initiated
with a DOMSource other than stree, that I can't use the stree methods
like getChildCount()
and that I need to stick with only the DOM functions that stree
ElementImpl implements.
Is this true ?? and is the code below an efficient way to accomplish
that goal ??

Thanks
John G


XSL
    <!-- 1. Make the connection -->
    <xsl:param name="cinfo" select="//DBINFO" />
    <xsl:variable name="accounts" select='sql:new($cinfo)'/>

XML
 <DBINFO>
   <dbdriver>com.sybase.jdbc2.jdbc.SybDriver</dbdriver>
   <dburl>jdbc:sybase:Tds:localhost:5000/smart911</dburl>
   <user>sa</user>
   <password/>
 </DBINFO>

  /**
   *
   * Allow the database connection information to be sepcified in
   * the XML tree. The connection information could also be
   * externally originated and passed in as an XSL Parameter.
   *
   * The required XML Format is as follows.
   *
   * // A document fragment is needed to specify the connection
information
   * // the top tag name is not specific for this code, we are only
interested
   * // in the tags inside.
   * <DBINFO-TAG>
   *
   * // Specify the driver name for this connection pool
   *  <dbdriver>drivername</dbdriver>
   *
   * // Specify the URL for the driver in this connection pool
   *  <dburl>url</dburl>
   *
   * // Specify the password for this connection pool
   *  <password>password</password>
   *
   * // Specify the username for this connection pool
   *  <user>username</user>
   *
   *  // You can add extra protocol items including the User Name &
Password
   *  // with the protocol tag. For each extra protocol item, add a new
element
   *  // where the name of the item is specified as the name attribute
and
   *  // and its value as the elements value.
   *  <protocol name="name of value">value</protocol>
   *
   * </DBINFO-TAG>
   *
   */

  public XConnection(NodeList list)
  {
    initFromElement( (Element) list.item(0) );
  }

  private void initFromElement(Element e)
  {

    Properties prop = new Properties();
    String driver = "";
    String dbURL = "";
    Node n = e.getFirstChild();

    if (null == n) return; // really need to throw an error

    do
    {
      String nName = n.getNodeName();

      if (nName.equalsIgnoreCase("dbdriver"))
      {
        driver = "";
        Node n1 = n.getFirstChild();
        if (null != n1)
        {
          driver = n1.getNodeValue();
        }
      }

      if (nName.equalsIgnoreCase("dburl"))
      {
        dbURL = "";
        Node n1 = n.getFirstChild();
        if (null != n1)
        {
          dbURL = n1.getNodeValue();
        }
      }

      if (nName.equalsIgnoreCase("password"))
      {
        String s = "";
        Node n1 = n.getFirstChild();
        if (null != n1)
        {
          s = n1.getNodeValue();
        }
        prop.setProperty("password", s);
      }

      if (nName.equalsIgnoreCase("user"))
      {
        String s = "";
        Node n1 = n.getFirstChild();
        if (null != n1)
        {
          s = n1.getNodeValue();
        }
        prop.setProperty("user", s);
      }

      if (nName.equalsIgnoreCase("protocol"))
      {
        String Name = "";

        NamedNodeMap attrs = n.getAttributes();
        Node n1 = attrs.getNamedItem("name");
        if (null != n1)
        {
          String s = "";
          Name = n1.getNodeValue();

          Node n2 = n.getFirstChild();
          if (null != n2) s = n2.getNodeValue();

          prop.setProperty(Name, s);
        }
      }

    } while ( (n = n.getNextSibling()) != null);

    init(driver, dbURL, prop);
  }




Re: Extensions, best way to handle elements ??

Posted by Gary L Peskin <ga...@firstech.com>.
> John Gentilin wrote:
> > As for out previous discussion, all this information would be great to capture
> > in the Extensions document.  It was not apparent to me (probably because of
> > my lack of experience with Xalan) that arguments can be passed as XPath
> > expressions with out using the attribute select="XPATH/HERE" and that
> > you could specify multiple XPath statements in a single element.

> > Gary L Peskin wrote:
> Again, this should be just standard XSLT extension stuff, not Xalan
> specific.

Just to elaborate on this, this is actually specified in XPath section
3.2 (http://www.w3.org/TR/xpath#section-Function-Calls) which is
incorporated into section 14 of the XSLT Recommendation.  It applies not
only to extension functions but to all functions, including standard
XPath functions.

Gary

Re: Extensions, best way to handle elements ??

Posted by Gary L Peskin <ga...@firstech.com>.
John Gentilin wrote:
> 
> Gary L Peskin wrote:
> 
> > > >
> > > >   select = 'sql:new(/DBINFO/dbdriver, /DBINFO/dburl, /DBINFO/user,
> > > > /DBINFO/password)'
> > > >
> >
> > My example, as shown above, will work properly.  You can have multiple
> > XPath statements as arguments to an extension function call.  They will
> > be evaluated just like any other arguments.
> >
> 
> What is the mechanism that the extension handler uses to know the difference
> if the argument is an XPath Statement and not 4 literal strings, the "'" chars ??

If you mean the Xalan internal extension mechanism itself, then it looks
at the XSLT datatype which will be some form of an XObject.  If you mean
the java extension function that you write, you specify what you'd like
and Xalan will try to convert the XSLT argument to that type.

> Before you stated that my implementation seemed like a bit of work to actual
> parse down into the whole node set. In your opinion would it be more efficient
> to have Xalan execute 4 separate XPath statements and cast (for lack of a
> better term) them to string, or just pass in a single NodeList and have the
> extension function process it in a linear fashion ??

It's hard to tell without testing and profiling.  However, it seems like
it is an infrequent operation (that is, making the connection, not
executing the SELECT and retrieving the rows) so small differences
probably won't be significant unless you are in a performance-critical
environment.  You'd have to try it both ways and see what the difference
in timing is.

> As for out previous discussion, all this information would be great to capture
> in the Extensions document.  It was not apparent to me (probably because of
> my lack of experience with Xalan) that arguments can be passed as XPath
> expressions with out using the attribute select="XPATH/HERE" and that
> you could specify multiple XPath statements in a single element.

Again, this should be just standard XSLT extension stuff, not Xalan
specific.  I agree that an expanded section of Mike Kay's book or some
type of online tutorial on extensions would be a good thing.  Any
volunteers?

Gary

Re: Extensions, best way to handle elements ??

Posted by John Gentilin <jo...@eyecatching.com>.
Gary L Peskin wrote:

> > >
> > >   select = 'sql:new(/DBINFO/dbdriver, /DBINFO/dburl, /DBINFO/user,
> > > /DBINFO/password)'
> > >
>
> My example, as shown above, will work properly.  You can have multiple
> XPath statements as arguments to an extension function call.  They will
> be evaluated just like any other arguments.
>

What is the mechanism that the extension handler uses to know the difference
if the argument is an XPath Statement and not 4 literal strings, the "'" chars ??

Before you stated that my implementation seemed like a bit of work to actual
parse down into the whole node set. In your opinion would it be more efficient
to have Xalan execute 4 separate XPath statements and cast (for lack of a
better term) them to string, or just pass in a single NodeList and have the
extension function process it in a linear fashion ??

As for out previous discussion, all this information would be great to capture
in the Extensions document.  It was not apparent to me (probably because of
my lack of experience with Xalan) that arguments can be passed as XPath
expressions with out using the attribute select="XPATH/HERE" and that
you could specify multiple XPath statements in a single element.

John G


Re: Extensions, best way to handle elements ??

Posted by Gary L Peskin <ga...@firstech.com>.
John Gentilin wrote:
> OK, I was using the sql/XConnection.java class as an example which
> already has a constructor that takes an Element as an argument.

Hmm.  Not sure how that worked.

> > It seems like it will do the trick.  This should only get executed once
> > when the constructor is invoked (is that correct?) so it's not like it's
> > in some kind of tight loop in terms of the need to optimize it.  It does
> > seem like work, though, to get the info out.  Perhaps you'd consider
> > just having the constructor take four string arguments and then calling
> > it with those:
> >
> >   select = 'sql:new(/DBINFO/dbdriver, /DBINFO/dburl, /DBINFO/user,
> > /DBINFO/password)'
> >
> 
> I am trying to make the SQL extensions a little more flexible, The original
> constructor that takes separate arguments is still there, it takes 4 strings
> as such
>     <xsl:variable name="accounts"
>                   select="sql:new('com.sybase.jdbc2.jdbc.SybDriver',
>                                 'jdbc:sybase:Tds:localhost:5000/smart911', 'sa',
>                                 '')"/>
> 
> To do it as you suggest above, I would actually have to implement 4 separate
> XSL parameters. i.e.
>   <xsl:param name="driver" select="/DBINFO/dbdriver" /> ...
> 
> and then use parameter substitution in the call. i.e.
> 
>     <xsl:variable name="accounts"
>                   select="sql:new($driver, $url, $user, $passowrd'')"/>
> 
> But this would result in a signature of 4 NodeLists (or Node), not Strings
> Correct ??
> 
> You can't just have multiple XPath statements as arguments to an extension
> function call or am I mistaken ??

My example, as shown above, will work properly.  You can have multiple
XPath statements as arguments to an extension function call.  They will
be evaluated just like any other arguments.

In terms of the method signature, the extension mechanism will match up
your XSLT parameter types with the java parameter types.  So, if you
have an XSLT extension call with four XSLT node-set parameters and a
java method signature with four String arguments, the extension
mechanism will match those up by converting each node-set to a String as
if by a call to the XPath string() function.

Similarly, if you have a java method signature with four NodeList
arguments, the extension mechanism will convert each XPath node-set to a
NodeList.

If you have two different methods in your java class, one with four
String parameters and one with four NodeList parameters, the extension
mechanism would select the one with the four NodeList parameters.  It
uses a scoring system which looks at each XSLT type and prefers certain
java types over others in a conversion.  Since a java NodeList is
"closer" to an XSLT node-set than is a java String, that method would
get the higher score and would be selected.

I know this is confusing so if you have questions, please post them.

Gary

Re: Extensions, best way to handle elements ??

Posted by John Gentilin <jo...@eyecatching.com>.
Gary L Peskin wrote:

>
> Unfortunately, in its current incarnation, the extension mechanism does
> -not- know how to map from an XSLT node-set to a java Element so you
> can't specify Element as your argument type in java.  This support is
> planned.
>

OK, I was using the sql/XConnection.java class as an example which
already has a constructor that takes an Element as an argument.

  /**
   * Create an XConnection object with a connection protocol
   * @param driver JDBC driver of the form foo.bar.Driver.
   * @param dbURL database URL of the form jdbc:subprotocol:subname.
   * @param protocolElem list of string tag/value connection arguments,
   * normally including at least "user" and "password".
   */
  public XConnection(String driver, String dbURL, Element protocolElem)
  {


>
>
> It seems like it will do the trick.  This should only get executed once
> when the constructor is invoked (is that correct?) so it's not like it's
> in some kind of tight loop in terms of the need to optimize it.  It does
> seem like work, though, to get the info out.  Perhaps you'd consider
> just having the constructor take four string arguments and then calling
> it with those:
>
>   select = 'sql:new(/DBINFO/dbdriver, /DBINFO/dburl, /DBINFO/user,
> /DBINFO/password)'
>

I am trying to make the SQL extensions a little more flexible, The original
constructor that takes separate arguments is still there, it takes 4 strings
as such
    <xsl:variable name="accounts"
                  select="sql:new('com.sybase.jdbc2.jdbc.SybDriver',
                                'jdbc:sybase:Tds:localhost:5000/smart911', 'sa',
                                '')"/>

To do it as you suggest above, I would actually have to implement 4 separate
XSL parameters. i.e.
  <xsl:param name="driver" select="/DBINFO/dbdriver" /> ...

and then use parameter substitution in the call. i.e.

    <xsl:variable name="accounts"
                  select="sql:new($driver, $url, $user, $passowrd'')"/>

But this would result in a signature of 4 NodeLists (or Node), not Strings
Correct ??

You can't just have multiple XPath statements as arguments to an extension
function call or am I mistaken ??

John G



Re: Extensions, best way to handle elements ??

Posted by Gary L Peskin <ga...@firstech.com>.
John Gentilin wrote:
> Using the XSL/XML below, the entry point to my function was called with a NodeList
> representing the children of my select and not the Element. When are
> ExtensionFunctions called with Elements instead of NodeLists.

The result of the select in XSLT is the XSLT type "node-set".  Your call
to the XConnector constructor contains a single argument which is the
node-set represented by the value of $cinfo.  When calling a java
function, the extension mechanism will find the constructor and compare
the parameter type in the java function to the xslt type in the caller.

The extension mechanism currently knows how to map an XSLT node-set to a
java NodeList.  It also knows how to map an XSLT node-set to a java Node
so you could specify 

  public XConnection(Node elem)
  {
    initFromElement( (Element) elem);
  }

The node passed is the first node in the node-set.

Unfortunately, in its current incarnation, the extension mechanism does
-not- know how to map from an XSLT node-set to a java Element so you
can't specify Element as your argument type in java.  This support is
planned.

Scott, do you think that I should work on adding this in now so that it
will make it into beta 2?  I think this is fairly low risk.  I'm not
sure how much functionality you'd like to be adding in to the code
during the beta period.

> The NodeList I received is populated with stree ElemeentImpl because I used the
> standard command line function but I am assuming  if the parser was
> initiated with a DOMSource other than stree, that I can't use the stree methods
> like getChildCount() and that I need to stick with only the DOM functions that
> stree ElementImpl implements.
> Is this true ??

I'm not sure if the stree gets built everytime or whether Xalan will
directly use a DOM implementation that was passed in.  Perhaps Scott or
Myriam or Joe or David or someone will know the answer to this.  In any
event, if you stick to the DOM Element functions you should be safe and
stand a chance of having an extension function that can be moved to
other transformers.

>and is the code below an efficient way to accomplish
> that goal ??

It seems like it will do the trick.  This should only get executed once
when the constructor is invoked (is that correct?) so it's not like it's
in some kind of tight loop in terms of the need to optimize it.  It does
seem like work, though, to get the info out.  Perhaps you'd consider
just having the constructor take four string arguments and then calling
it with those:

  select = 'sql:new(/DBINFO/dbdriver, /DBINFO/dburl, /DBINFO/user,
/DBINFO/password)'

Then, the user could specify constants if they wanted or concoct the
values of the arguments from some non-XML source.  Of course you could
do this with the body of a variable element but this might be more
straightforward for the common usage.  I dunno.

Gary