org.apache.lucene.queryparser.surround.parser.QueryParser.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.queryparser.surround.parser.QueryParser.java

Source

/* Generated By:JavaCC: Do not edit this line. QueryParser.java */
package org.apache.lucene.queryparser.surround.parser;

import java.util.ArrayList;
import java.util.List;
import java.io.StringReader;

import org.apache.lucene.queryparser.surround.query.SrndQuery;
import org.apache.lucene.queryparser.surround.query.FieldsQuery;
import org.apache.lucene.queryparser.surround.query.OrQuery;
import org.apache.lucene.queryparser.surround.query.AndQuery;
import org.apache.lucene.queryparser.surround.query.NotQuery;
import org.apache.lucene.queryparser.surround.query.DistanceQuery;
import org.apache.lucene.queryparser.surround.query.SrndTermQuery;
import org.apache.lucene.queryparser.surround.query.SrndPrefixQuery;
import org.apache.lucene.queryparser.surround.query.SrndTruncQuery;

/**
 * This class is generated by JavaCC.  The only method that clients should need
 * to call is {@link #parse parse()}.
 *
    
 * <p>This parser generates queries that make use of position information
 *   (Span queries). It provides positional operators (<code>w</code> and
 *   <code>n</code>) that accept a numeric distance, as well as boolean
 *   operators (<code>and</code>, <code>or</code>, and <code>not</code>,
 *   wildcards (<code>*</code> and <code>?</code>), quoting (with
 *   <code>"</code>), and boosting (via <code>^</code>).</p>
    
 *  <p>The operators (W, N, AND, OR, NOT) can be expressed lower-cased or
 *   upper-cased, and the non-unary operators (everything but NOT) support
 *   both infix <code>(a AND b AND c)</code> and prefix <code>AND(a, b,
 *   c)</code> notation. </p>
     
 *  <p>The W and N operators express a positional relationship among their
 *  operands.  W is ordered, and N is unordered.  The distance is 1 by
 *  default, meaning the operands are adjacent, or may be provided as a
 *  prefix from 2-99.  So, for example, 3W(a, b) means that terms a and b
 *  must appear within three positions of each other, or in other words, up
 *  to two terms may appear between a and b.  </p>
 */

public class QueryParser implements QueryParserConstants {
    static final int MINIMUM_PREFIX_LENGTH = 3;
    static final int MINIMUM_CHARS_IN_TRUNC = 3;
    static final String TRUNCATION_ERROR_MESSAGE = "Too unrestrictive truncation: ";
    static final String BOOST_ERROR_MESSAGE = "Cannot handle boost value: ";

    /* CHECKME: These should be the same as for the tokenizer. How? */
    static final char TRUNCATOR = '*';
    static final char ANY_CHAR = '?';
    static final char FIELD_OPERATOR = ':';

    static public SrndQuery parse(String query) throws ParseException {
        QueryParser parser = new QueryParser();
        return parser.parse2(query);
    }

    public QueryParser() {
        this(new FastCharStream(new StringReader("")));
    }

    public SrndQuery parse2(String query) throws ParseException {
        ReInit(new FastCharStream(new StringReader(query)));
        try {
            return TopSrndQuery();
        } catch (TokenMgrError tme) {
            throw new ParseException(tme.getMessage());
        }
    }

    protected SrndQuery getFieldsQuery(SrndQuery q, ArrayList<String> fieldNames) {
        /* FIXME: check acceptable subquery: at least one subquery should not be
         * a fields query.
         */
        return new FieldsQuery(q, fieldNames, FIELD_OPERATOR);
    }

    protected SrndQuery getOrQuery(List<SrndQuery> queries, boolean infix, Token orToken) {
        return new OrQuery(queries, infix, orToken.image);
    }

    protected SrndQuery getAndQuery(List<SrndQuery> queries, boolean infix, Token andToken) {
        return new AndQuery(queries, infix, andToken.image);
    }

    protected SrndQuery getNotQuery(List<SrndQuery> queries, Token notToken) {
        return new NotQuery(queries, notToken.image);
    }

    protected static int getOpDistance(String distanceOp) {
        /* W, 2W, 3W etc -> 1, 2 3, etc. Same for N, 2N ... */
        return distanceOp.length() == 1 ? 1 : Integer.parseInt(distanceOp.substring(0, distanceOp.length() - 1));
    }

    protected static void checkDistanceSubQueries(DistanceQuery distq, String opName) throws ParseException {
        String m = distq.distanceSubQueryNotAllowed();
        if (m != null) {
            throw new ParseException("Operator " + opName + ": " + m);
        }
    }

    protected SrndQuery getDistanceQuery(List<SrndQuery> queries, boolean infix, Token dToken, boolean ordered)
            throws ParseException {
        DistanceQuery dq = new DistanceQuery(queries, infix, getOpDistance(dToken.image), dToken.image, ordered);
        checkDistanceSubQueries(dq, dToken.image);
        return dq;
    }

    protected SrndQuery getTermQuery(String term, boolean quoted) {
        return new SrndTermQuery(term, quoted);
    }

    protected boolean allowedSuffix(String suffixed) {
        return (suffixed.length() - 1) >= MINIMUM_PREFIX_LENGTH;
    }

    protected SrndQuery getPrefixQuery(String prefix, boolean quoted) {
        return new SrndPrefixQuery(prefix, quoted, TRUNCATOR);
    }

    protected boolean allowedTruncation(String truncated) {
        /* At least 3 normal characters needed. */
        int nrNormalChars = 0;
        for (int i = 0; i < truncated.length(); i++) {
            char c = truncated.charAt(i);
            if ((c != TRUNCATOR) && (c != ANY_CHAR)) {
                nrNormalChars++;
            }
        }
        return nrNormalChars >= MINIMUM_CHARS_IN_TRUNC;
    }

    protected SrndQuery getTruncQuery(String truncated) {
        return new SrndTruncQuery(truncated, TRUNCATOR, ANY_CHAR);
    }

    final public SrndQuery TopSrndQuery() throws ParseException {
        SrndQuery q;
        q = FieldsQuery();
        jj_consume_token(0);
        {
            if (true)
                return q;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery FieldsQuery() throws ParseException {
        SrndQuery q;
        ArrayList<String> fieldNames;
        fieldNames = OptionalFields();
        q = OrQuery();
        {
            if (true)
                return (fieldNames == null) ? q : getFieldsQuery(q, fieldNames);
        }
        throw new Error("Missing return statement in function");
    }

    final public ArrayList<String> OptionalFields() throws ParseException {
        Token fieldName;
        ArrayList<String> fieldNames = null;
        label_1: while (true) {
            if (jj_2_1(2)) {
                ;
            } else {
                break label_1;
            }
            // to the colon
            fieldName = jj_consume_token(TERM);
            jj_consume_token(COLON);
            if (fieldNames == null) {
                fieldNames = new ArrayList<String>();
            }
            fieldNames.add(fieldName.image);
        }
        {
            if (true)
                return fieldNames;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery OrQuery() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries = null;
        Token oprt = null;
        q = AndQuery();
        label_2: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case OR:
                ;
                break;
            default:
                jj_la1[0] = jj_gen;
                break label_2;
            }
            oprt = jj_consume_token(OR);
            /* keep only last used operator */
            if (queries == null) {
                queries = new ArrayList<SrndQuery>();
                queries.add(q);
            }
            q = AndQuery();
            queries.add(q);
        }
        {
            if (true)
                return (queries == null) ? q : getOrQuery(queries, true /* infix */, oprt);
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery AndQuery() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries = null;
        Token oprt = null;
        q = NotQuery();
        label_3: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case AND:
                ;
                break;
            default:
                jj_la1[1] = jj_gen;
                break label_3;
            }
            oprt = jj_consume_token(AND);
            /* keep only last used operator */
            if (queries == null) {
                queries = new ArrayList<SrndQuery>();
                queries.add(q);
            }
            q = NotQuery();
            queries.add(q);
        }
        {
            if (true)
                return (queries == null) ? q : getAndQuery(queries, true /* infix */, oprt);
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery NotQuery() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries = null;
        Token oprt = null;
        q = NQuery();
        label_4: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case NOT:
                ;
                break;
            default:
                jj_la1[2] = jj_gen;
                break label_4;
            }
            oprt = jj_consume_token(NOT);
            /* keep only last used operator */
            if (queries == null) {
                queries = new ArrayList<SrndQuery>();
                queries.add(q);
            }
            q = NQuery();
            queries.add(q);
        }
        {
            if (true)
                return (queries == null) ? q : getNotQuery(queries, oprt);
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery NQuery() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries;
        Token dt;
        q = WQuery();
        label_5: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case N:
                ;
                break;
            default:
                jj_la1[3] = jj_gen;
                break label_5;
            }
            dt = jj_consume_token(N);
            queries = new ArrayList<SrndQuery>();
            queries.add(q); /* left associative */

            q = WQuery();
            queries.add(q);
            q = getDistanceQuery(queries, true /* infix */, dt, false /* not ordered */);
        }
        {
            if (true)
                return q;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery WQuery() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries;
        Token wt;
        q = PrimaryQuery();
        label_6: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case W:
                ;
                break;
            default:
                jj_la1[4] = jj_gen;
                break label_6;
            }
            wt = jj_consume_token(W);
            queries = new ArrayList<SrndQuery>();
            queries.add(q); /* left associative */

            q = PrimaryQuery();
            queries.add(q);
            q = getDistanceQuery(queries, true /* infix */, wt, true /* ordered */);
        }
        {
            if (true)
                return q;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery PrimaryQuery() throws ParseException {
        /* bracketed weighted query or weighted term */
        SrndQuery q;
        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
        case LPAREN:
            jj_consume_token(LPAREN);
            q = FieldsQuery();
            jj_consume_token(RPAREN);
            break;
        case OR:
        case AND:
        case W:
        case N:
            q = PrefixOperatorQuery();
            break;
        case TRUNCQUOTED:
        case QUOTED:
        case SUFFIXTERM:
        case TRUNCTERM:
        case TERM:
            q = SimpleTerm();
            break;
        default:
            jj_la1[5] = jj_gen;
            jj_consume_token(-1);
            throw new ParseException();
        }
        OptionalWeights(q);
        {
            if (true)
                return q;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery PrefixOperatorQuery() throws ParseException {
        Token oprt;
        List<SrndQuery> queries;
        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
        case OR:
            oprt = jj_consume_token(OR);
            /* prefix OR */
            queries = FieldsQueryList(); {
            if (true)
                return getOrQuery(queries, false /* not infix */, oprt);
        }
            break;
        case AND:
            oprt = jj_consume_token(AND);
            /* prefix AND */
            queries = FieldsQueryList(); {
            if (true)
                return getAndQuery(queries, false /* not infix */, oprt);
        }
            break;
        case N:
            oprt = jj_consume_token(N);
            /* prefix N */
            queries = FieldsQueryList(); {
            if (true)
                return getDistanceQuery(queries, false /* not infix */, oprt, false /* not ordered */);
        }
            break;
        case W:
            oprt = jj_consume_token(W);
            /* prefix W */
            queries = FieldsQueryList(); {
            if (true)
                return getDistanceQuery(queries, false /* not infix */, oprt, true /* ordered */);
        }
            break;
        default:
            jj_la1[6] = jj_gen;
            jj_consume_token(-1);
            throw new ParseException();
        }
        throw new Error("Missing return statement in function");
    }

    final public List<SrndQuery> FieldsQueryList() throws ParseException {
        SrndQuery q;
        ArrayList<SrndQuery> queries = new ArrayList<SrndQuery>();
        jj_consume_token(LPAREN);
        q = FieldsQuery();
        queries.add(q);
        label_7: while (true) {
            jj_consume_token(COMMA);
            q = FieldsQuery();
            queries.add(q);
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case COMMA:
                ;
                break;
            default:
                jj_la1[7] = jj_gen;
                break label_7;
            }
        }
        jj_consume_token(RPAREN);
        {
            if (true)
                return queries;
        }
        throw new Error("Missing return statement in function");
    }

    final public SrndQuery SimpleTerm() throws ParseException {
        Token term;
        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
        case TERM:
            term = jj_consume_token(TERM); {
            if (true)
                return getTermQuery(term.image, false /* not quoted */);
        }
            break;
        case QUOTED:
            term = jj_consume_token(QUOTED); {
            if (true)
                return getTermQuery(term.image.substring(1, term.image.length() - 1), true /* quoted */);
        }
            break;
        case SUFFIXTERM:
            term = jj_consume_token(SUFFIXTERM);
            /* ending in * */
            if (!allowedSuffix(term.image)) {
                {
                    if (true)
                        throw new ParseException(TRUNCATION_ERROR_MESSAGE + term.image);
                }
            } {
            if (true)
                return getPrefixQuery(term.image.substring(0, term.image.length() - 1), false /* not quoted */);
        }
            break;
        case TRUNCTERM:
            term = jj_consume_token(TRUNCTERM);
            /* with at least one * or ? */
            if (!allowedTruncation(term.image)) {
                {
                    if (true)
                        throw new ParseException(TRUNCATION_ERROR_MESSAGE + term.image);
                }
            } {
            if (true)
                return getTruncQuery(term.image);
        }
            break;
        case TRUNCQUOTED:
            term = jj_consume_token(TRUNCQUOTED);
            /* eg. "9b-b,m"* */
            if ((term.image.length() - 3) < MINIMUM_PREFIX_LENGTH) {
                {
                    if (true)
                        throw new ParseException(TRUNCATION_ERROR_MESSAGE + term.image);
                }
            } {
            if (true)
                return getPrefixQuery(term.image.substring(1, term.image.length() - 2), true /* quoted */);
        }
            break;
        default:
            jj_la1[8] = jj_gen;
            jj_consume_token(-1);
            throw new ParseException();
        }
        throw new Error("Missing return statement in function");
    }

    final public void OptionalWeights(SrndQuery q) throws ParseException {
        Token weight = null;
        label_8: while (true) {
            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
            case CARAT:
                ;
                break;
            default:
                jj_la1[9] = jj_gen;
                break label_8;
            }
            jj_consume_token(CARAT);
            weight = jj_consume_token(NUMBER);
            float f;
            try {
                f = Float.parseFloat(weight.image);
            } catch (Exception floatExc) {
                {
                    if (true)
                        throw new ParseException(BOOST_ERROR_MESSAGE + weight.image + " (" + floatExc + ")");
                }
            }
            if (f <= 0.0) {
                {
                    if (true)
                        throw new ParseException(BOOST_ERROR_MESSAGE + weight.image);
                }
            }
            q.setWeight(f * q.getWeight()); /* left associative, fwiw */

        }
    }

    private boolean jj_2_1(int xla) {
        jj_la = xla;
        jj_lastpos = jj_scanpos = token;
        try {
            return !jj_3_1();
        } catch (LookaheadSuccess ls) {
            return true;
        } finally {
            jj_save(0, xla);
        }
    }

    private boolean jj_3_1() {
        if (jj_scan_token(TERM))
            return true;
        if (jj_scan_token(COLON))
            return true;
        return false;
    }

    /** Generated Token Manager. */
    public QueryParserTokenManager token_source;
    /** Current token. */
    public Token token;
    /** Next token. */
    public Token jj_nt;
    private int jj_ntk;
    private Token jj_scanpos, jj_lastpos;
    private int jj_la;
    private int jj_gen;
    final private int[] jj_la1 = new int[10];
    static private int[] jj_la1_0;
    static {
        jj_la1_init_0();
    }

    private static void jj_la1_init_0() {
        jj_la1_0 = new int[] { 0x100, 0x200, 0x400, 0x1000, 0x800, 0x7c3b00, 0x1b00, 0x8000, 0x7c0000, 0x20000, };
    }

    final private JJCalls[] jj_2_rtns = new JJCalls[1];
    private boolean jj_rescan = false;
    private int jj_gc = 0;

    /** Constructor with user supplied CharStream. */
    public QueryParser(CharStream stream) {
        token_source = new QueryParserTokenManager(stream);
        token = new Token();
        jj_ntk = -1;
        jj_gen = 0;
        for (int i = 0; i < 10; i++)
            jj_la1[i] = -1;
        for (int i = 0; i < jj_2_rtns.length; i++)
            jj_2_rtns[i] = new JJCalls();
    }

    /** Reinitialise. */
    public void ReInit(CharStream stream) {
        token_source.ReInit(stream);
        token = new Token();
        jj_ntk = -1;
        jj_gen = 0;
        for (int i = 0; i < 10; i++)
            jj_la1[i] = -1;
        for (int i = 0; i < jj_2_rtns.length; i++)
            jj_2_rtns[i] = new JJCalls();
    }

    /** Constructor with generated Token Manager. */
    public QueryParser(QueryParserTokenManager tm) {
        token_source = tm;
        token = new Token();
        jj_ntk = -1;
        jj_gen = 0;
        for (int i = 0; i < 10; i++)
            jj_la1[i] = -1;
        for (int i = 0; i < jj_2_rtns.length; i++)
            jj_2_rtns[i] = new JJCalls();
    }

    /** Reinitialise. */
    public void ReInit(QueryParserTokenManager tm) {
        token_source = tm;
        token = new Token();
        jj_ntk = -1;
        jj_gen = 0;
        for (int i = 0; i < 10; i++)
            jj_la1[i] = -1;
        for (int i = 0; i < jj_2_rtns.length; i++)
            jj_2_rtns[i] = new JJCalls();
    }

    private Token jj_consume_token(int kind) throws ParseException {
        Token oldToken;
        if ((oldToken = token).next != null)
            token = token.next;
        else
            token = token.next = token_source.getNextToken();
        jj_ntk = -1;
        if (token.kind == kind) {
            jj_gen++;
            if (++jj_gc > 100) {
                jj_gc = 0;
                for (int i = 0; i < jj_2_rtns.length; i++) {
                    JJCalls c = jj_2_rtns[i];
                    while (c != null) {
                        if (c.gen < jj_gen)
                            c.first = null;
                        c = c.next;
                    }
                }
            }
            return token;
        }
        token = oldToken;
        jj_kind = kind;
        throw generateParseException();
    }

    static private final class LookaheadSuccess extends java.lang.Error {
    }

    final private LookaheadSuccess jj_ls = new LookaheadSuccess();

    private boolean jj_scan_token(int kind) {
        if (jj_scanpos == jj_lastpos) {
            jj_la--;
            if (jj_scanpos.next == null) {
                jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
            } else {
                jj_lastpos = jj_scanpos = jj_scanpos.next;
            }
        } else {
            jj_scanpos = jj_scanpos.next;
        }
        if (jj_rescan) {
            int i = 0;
            Token tok = token;
            while (tok != null && tok != jj_scanpos) {
                i++;
                tok = tok.next;
            }
            if (tok != null)
                jj_add_error_token(kind, i);
        }
        if (jj_scanpos.kind != kind)
            return true;
        if (jj_la == 0 && jj_scanpos == jj_lastpos)
            throw jj_ls;
        return false;
    }

    /** Get the next Token. */
    final public Token getNextToken() {
        if (token.next != null)
            token = token.next;
        else
            token = token.next = token_source.getNextToken();
        jj_ntk = -1;
        jj_gen++;
        return token;
    }

    /** Get the specific Token. */
    final public Token getToken(int index) {
        Token t = token;
        for (int i = 0; i < index; i++) {
            if (t.next != null)
                t = t.next;
            else
                t = t.next = token_source.getNextToken();
        }
        return t;
    }

    private int jj_ntk() {
        if ((jj_nt = token.next) == null)
            return (jj_ntk = (token.next = token_source.getNextToken()).kind);
        else
            return (jj_ntk = jj_nt.kind);
    }

    private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
    private int[] jj_expentry;
    private int jj_kind = -1;
    private int[] jj_lasttokens = new int[100];
    private int jj_endpos;

    private void jj_add_error_token(int kind, int pos) {
        if (pos >= 100)
            return;
        if (pos == jj_endpos + 1) {
            jj_lasttokens[jj_endpos++] = kind;
        } else if (jj_endpos != 0) {
            jj_expentry = new int[jj_endpos];
            for (int i = 0; i < jj_endpos; i++) {
                jj_expentry[i] = jj_lasttokens[i];
            }
            jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) {
                int[] oldentry = (int[]) (it.next());
                if (oldentry.length == jj_expentry.length) {
                    for (int i = 0; i < jj_expentry.length; i++) {
                        if (oldentry[i] != jj_expentry[i]) {
                            continue jj_entries_loop;
                        }
                    }
                    jj_expentries.add(jj_expentry);
                    break jj_entries_loop;
                }
            }
            if (pos != 0)
                jj_lasttokens[(jj_endpos = pos) - 1] = kind;
        }
    }

    /** Generate ParseException. */
    public ParseException generateParseException() {
        jj_expentries.clear();
        boolean[] la1tokens = new boolean[24];
        if (jj_kind >= 0) {
            la1tokens[jj_kind] = true;
            jj_kind = -1;
        }
        for (int i = 0; i < 10; i++) {
            if (jj_la1[i] == jj_gen) {
                for (int j = 0; j < 32; j++) {
                    if ((jj_la1_0[i] & (1 << j)) != 0) {
                        la1tokens[j] = true;
                    }
                }
            }
        }
        for (int i = 0; i < 24; i++) {
            if (la1tokens[i]) {
                jj_expentry = new int[1];
                jj_expentry[0] = i;
                jj_expentries.add(jj_expentry);
            }
        }
        jj_endpos = 0;
        jj_rescan_token();
        jj_add_error_token(0, 0);
        int[][] exptokseq = new int[jj_expentries.size()][];
        for (int i = 0; i < jj_expentries.size(); i++) {
            exptokseq[i] = jj_expentries.get(i);
        }
        return new ParseException(token, exptokseq, tokenImage);
    }

    /** Enable tracing. */
    final public void enable_tracing() {
    }

    /** Disable tracing. */
    final public void disable_tracing() {
    }

    private void jj_rescan_token() {
        jj_rescan = true;
        for (int i = 0; i < 1; i++) {
            try {
                JJCalls p = jj_2_rtns[i];
                do {
                    if (p.gen > jj_gen) {
                        jj_la = p.arg;
                        jj_lastpos = jj_scanpos = p.first;
                        switch (i) {
                        case 0:
                            jj_3_1();
                            break;
                        }
                    }
                    p = p.next;
                } while (p != null);
            } catch (LookaheadSuccess ls) {
            }
        }
        jj_rescan = false;
    }

    private void jj_save(int index, int xla) {
        JJCalls p = jj_2_rtns[index];
        while (p.gen > jj_gen) {
            if (p.next == null) {
                p = p.next = new JJCalls();
                break;
            }
            p = p.next;
        }
        p.gen = jj_gen + xla - jj_la;
        p.first = token;
        p.arg = xla;
    }

    static final class JJCalls {
        int gen;
        Token first;
        int arg;
        JJCalls next;
    }

}