ogradyjp / ajaxslt

Automatically exported from code.google.com/p/ajaxslt
Other
0 stars 0 forks source link

Patch for several issues. #15

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
I hit some of the "not implemented yet" items and a few others I spotted
before deciding that this probably doesn't suit my purposes. Still, the
changes I've made may prove useful to you (or not), so here they are.

TODO Items done:
    local-name
    namespace-uri
    xsl:message

Bug in translate function.
To reproduce:
1. run stylesheet that will do translate('abc','aba','xaz')

Expected output is string 'xac' as spec says "If a character occurs more
than once in the second argument string, then the first occurrence
determines the replacement character". Instead output is "xzc" (string gets
changed first to "xbc" then "xac" and then "xzc").

XPath extensions for XSLT that were are not supported:
    current
    document (support added is not complete - also could fail on some
platforms, but will report this through a call to function-available properly.)
    element-available
    function-available
    generate-id
    system-property
Still not supported, but now report their not-implemented status:
    format-number
    key

Calls to empty function xpathLog do nothing. Replace with commented out;
someone implementing xpathLog can also do a find and replace to turn
"//xpathLog" to "xpathLog".

Original issue reported on code.google.com by j...@hackcraft.net on 21 May 2007 at 5:20

Attachments:

GoogleCodeExporter commented 8 years ago
Cool. Please post the unidiff of your change as a comment to this change, so 
it's
easier to see what it's about. Consider to split them up into separate changes, 
so
it's more obvious what they do. Thanks!

Original comment by me...@google.com on 21 May 2007 at 9:59

GoogleCodeExporter commented 8 years ago
Hmm. Did some unidiff's and notice a bug and also that I left out my changes to
xslt.js in that zip. Anyway, here's the changes to utils.js first:

6a7,69
> // URI functions. Regular expression adapted from that in RFC 2396.
> // (resolvePartialUri works for all of the examples given in that RFC, though
> // the alternative interpretation of ../../../g will be produced (not for the
> // reason suggested in that RFC - it's just the way it worked :)
> function parseUri(uri)
> {
>   return 
/^(([^:/?#]+):)?([/]{2}([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(uri);
> }
> function isRelativeUri(uri)
> {
>   return parseUri(uri)[1] == undefined;
> }
> function resolvePartialUri(base, partial)
> {
>   var arr = parseUri(partial);
>   if(arr[1] != undefined)//Uri is full.
>       return partial;
>   arrBase = parseUri(base);
>   if(arr[3] != undefined)
>       return arrBase[1] + partial;
>   if(arr[5] != undefined && arr[5].substring(0, 1) == '/')//abspath
>       return arrBase[1] + arrBase[3] + partial;
>   if(arr[5] != undefined && arr[5].length)
>   {
>       var steps = arr[5].split('/')
>       var stepsBase = arrBase[5].split('/');
>       stepsBase.length--;
>       for(var i = 0; i < steps.length; ++i)
>           switch(steps[i])
>           {
>               case '.':
>                   steps.splice(i--, 1);
>                   break;
>               case '..':
>                   steps.splice(i--, 1);
>                   if(i > -1)
>                   {
>                       steps.splice(0, 1);
>                       --i;
>                   }
>                   else if(stepsBase.length)
>                       stepsBase.length--;
>                   break;
>           }
>           return arrBase[1] + arrBase[3] + stepsBase.join('/') + '/' + 
steps.join('/') +
(arr[6] == undefined ? '' : arr[6]) + (arr[8] == undefined ? '' : arr[8]);
>   }
>   if(arr[6] != undefined)
>       return arrBase[1] + arrBase[3] + arrBase[5].substring(0,
arrBase[5].lastIndexOf('/') + 1) + arr[6] + (arr[8] == undefined ? '' : arr[8]);
>   if(arr[8] != undefined)
>       return arrBase[1] + arrBase[3] + arrBase[5] + (arrBase[6] == undefined ? '' 
:
arrBase[6]) + arr[8];
>   return arrBase[1] + arrBase[3] + arrBase[5] + (arrBase[6] == undefined ? '' :
arrBase[6]);
> }
> // Wraps around alert() so that we need not assume that these scripts are 
being
> // used in a context that offers alert() - i.e. a browser.
> // Also allows for termination by throwing an error.
> function reportMessage(msg, terminate)
> {
>   alert(msg);
>   //xpathLog((terminate ? 'Terminating Message: ' : 'Message: ') + msg);
>   if(terminate)
>       throw 'Terminating Message: ' + msg;
> }
> 
403a467,470
> function domCreateProcessingInstruction(doc, target, data) {//need to test 
level of
support
>   return doc.createProcessingInstruction(target, data);
> }
> 

Original comment by j...@hackcraft.net on 21 May 2007 at 11:01

GoogleCodeExporter commented 8 years ago
Now dom.js:

74a75,148
> //Returns true if an HTMLRequest object can be created.
> //Hence indicates if there is a chance of xmlLoad working
> //and hence that document() in XSLT can work.
> //Written by Jon Hanna <jon@hackcraft.net>.
> function xmlLoadAvailable()
> {
>   if (window.XMLHttpRequest) // Mozilla, Safari, Firefox...
>   {    
>       return true;
>   }
>   else if (window.ActiveXObject)// IE 
>   { 
>       try 
>       {
>           var test_request = new ActiveXObject("Msxml2.XMLHTTP");
>           return true;
>       } 
>       catch (e) 
>       {
>           try 
>           {
>               var test_request = new ActiveXObject("Microsoft.XMLHTTP");
>               return true;
>           } 
>           catch (e) {return false}
>       }
>   }
>   else
>       return false;
> }
> //Loads an XDocument from a URI.
> //Scheme support is browser-dependent, though all support HTTP at a minimum.
> //Written by Jon Hanna <jon@hackcraft.net>.
> //Amongst other things lets document() work (should we just use the DOM object
> //from XMLHttpRequest::responseXML on some browsers?)
> function xmlLoad(uri)
> {
>   var http_request;
>   if (window.XMLHttpRequest) // Mozilla, Safari, Firefox...
>   {    
>       http_request = new XMLHttpRequest();
>       if (http_request.overrideMimeType)
>       {
>           http_request.overrideMimeType('text/xml');
>       }
>   }
>   else if (window.ActiveXObject)// IE 
>   { 
>       try 
>       {
>           http_request = new ActiveXObject("Msxml2.XMLHTTP");
>       } 
>       catch (e) 
>       {
>           try 
>           {
>               http_request = new ActiveXObject("Microsoft.XMLHTTP");
>           } 
>           catch (e) {return null;}
>       }
>   }
>   else
>       return null;
> 
>   try
>   {
>       http_request.open('GET', uri, false);
>   }
>   catch(e){}
>   http_request.send(null);
>   var doc = xmlParse(http_request.responseText);
>   doc.baseUri = uri;
>   return doc;
> }
323a398,412
> //Base URI see http://www.w3.org/TR/xmlbase/
> //Find the nearest ancestor with an xml:base attribute. If absolute, that's 
the base
> //if relative it's relative to the next xml:base attribute. If we crawl as 
far as the
> //root then the base there is the URI it was loaded from (why this is defined
differently
> //on XDocument).
> XNode.prototype.getBaseUri = function()
> {
>   var decHere = this.getAttribute('xml:base');
>   if(decHere == null)
>       return this.parentNode ? this.parentNode.getBaseUri() : null;
>   if(isRelativeUri(decHere) && this.parentNode)
>       return resolvePartialUri(this.parentNode.getBaseUri(), decHere);
>   return decHere;
> }
> 
519a609
>   this.baseUri = '';
527a618,621
> XDocument.prototype.getBaseUri = function()
> {
>   return this.baseUri;
> }

Now xslt.js:

66c66
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
111c111
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
167c167
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
179c179
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
194c194
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
198c198
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
202c202
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
206,207c206,211
<       alert('not implemented: ' + nodename[1]);
<       break;
---
>       var msgOutput = domCreateDocumentFragment(new XDocument)
>       var msgOutputDocument = xmlOwnerDocument(output);
>       xsltChildNodes(input, template, msgOutput);
>       alert(msgOutput.toSource())
>       reportMessage(xmlValue(msgOutput), xmlGetAttribute(template, 
'terminate') ==
'yes')
>           break;
210c214
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
214c218
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
218c222
<       alert('error if here: ' + nodename[1]);
---
>       reportMessage('error if here: ' + nodename[1], true);
229c233
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
233c237
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
241c245
<       alert('not implemented: ' + nodename[1]);
---
>       reportMessage('not implemented: ' + nodename[1], true);
278c282
<       alert('error if here: ' + nodename[1]);
---
>       reportMessage('error if here: ' + nodename[1], true);
282c286
<       alert('error if here: ' + nodename[1]);
---
>       reportMessage('error if here: ' + nodename[1], true);
286c290
<       alert('error if here: ' + nodename[1]);
---
>       reportMessage('error if here: ' + nodename[1], true);

And here's the big one, xpath.js:

44c44
<   xpathLog('parse ' + expr);
---
>   //xpathLog('parse ' + expr);
49c49
<     xpathLog(' ... cached');
---
>     //xpathLog(' ... cached');
62c62
<     xpathLog(' ... simple');
---
>     //xpathLog(' ... simple');
69c69
<     xpathLog(' ... simple 2');
---
>     //xpathLog(' ... simple 2');
132c132
<       xpathLog('token: ' + match + ' -- ' + rule.label);
---
>       //xpathLog('token: ' + match + ' -- ' + rule.label);
141c141
<       xpathLog('DONE');
---
>       //xpathLog('DONE');
147c147
<       xpathLog('stack: ' + stackToString(stack));
---
>       //xpathLog('stack: ' + stackToString(stack));
151c151
<   xpathLog('stack: ' + stackToString(stack));
---
>   //xpathLog('stack: ' + stackToString(stack));
160,161c160
<   xpathLog('XPath parse: ' + parse_count + ' / ' +
<            lexer_count + ' / ' + reduce_count);
---
>   //xpathLog('XPath parse: ' + parse_count + ' / ' + lexer_count + ' / ' +
reduce_count);
203,206c202
<     xpathLog('reduce ' + cand.tag.label + ' ' + cand.prec +
<              ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
<                           (ahead.tag.left ? ' left' : '')
<                           : ' none '));
---
>     //xpathLog('reduce ' + cand.tag.label + ' ' + cand.prec + ' ahead ' + 
(ahead ?
ahead.tag.label + ' ' + ahead.prec + (ahead.tag.left ? ' left' : '') : ' none 
'));
216,219c212
<       xpathLog('shift ' + ahead.tag.label + ' ' + ahead.prec +
<                (ahead.tag.left ? ' left' : '') +
<                ' over ' + (cand ? cand.tag.label + ' ' +
<                            cand.prec : ' none'));
---
>       //xpathLog('shift ' + ahead.tag.label + ' ' + ahead.prec + 
(ahead.tag.left ?
' left' : '') + ' over ' + (cand ? cand.tag.label + ' ' + cand.prec : ' none'));
673c666
<     alert('not implemented: axis namespace');
---
>     reportMessage('not implemented: axis namespace', true);
813c806
<     xpathLog('XPath NO SUCH FUNCTION ' + fn);
---
>     //xpathLog('XPath NO SUCH FUNCTION ' + fn);
865c858,862
<     alert('not implmented yet: XPath function local-name()');
---
>   //Let name() do most of the work!
>     assert(this.args.length == 1 || this.args.length == 0);
>     var name = this.xpathfunctions['name'].call(this, ctx).stringValue();
>     var idx = name.indexOf(':');
>     return new StringValue(idx == -1 ? name : name.substring(idx + 1, 
name.length));
868,869c865,879
<   'namespace-uri': function(ctx) {
<     alert('not implmented yet: XPath function namespace-uri()');
---
>   'namespace-uri': function(ctx) {//TODO. This depends upon the namespace 
handling
being "TODO" in XNode and XDocument objects. Must fix this function once those 
have
full namespace support.
>     var name = this.xpathfunctions['name'].call(this, ctx).stringValue();
>     var idx = name.indexOf(':');
>     var nsAtt = idx == -1 ? 'xmlns' : 'xmlns:' + name.substring(0, idx);
> 
>     var xmlns;
>     var n = ctx.node;
>     while (n && n != n.parentNode /* just in case ... */) {
>       xmlns = n.getAttribute(nsAtt);
>       if (xmlns) {
>         break;
>       }
>       n = n.parentNode;
>     }
>     return new StringValue(xmlns ? xmlns : '');
898,899c908,910
<     var ret = '';
<     for (var i = 0; i < this.args.length; ++i) {
---
>       assert(this.args.length > 1);
>     var ret = this.args[0].evaluate(ctx).stringValue(); //<jon@hackcraft.net>:
minor efficiency gain.
>     for (var i = 1; i < this.args.length; ++i) {
996c1007,1009
<       s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
---
>       if(s1.indexOf(s1.charAt(i)) == i)  {//repeated characters in s1 should 
NOT be
used.
>         s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
>       }
1124c1137,1290
<   }
---
>   },
>   // Following functions are the extensions to 
>   // XPath added by XSLT. <jon@hackcraft.net>
>   'current': function(ctx){
>     assert(this.args.length == 0);
>       var ret = [];
>       ret.push(_current_node);
>       return new NodeSetValue(ret);
>   },
>   'document': function(ctx){
>     assert(this.args.length == 1 || this.args.length == 2);
>     var nodes = this.args.length > 1 ? 
this.args[0].evaluate(ctx).nodeSetValue() :
null;
>     var baseUriNode = nodes && nodes.length ? nodes[0] : null; //TODO: Make 
it use
first node in *document order*.
>   var baseUri = baseUriNode ? baseUriNode.getBaseUri() : null;
> 
>   var firstArg = this.args[0].evaluate(ctx);
>   var uris = []
>   if(firstArg.type == 'node-set')
>   {
>       var nodeSet = firstArg.nodeSetValue()
>       for(var i = 0; i != nodeSet.length; ++i)
>           if(baseUri)
>               uris.push(resolvePartialUri(baseUri, xmlValue(nodeSet[i])));
>           else
>               uris.push(resolvePartialUri(nodeSet[i].getBaseUri(), 
xmlValue(nodeSet[i])));
>   }
>   else
>   {
>       if(baseUri)
>           uris.push(resolvePartialUri(baseUri, firstArg.stringValue()));
>       else
>           uris.push(firstArg.stringValue());
>   }
>     var ret = [];
>   for(var i = 0; i != uris.length; ++i)
>   {
>       var newDom = xmlLoad(uris[i]);
>       ret.push(newDom);
>     }
>     return new NodeSetValue(ret);
>   },
>   'element-available': function(ctx){
>       assert(this.args.length == 1);
>     var elName = this.args[0].evaluate(ctx).stringValue();
>     if(elName.indexOf('xslt:') == 0)//fake support for commonly used xslt 
namespace
prefix
>       elName = elName.substring(5, elName.length);
>     else if(elName.indexOf('xsl:') == 0)//fake support for commonly used xslt
namespace prefix
>       elName = elName.substring(4, elName.length);
>       
>     if(elName.indexOf(':') != -1)//Non-XSLT element - none supported.
>       return new BooleanValue(false);
>     switch(elName)
>     {
>       case 'apply-templates':
>       case 'attribute':
>       case 'call-template':
>       case 'choose':
>       case 'comment':
>       case 'copy':
>       case 'copy-of':
>       case 'element':
>       case 'for-each':
>       case 'if':
>       case 'message':
>       case 'otherwise':
>       case 'output':
>       case 'sort':
>       case 'stylesheet':
>       case 'transform':
>       case 'template':
>       case 'text':
>       case 'value-of':
>       case 'param':
>       case 'variable':
>       case 'when':
>       case 'with-param':
>           return new BooleanValue(true);
>       default:
>           return new BooleanValue(false);
>     }
>   },
>   'format-number': function(ctx){
>       reportMessage('not implmented yet: XSLT extension XPath function
format-number()', true);
>   },
>   'function-available': function(ctx){
>     assert(this.args.length == 1);
>     var funcName = this.args[0].evaluate(ctx).stringValue();
>     if(funcName.indexOf(':') != -1)//Non-XSLT extended function - none 
supported.
>       return new BooleanValue(false);
>     if(!FunctionCallExpr.prototype.xpathfunctions[funcName])//Not defined 
here at all.
>       return new BooleanValue(false);
>     switch(funcName)
>     {
>       case 'format-number':
>       case 'key':
>       case 'unparsed-entity-uri':
>           return new BooleanValue(false);
>       case 'document':
>           return new BooleanValue(xmlLoadAvailable());
>       default:
>           return new BooleanValue(true);
>     }
>     
>   },
>   'generate-id': function(ctx){
>       //The spec insists that we guarantee a consisten id such that if 
generate-id
>       //is called on the same node twice we return the same id, but we would 
never
>       //return the same id for two different ones. This method is far from 
efficient
>       //nor concise in the ids generated, but it is simple and fulfils the spec.
>     assert(this.args.length == 1 || this.args.length == 0);
>     var n;
>     if (this.args.length == 0) {
>       n = ctx.node;
>     } else {
>       n = this.args[0].evaluate(ctx).nodeSetValue()[0];
>     }
>     var genId = '';
>     while (n && n != n.parentNode /* just in case ... */) {
>       var c = 0;
>       var p = n.previousSibling;
>       while(p)
>       {
>           ++c;
>           p = p.previousSibling;
>       }
>       genId = '_' + c + genId;
>       n = n.parentNode;
>     }
>     return new StringValue(genId);
>   },
>   'key': function(ctx){
>       reportMessage('not implmented yet: XSLT extension XPath function key()', 
true);
>   },
>   'system-property': function(ctx){
>     assert(this.args.length == 1);
>     var propName = this.args[0].evaluate(ctx).stringValue();
>     var idx = propName.indexOf(':');
>     if(idx != -1)//TODO: Fix namespace check. Until then, assume everything 
is in
the XSLT namespace and take local part:
>       propName = propName.substring(idx + 1, propName.length);
>     switch(propName)
>     {
>       case 'version':
>           return new StringValue('1.0');
>       case 'vendor':
>           return new StringValue('Google Code');
>       case 'vendor-url':
>           return new StringValue('http://code.google.com/');
>       default:
>           return new StringValue('');
>     } 
>   },
>   'unparsed-entity-uri': function(ctx){
>       reportMessage('not implmented yet: XSLT extension XPath function
unparsed-entity-uri()', true);
>   },
1267c1433
<       alert('BinaryExpr.evaluate: ' + this.op.value);
---
>       reportMessage('BinaryExpr.evaluate: ' + this.op.value, true);
1640c1806
< var xpathAxesRe = [
---
> var xpathAxesRe = '(' + [
1654c1820
< ].join('|');
---
> ].join('|') + ')(?=::)';
1927c2093
<    [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20,
---
>    [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 31,
2017c2183
<   xpathLog('XPath parse INIT: ' + k + ' rules');
---
>   //xpathLog('XPath parse INIT: ' + k + ' rules');
2055c2221
<   xpathLog('XPath parse INIT: ' + xpathRules.length + ' rule bins');
---
>   //xpathLog('XPath parse INIT: ' + xpathRules.length + ' rule bins');
2064,2065c2230
<   xpathLog('XPath parse INIT: ' + (sum / xpathRules.length) +
<            ' average bin size');
---
>   //xpathLog('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin 
size');
2162c2327
< 
---
> var _current_node;//<jon@hackcraft.net>: Used for current(). TODO - this is 
global
and will have problems if two transforms are simultaneous
2165a2331
>   _current_node = context.node;//<jon@hackcraft.net>: Used for current().

Original comment by j...@hackcraft.net on 21 May 2007 at 11:03

GoogleCodeExporter commented 8 years ago
Gosh, did more than I'd realised :).

The change:

1640c1806
< var xpathAxesRe = [
---
> var xpathAxesRe = '(' + [
1654c1820
< ].join('|');
---
> ].join('|') + ')(?=::)';

is because otherwise the parsing matched 'namespace' in 'namespace-uri()' and
interpretted that as identifying the namespace axis. The above change means 
that axes
have to be followed by :: to match.

The line:
>       var msgOutputDocument = xmlOwnerDocument(output);

Can be dropped - it's just from what I was thinking about at the time!

Original comment by j...@hackcraft.net on 21 May 2007 at 11:28

GoogleCodeExporter commented 8 years ago
Any chance of a new version release incorporating this patch? 

Original comment by albert.l...@gmail.com on 28 Sep 2007 at 12:16

GoogleCodeExporter commented 8 years ago
This patch is huge, and hard to make sense of.  It would be a lot of work to 
apply
this patch correctly; I doubt we'll want to apply it as-is.  (Especially since 
it
introduces several TODOs and includes no tests.)

A few comments:

1) We need to provide more complete support for namespace resolution; this patch
gestures in that direction, but doesn't go all the way, and I think we'd need 
to cut
out the changes submitted here if/when we ever did implement namespace 
resolution.

2) The implementation of document() relies on XmlHttpRequest, which is puzzling,
because if you've got XHR, you probably have XSLT support built-in.

3) The use of a global variable to handle current() is concerning.

If somebody can call out one part of the patch that they need in the release 
(perhaps
filing a separate bug?) we can talk about implementing these features (perhaps 
using
part of this patch?) on a one-off basis.

Original comment by DanFabul...@gmail.com on 7 Nov 2007 at 4:56

GoogleCodeExporter commented 8 years ago
"2) The implementation of document() relies on XmlHttpRequest, which is 
puzzling,
because if you've got XHR, you probably have XSLT support built-in."

Safari and Google Chrome have XmlHttpRequest but don't process the document 
function
using the built in XSLTProcessor object, perhaps you could rethink including at 
least
this part of this patch?

Original comment by neerol...@gmail.com on 11 Sep 2008 at 4:16