/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.datastore.neo4j.query.impl; import org.hibernate.engine.query.spi.ParameterParser.Recognizer; import org.parboiled.BaseParser; import org.parboiled.Rule; import org.parboiled.annotations.SkipNode; import org.parboiled.annotations.SuppressSubnodes; import org.parboiled.support.StringVar; /** * Simple parser for Neo4j queries which recognizes named parameters. * * @author Gunnar Morling */ // Use @BuildParseTree together with ParseTreeUtils#printNodeTree() for debugging purposes //@BuildParseTree public class QueryParser extends BaseParser<Recognizer> { final Recognizer journaler; final RecognizerAdapter adapter; public QueryParser(Recognizer journaler) { this.journaler = journaler; this.adapter = new RecognizerAdapter( journaler ); } public Rule Query() { return Sequence( QueryParts(), EOI, push( journaler ) ); } @SkipNode public Rule QueryParts() { return ZeroOrMore( QueryPart() ); } @SkipNode public Rule QueryPart() { return FirstOf( Quoted(), Escaped(), NamedParameter(), Other() ); } @SuppressSubnodes public Rule NamedParameter() { StringVar name = new StringVar( "" ); return Sequence( ParameterBeginDelimiter(), ZeroOrMore( WhiteSpace() ), OneOrMore( Alphanumeric() ), name.set( match() ), ZeroOrMore( WhiteSpace() ), ParameterEndDelimiter(), adapter.addNamedParameter( name.get(), currentIndex() ) ); } @SuppressSubnodes public Rule Quoted() { return Sequence( QuoteDelimiter(), ZeroOrMore( FirstOf( EscapedQuoteDelimiter(), Sequence( TestNot( QuoteDelimiter() ), ANY ) ) ), QuoteDelimiter() ); } @SuppressSubnodes public Rule Escaped() { return Sequence( EscapeDelimiter(), ZeroOrMore( FirstOf( EscapedEscapeDelimiter(), Sequence( TestNot( EscapeDelimiter() ), ANY ) ) ), EscapeDelimiter() ); } @SuppressSubnodes public Rule Other() { return OneOrMore( TestNot( NamedParameter() ), TestNot( Quoted() ), TestNot( Escaped() ), ANY ); } public Rule QuoteDelimiter() { return Ch( '\'' ); } public Rule EscapedQuoteDelimiter() { return Sequence( Ch( '\\' ), Ch( '\'' ) ); } public Rule EscapeDelimiter() { return Ch( '`' ); } public Rule EscapedEscapeDelimiter() { return Sequence( Ch( '\\' ), Ch( '`' ) ); } public Rule ParameterBeginDelimiter() { return Ch( '{' ); } public Rule ParameterEndDelimiter() { return Ch( '}' ); } public Rule Alphanumeric() { return FirstOf( Letter(), Digit() ); } public Rule Letter() { return FirstOf( CharRange( 'a', 'z' ), CharRange( 'A', 'Z' ) ); } public Rule Digit() { return CharRange( '0', '9' ); } public Rule WhiteSpace() { return OneOrMore( AnyOf( " \t\f" ) ); } /** * Wraps a {@link Recognizer} so it can be invoked by the parser (which requires the methods to return a * {@code boolean}). * * @author Gunnar Morling */ private static class RecognizerAdapter { private final Recognizer recognizer; private RecognizerAdapter(Recognizer recognizer) { this.recognizer = recognizer; } private boolean addNamedParameter(String name, int position) { recognizer.namedParameter( name, position ); return true; } } }