blob: ac54b1aae00c4fb0dcb3a5d121d3caee738892c3 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.store;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlRuntimeException;
import org.apache.xmlbeans.impl.common.XPath;
import org.apache.xmlbeans.impl.store.Cursor.PathEngine;
import org.apache.xmlbeans.impl.store.Cursor.Selections;
import java.util.HashMap;
import java.util.List;
/**
* Represents a precompiled path expression
*/
public abstract class Path
{
static Selections newSelections ( )
{
return new Selections();
}
static PathEngine select (
Root r, Splay s, int p, String pathExpr, XmlOptions options )
{
Path path = getPath( pathExpr, options );
return (path == null) ? null : path.execute( r, s, p, options );
}
public static String _useXqrlForXpath = "use xqrl for xpath";
public static String _useXbeanForXpath = "use xbean for xpath";
public static Path getPath ( String pathExpr )
{
return getPath( pathExpr, false, null );
}
public static Path getPath ( String pathExpr, XmlOptions options )
{
return
getPath(
pathExpr,
XmlOptions.maskNull( options ).hasOption( _useXqrlForXpath ),
options );
}
public static Path getPath ( String pathExpr, boolean xqrl, XmlOptions options )
{
Object path = null;
synchronized ( _xbeanPathCache )
{
if (!xqrl)
{
path = _xbeanPathCache.get( pathExpr );
}
if (path == null)
path = _xqrlPathCache.get( pathExpr );
if (path == null)
{
String pathStr = getCompiledPath( pathExpr, xqrl, options );
path =
(pathStr == null)
? null
: getPath( pathExpr, xqrl, options );
}
}
return (Path) path;
}
public static String getCompiledPath ( String pathExpr, XmlOptions options )
{
return getCompiledPath( pathExpr, false, options );
}
private static String getCurrentNodeVar ( XmlOptions options )
{
String currentNodeVar = "this";
options = XmlOptions.maskNull( options );
if (options.hasOption( XmlOptions.XQUERY_CURRENT_NODE_VAR ))
{
currentNodeVar = (String) options.get( XmlOptions.XQUERY_CURRENT_NODE_VAR );
if (currentNodeVar.startsWith( "$" ))
{
throw
new IllegalArgumentException(
"Omit the '$' prefix for the current node variable" );
}
}
return currentNodeVar;
}
public static String getCompiledPath ( String pathExpr, boolean xqrl, XmlOptions options )
{
Path path = null;
options = XmlOptions.maskNull( options );
// ensure options doesn't contain XQuery variable mapping.
// someday we should implement this in all the pathing engines
// but for now it only applies to the xqrl.
if (options.hasOption( XmlOptions.XQUERY_VARIABLE_MAP ))
{
throw
new XmlRuntimeException(
"XmlOptions.XQUERY_VARIABLE_MAP is not allowed in XPath expressions.");
}
String currentNodeVar = getCurrentNodeVar( options );
synchronized ( _xbeanPathCache )
{
assert (xqrl |= options.hasOption( _useXqrlForXpath )) || true;
if (!xqrl || options.hasOption( _useXbeanForXpath ))
{
path = (Path) _xbeanPathCache.get( pathExpr );
if (path == null)
{
path = XbeanPathImpl.create( pathExpr, currentNodeVar );
if (path != null)
_xbeanPathCache.put( path.getPathExpr(), path );
}
if (path == null)
{
path = JaxenPathImpl.create( pathExpr, currentNodeVar );
if (path != null)
_xbeanPathCache.put( path.getPathExpr(), path );
}
}
if (path == null)
{
assert ! options.hasOption( _useXbeanForXpath );
path = (Path) _xqrlPathCache.get( pathExpr );
if (path == null)
{
path = XqrlPathImpl.create( pathExpr, options );
if (path != null)
_xqrlPathCache.put( path.getPathExpr(), path );
}
}
}
return path == null ? null : path.getPathExpr();
}
public interface Query
{
PathEngine executePath ( Root r, Splay s, int p, XmlOptions options );
XmlCursor executeQuery ( Cursor c, XmlOptions options );
XmlObject[] executeQuery ( Type type, XmlOptions options );
String getQueryExpr ( );
}
// public static Query getQuery ( String queryExpr )
// {
// return getQuery( queryExpr, null );
// }
public static Query getQuery ( String queryExpr, XmlOptions options )
{
Object query = null;
synchronized ( _xqrlQueryCache )
{
query = _xqrlQueryCache.get( queryExpr );
if (query == null)
{
String queryStr = getCompiledQuery( queryExpr, options );
query =
(queryStr == null) ? null : getQuery( queryExpr, options );
}
}
return (Query) query;
}
public static String getCompiledQuery ( String queryExpr, XmlOptions options )
{
Query query = null;
synchronized ( _xqrlQueryCache )
{
query = (Query) _xqrlQueryCache.get( queryExpr );
if (query == null)
{
query = XqrlDelegate.compileQuery( queryExpr, options );
if (query != null)
_xqrlQueryCache.put( query.getQueryExpr(), query );
}
}
return query == null ? null : query.getQueryExpr();
}
public static XmlCursor query (
Cursor c, String queryExpr, XmlOptions options )
{
Query query = getQuery( queryExpr, options );
return (query == null) ? null : query.executeQuery( c, options );
}
public static XmlObject[] query (
Type type, String queryExpr, XmlOptions options )
{
Query query = getQuery( queryExpr, options );
return (query == null) ? null : query.executeQuery( type, options );
}
protected abstract PathEngine execute (
Root r, Splay s, int p, XmlOptions options );
protected abstract String getPathExpr ( );
private static class XqrlPathImpl extends Path
{
private XqrlPathImpl ( String pathExpr, Query compiledPath )
{
_pathExpr = pathExpr;
_compiledPath = compiledPath;
}
private String _pathExpr;
private Query _compiledPath;
static Path create ( String pathExpr, XmlOptions options )
{
return new XqrlPathImpl(
pathExpr,
XqrlDelegate.compilePath( pathExpr, options ) );
}
protected PathEngine execute (
Root r, Splay s, int p, XmlOptions options )
{
return _compiledPath.executePath( r, s, p, options );
}
protected String getPathExpr ( ) { return _pathExpr; }
}
//
//
//
private static final class XbeanPathImpl extends Path
{
private XbeanPathImpl (
XPath xpath, String pathExpr, String currentNodeVar )
{
_pathExpr = pathExpr;
_xpath = xpath;
_currentNodeVar = currentNodeVar;
}
// try
// {
// String currentNodeVar = "this";
//
// if (XmlOptions.XQUERY_CURRENT_NODE_VAR.has( options ))
// {
// currentNodeVar =
// "$" + XmlOptions.XQUERY_CURRENT_NODE_VAR.get( options );
//
// if (currentNodeVar.startsWith( "$$" ))
// {
// throw
// new IllegalArgumentException(
// "Omit the '$p' refix for the current " +
// "node variable" );
// }
// }
static Path create ( String pathExpr, String currentNodeVar )
{
assert !currentNodeVar.startsWith( "$" );
try
{
return
new XbeanPathImpl(
XPath.compileXPath( pathExpr, currentNodeVar ),
pathExpr, currentNodeVar );
}
catch ( XPath.XPathCompileException e )
{
return null;
}
}
protected String getPathExpr ( ) { return _pathExpr; }
protected PathEngine execute (
Root r, Splay s, int p, XmlOptions options )
{
// The builtin XPath engine works only on containers. Delegate to
// xqrl otherwise. Also, if the path had a //. at the end, the
// simple xpath engine can't do the generate case, it only handles
// attrs and elements.
if (p != 0 || !s.isContainer() || _xpath.sawDeepDot())
{
// If the xbean path compiler could could handle the path, then
// the xqrl compiler better be able to!
try
{
return
getPath( _pathExpr, true, null ).
execute( r, s, p, options );
}
catch ( Throwable e )
{
throw new RuntimeException( "Can't compile path", e );
}
}
return new XBeanPathEngine( _xpath, r, s );
}
// TODO - because this xpath engine does not use a saver, any attributes in the
// path which refer to namesapce attributes will require us to run the XQRL
// path engine which is based on the saver.
private static class XBeanPathEngine
extends XPath.ExecutionContext implements PathEngine
{
XBeanPathEngine ( XPath xpath, Root r, Splay s )
{
assert s.isContainer();
_root = r;
_curr = _top = s;
_version = r.getVersion();
init( xpath );
}
public boolean next ( Selections selections )
{
int initialSize = selections.currentSize();
for ( ; ; )
{
if (_root.getVersion() != _version)
throw new IllegalStateException( "Document changed" );
if (_curr == null)
return false;
advance( selections );
if (initialSize < selections.currentSize())
return true;
}
}
private Splay doAttrs ( int ret, Splay s, Selections selections )
{
if ((ret & ATTRS) == 0)
return null;
for ( s = _curr.nextSplay() ; s.isAttr() ;
s = s.nextSplay() )
{
if (s.isNormalAttr() && attr( s.getName() ))
selections.add( _root, s );
}
return s;
}
private void advance ( Selections selections )
{
if (_curr == _top)
{
int ret = start();
if ((ret & HIT) != 0)
selections.add( _root, _curr );
Splay s = doAttrs( ret, _curr, selections );
if ((ret & DESCEND) == 0 || _curr.isLeaf())
_curr = null;
else
_curr = s == null ? _curr.nextNonAttrSplay() : s;
return;
}
for ( ; ; )
{
if (_curr.isFinish())
{
if (_curr.getContainer() == _top)
_curr = null;
else
{
end();
_curr = _curr.nextSplay();
}
return;
}
if (_curr.isBegin())
{
int ret = element( _curr.getName() );
if ((ret & HIT) != 0)
selections.add( _root, _curr );
Splay s = doAttrs( ret, _curr, selections );
if (_curr.isLeaf())
{
end();
_curr = s == null ? _curr.nextNonAttrSplay() : s;
}
else if ((ret & DESCEND) == 0)
_curr = ((Splay.Container) _curr).getFinish();
else
_curr = s == null ? _curr.nextNonAttrSplay() : s;
return;
}
_curr = _curr.nextSplay();
}
}
private Root _root;
private long _version;
private Splay _top;
private Splay _curr;
}
private String _pathExpr;
private XPath _xpath;
private String _currentNodeVar;
}
private static final class JaxenPathImpl extends Path
{
private String _pathExpr;
private JaxenXBeansDelegate.SelectPathInterface _xpathImpl;
private JaxenPathImpl (
JaxenXBeansDelegate.SelectPathInterface xpathImpl, String pathExpr )
{
_xpathImpl = xpathImpl;
_pathExpr = pathExpr;
}
static Path create ( String pathExpr, String currentNodeVar )
{
assert !currentNodeVar.startsWith( "$" ); // cezar review with ericvas
JaxenXBeansDelegate.SelectPathInterface impl = JaxenXBeansDelegate.createInstance( pathExpr );
if (impl == null)
return null;
return new JaxenPathImpl( impl , pathExpr );
}
protected String getPathExpr ( ) { return _pathExpr; }
protected PathEngine execute ( Root r, Splay s, int p, XmlOptions options )
{
return new JaxenPathEngine( _xpathImpl, r, s, p);
}
private static class JaxenPathEngine
extends XPath.ExecutionContext implements PathEngine
{
JaxenPathEngine( JaxenXBeansDelegate.SelectPathInterface xpathImpl, Root r, Splay s, int p )
{
_jaxenXpathImpl = xpathImpl;
_root = r;
_splay = s;
_p = p;
_version = r.getVersion();
}
public boolean next ( Selections selections )
{
if (!_firstCall)
return false;
_firstCall = false;
if (_root.getVersion() != _version)
throw new IllegalStateException( "Document changed" );
List resultsList;
Cursor cur = new Cursor(_root, _splay, _p);
resultsList = _jaxenXpathImpl.selectPath(cur);
int i;
for (i = 0; i<resultsList.size(); i++)
{
XmlCursor.XmlBookmark b = (XmlCursor.XmlBookmark)resultsList.get(i);
Splay.Annotation ann = ((Splay.Annotation)b._currentMark);
selections.add(_root, ann.getSplay(), ann.getPos());
}
cur.dispose();
_root = null;
_splay = null;
_jaxenXpathImpl = null;
return false;
}
private JaxenXBeansDelegate.SelectPathInterface _jaxenXpathImpl;
private Root _root;
private Splay _splay;
private int _p;
private long _version;
private boolean _firstCall = true;
}
}
private static HashMap _xqrlPathCache = new HashMap();
private static HashMap _xbeanPathCache = new HashMap();
private static HashMap _xqrlQueryCache = new HashMap();
}