| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache |
| * XMLBeans", nor may "Apache" appear in their name, without prior |
| * written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2000-2003 BEA Systems |
| * Inc., <http://www.bea.com/>. For more information on the Apache Software |
| * Foundation, please see <http://www.apache.org/>. |
| */ |
| |
| package org.apache.xmlbeans.impl.store; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import org.apache.xmlbeans.impl.store.Splay.CursorGoober; |
| import org.apache.xmlbeans.impl.store.Cursor.Selections; |
| import org.apache.xmlbeans.impl.store.Cursor.PathEngine; |
| import org.apache.xmlbeans.XmlOptions; |
| import org.apache.xmlbeans.XmlCursor; |
| import org.apache.xmlbeans.XmlException; |
| import org.apache.xmlbeans.XmlObject; |
| import org.apache.xmlbeans.impl.common.XPath; |
| import org.apache.xmlbeans.impl.values.TypeStore; |
| |
| /** |
| * 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 ); |
| |
| 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) |
| { |
| assert ! options.hasOption( _useXbeanForXpath ); |
| |
| path = (Path) _xqrlPathCache.get( pathExpr ); |
| |
| if (path == null) |
| { |
| path = XqrlPathImpl.create( pathExpr, currentNodeVar ); |
| |
| 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; |
| String currentNodeVar = getCurrentNodeVar( options ); |
| |
| synchronized ( _xqrlQueryCache ) |
| { |
| query = (Query) _xqrlQueryCache.get( queryExpr ); |
| |
| if (query == null) |
| { |
| query = XqrlDelegate.compileQuery( queryExpr, currentNodeVar ); |
| |
| 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, String currentNodeVar ) |
| { |
| return new XqrlPathImpl( |
| pathExpr, |
| XqrlDelegate.compilePath( pathExpr, currentNodeVar ) ); |
| } |
| |
| 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 HashMap _xqrlPathCache = new HashMap(); |
| private static HashMap _xbeanPathCache = new HashMap(); |
| private static HashMap _xqrlQueryCache = new HashMap(); |
| } |