0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.ejb.ql;
0031:
0032: import com.caucho.ejb.cfg21.EjbEntityBean;
0033: import com.caucho.config.ConfigException;
0034: import com.caucho.config.LineConfigException;
0035: import com.caucho.ejb.cfg.*;
0036: import com.caucho.util.CharBuffer;
0037: import com.caucho.util.IntMap;
0038: import com.caucho.util.L10N;
0039: import com.caucho.util.Log;
0040:
0041: import javax.ejb.EJBLocalObject;
0042: import javax.ejb.EntityBean;
0043: import javax.ejb.FinderException;
0044: import java.util.ArrayList;
0045: import java.util.HashMap;
0046: import java.util.logging.Logger;
0047:
0048: /**
0049: * Contains the parser for EJB-QL queries and stores the parsed expressions.
0050: *
0051: * <p>The expression tree is rooted at Expr.
0052: */
0053: public class QLParser extends Query {
0054: static final Logger log = Log.open(QLParser.class);
0055: static final L10N L = new L10N(QLParser.class);
0056:
0057: final static int IDENTIFIER = 128;
0058: final static int INTEGER = IDENTIFIER + 1;
0059: final static int LONG = INTEGER + 1;
0060: final static int DOUBLE = LONG + 1;
0061: final static int STRING = DOUBLE + 1;
0062: final static int TRUE = STRING + 1;
0063: final static int FALSE = TRUE + 1;
0064: final static int UNKNOWN = FALSE + 1;
0065: final static int MEMBER = UNKNOWN + 1;
0066: final static int OF = MEMBER + 1;
0067: final static int EMPTY = OF + 1;
0068: final static int NULL = EMPTY + 1;
0069:
0070: final static int FROM = NULL + 1;
0071: final static int IN = FROM + 1;
0072: final static int SELECT = IN + 1;
0073: final static int DISTINCT = SELECT + 1;
0074: final static int WHERE = SELECT + 1;
0075: final static int AS = WHERE + 1;
0076: final static int ORDER = AS + 1;
0077: final static int BY = ORDER + 1;
0078: final static int ASC = BY + 1;
0079: final static int DESC = ASC + 1;
0080: final static int LIMIT = DESC + 1;
0081: final static int OFFSET = LIMIT + 1;
0082:
0083: final static int BETWEEN = OFFSET + 1;
0084: final static int LIKE = BETWEEN + 1;
0085: final static int ESCAPE = LIKE + 1;
0086: final static int IS = ESCAPE + 1;
0087:
0088: final static int EQ = IS + 1;
0089: final static int NE = EQ + 1;
0090: final static int LT = NE + 1;
0091: final static int LE = LT + 1;
0092: final static int GT = LE + 1;
0093: final static int GE = GT + 1;
0094:
0095: final static int AND = GE + 1;
0096: final static int OR = AND + 1;
0097: final static int NOT = OR + 1;
0098:
0099: final static int EXTERNAL_DOT = NOT + 1;
0100:
0101: final static int ARG = EXTERNAL_DOT + 1;
0102: final static int THIS = ARG + 1;
0103:
0104: private static IntMap _reserved;
0105:
0106: private String _location;
0107:
0108: // The owning bean
0109: private EjbEntityBean _bean;
0110:
0111: // The target bean
0112: private EjbEntityBean _target;
0113:
0114: // the functions
0115: private ArrayList<FunctionSignature> _functions;
0116:
0117: // Method name to generate
0118: private String _methodName;
0119: // Return class
0120: private Class _returnType;
0121: // Return ejb
0122: private String _returnEJB;
0123: // EJB-QL
0124: private String _query;
0125:
0126: // list of the identifier
0127: private ArrayList<FromItem> _fromList;
0128: // list of the identifiers
0129: private ArrayList<PathExpr> _fromIds = new ArrayList<PathExpr>();
0130:
0131: // list of the relation links
0132: //private ArrayList<LinkItem> _linkList;
0133: // select expression
0134: private Expr _selectExpr;
0135: // is distinct (set)
0136: private boolean _isDistinct;
0137:
0138: // from table
0139: private String _fromTable;
0140: // from identifier
0141: private String _fromId;
0142: // this expression
0143: private IdExpr _this Expr;
0144: // where expression
0145: private Expr _whereExpr;
0146: // arguments
0147: private ArrayList<Expr> _argList;
0148: // order by expression
0149: private ArrayList<Expr> _orderExprList;
0150: // order by ascending/descending
0151: private ArrayList<Boolean> _orderAscendingList;
0152: // order by limit max
0153: private Expr _limitMax;
0154: // order by limit offset
0155: private Expr _limitOffset;
0156:
0157: private AndExpr _andExpr;
0158: private boolean _isWhere;
0159:
0160: private HashMap<String, PathExpr> _idMap;
0161: private HashMap<Expr, Expr> _pathMap;
0162:
0163: // parse index
0164: private int _parseIndex;
0165: // current token
0166: private int _token;
0167: // temp for parsing
0168: private String lexeme;
0169:
0170: private int _unique;
0171:
0172: private String _booleanTrue = "1";
0173: private String _booleanFalse = "0";
0174:
0175: private boolean _addArgToQuery = true;
0176:
0177: private boolean _queryLoadsBean = true;
0178:
0179: private int _maxArg;
0180:
0181: private QLParser(EjbEntityBean bean) {
0182: _bean = bean;
0183:
0184: // _functions = FunExpr.getStandardFunctions();
0185: _functions = bean.getConfig().getFunctions();
0186: }
0187:
0188: /**
0189: * Creates a new select method.
0190: *
0191: * @param bean the owning persistent bean
0192: * @param methodName the method name to implement
0193: * @param method the method signature
0194: */
0195: public QLParser(EjbEntityBean bean, String methodName,
0196: ApiMethod method, Class returnType) throws ConfigException {
0197: this (bean);
0198:
0199: setMethod(method);
0200:
0201: _methodName = methodName;
0202: _returnType = returnType;
0203:
0204: Class[] exn = method.getExceptionTypes();
0205: for (int i = 0; i < exn.length; i++)
0206: if (FinderException.class.isAssignableFrom(exn[i]))
0207: return;
0208:
0209: throw new ConfigException(L.l(
0210: "{0}: '{1}' must throw javax.ejb.FinderException.",
0211: method.getDeclaringClass().getName(),
0212: getFullMethodName(method)));
0213: }
0214:
0215: public static void parseOrderBy(EjbEntityBean bean, String orderBy,
0216: ArrayList<String> orderList,
0217: ArrayList<Boolean> orderAscendingList)
0218: throws ConfigException {
0219: QLParser query = new QLParser(bean);
0220:
0221: query.parseOrderBy(orderBy, orderList, orderAscendingList);
0222: }
0223:
0224: /**
0225: * Sets the location.
0226: */
0227: public void setLocation(String location) {
0228: _location = location;
0229: }
0230:
0231: /**
0232: * Returns the owning bean
0233: */
0234: public EjbEntityBean getPersistentBean() {
0235: return _bean;
0236: }
0237:
0238: /**
0239: * Gets a persistent bean by its type.
0240: */
0241: public EjbEntityBean getBeanByName(String ejbName) {
0242: // return _bean.getManager().getBeanInfoByName(ejbName);
0243: throw new UnsupportedOperationException();
0244: }
0245:
0246: /**
0247: * Gets a persistent bean by its type.
0248: */
0249: public EjbEntityBean getBeanByType(Class type) {
0250: //return _bean.getManager().getBeanInfoByRemote(type);
0251: throw new UnsupportedOperationException();
0252: }
0253:
0254: /**
0255: * Returns the name of the select method.
0256: */
0257: public String getName() {
0258: return _methodName;
0259: }
0260:
0261: /**
0262: * Returns the function map.
0263: */
0264: public ArrayList<FunctionSignature> getFunctions() {
0265: return _functions;
0266: }
0267:
0268: /**
0269: * Sets the function map.
0270: */
0271: public void setFunctions(ArrayList<FunctionSignature> functions) {
0272: _functions = functions;
0273: }
0274:
0275: /**
0276: * Sets the boolean true.
0277: */
0278: public void setBooleanTrue(String literal) {
0279: _booleanTrue = literal;
0280: }
0281:
0282: /**
0283: * Sets the boolean false.
0284: */
0285: public void setBooleanFalse(String literal) {
0286: _booleanFalse = literal;
0287: }
0288:
0289: /**
0290: * Returns the return type of the select method.
0291: */
0292: public Class getReturnType() {
0293: return _returnType;
0294: }
0295:
0296: /**
0297: * Returns the return type of the select method.
0298: */
0299: public String getReturnEJB() {
0300: return _selectExpr.getReturnEJB();
0301: }
0302:
0303: /**
0304: * Sets the return type of the select method
0305: */
0306: void setReturnType(Class returnType) {
0307: _returnType = returnType;
0308: }
0309:
0310: /**
0311: * Returns the EJB-QL string
0312: */
0313: public String getQuery() {
0314: return _query;
0315: }
0316:
0317: /**
0318: * Gets the select expression
0319: */
0320: public Expr getSelectExpr() {
0321: return _selectExpr;
0322: }
0323:
0324: public boolean isDistinct() {
0325: return _isDistinct;
0326: }
0327:
0328: public boolean getQueryLoadsBean() {
0329: return _queryLoadsBean;
0330: }
0331:
0332: public void setQueryLoadsBean(boolean loadBean) {
0333: _queryLoadsBean = loadBean;
0334: }
0335:
0336: /**
0337: * Gets the this expression.
0338: */
0339: IdExpr getThisExpr() {
0340: return _this Expr;
0341: }
0342:
0343: /**
0344: * Gets the where expression.
0345: */
0346: Expr getWhereExpr() {
0347: return _whereExpr;
0348: }
0349:
0350: /**
0351: * Gets the order by expression.
0352: */
0353: ArrayList<Expr> getOrderExprList() {
0354: return _orderExprList;
0355: }
0356:
0357: /**
0358: * Returns true if the order is ascending.
0359: */
0360: ArrayList<Boolean> getAscendingList() {
0361: return _orderAscendingList;
0362: }
0363:
0364: /**
0365: * Returns any limit max expression
0366: */
0367: Expr getLimitMax() {
0368: return _limitMax;
0369: }
0370:
0371: /**
0372: * Returns any limit offset expression
0373: */
0374: Expr getLimitOffset() {
0375: return _limitOffset;
0376: }
0377:
0378: /**
0379: * Gets the from table
0380: */
0381: ArrayList<FromItem> getFromList() {
0382: return _fromList;
0383: }
0384:
0385: void addFromItem(String id, String table) {
0386: _fromList.add(new FromItem(id, table));
0387: }
0388:
0389: public int getUnique() {
0390: return _unique++;
0391: }
0392:
0393: /**
0394: * Adds a relation expression
0395: */
0396: /*
0397: public void addLink(String tableA, String columnA,
0398: String tableB, String columnB,
0399: boolean isCommon)
0400: {
0401: if (isCommon)
0402: addCommonLink(tableA, columnA, tableB, columnB);
0403: else
0404: addLink(tableA, columnA, tableB, columnB);
0405: }
0406: */
0407:
0408: /**
0409: * Adds a relation expression
0410: */
0411: /*
0412: public void addLink(String tableA, String columnA,
0413: String tableB, String columnB)
0414: {
0415: if (_andExpr != null) {
0416: LinkExpr expr = new LinkExpr(tableA, columnA, tableB, columnB);
0417:
0418: _andExpr.add(expr);
0419:
0420: return;
0421: }
0422:
0423: addCommonLink(tableA, columnA, tableB, columnB);
0424: }
0425: */
0426:
0427: /**
0428: * Adds a relation expression
0429: */
0430: /*
0431: public void addCommonLink(String tableA, String columnA,
0432: String tableB, String columnB)
0433: {
0434: if (_linkList == null)
0435: _linkList = new ArrayList<LinkItem>();
0436:
0437: LinkItem item = new LinkItem(tableA, columnA, tableB, columnB);
0438:
0439: if (! _linkList.contains(item))
0440: _linkList.add(item);
0441: }
0442: */
0443:
0444: /**
0445: * Returns the auxiliary relation expressions
0446: */
0447: /*
0448: ArrayList<LinkItem> getRelations()
0449: {
0450: return _linkList;
0451: }
0452: */
0453:
0454: /**
0455: * Adds a select method argument
0456: */
0457: public void addArg(Expr expr) {
0458: _argList.add(expr);
0459: }
0460:
0461: /**
0462: * Gets the select method arguments in SQL order.
0463: */
0464: ArrayList<Expr> getArgList() {
0465: return _argList;
0466: }
0467:
0468: /**
0469: * Parses the select method's query.
0470: *
0471: * @param query the source query string.
0472: */
0473: public void parseOrderBy(String orderBy,
0474: ArrayList<String> orderList,
0475: ArrayList<Boolean> orderAscendingList)
0476: throws ConfigException {
0477: _query = orderBy;
0478:
0479: _parseIndex = 0;
0480: _unique = 0;
0481: _token = -1;
0482:
0483: int token = -1;
0484:
0485: do {
0486: token = scanToken();
0487:
0488: if (token == IDENTIFIER)
0489: orderList.add(lexeme.toString());
0490: else
0491: throw error(L.l("unexpected token '{0}' in order-by",
0492: tokenName(token)));
0493:
0494: token = scanToken();
0495: if (token == DESC) {
0496: token = scanToken();
0497: orderAscendingList.add(Boolean.FALSE);
0498: } else if (token == ASC) {
0499: token = scanToken();
0500: orderAscendingList.add(Boolean.TRUE);
0501: } else
0502: orderAscendingList.add(Boolean.TRUE);
0503: } while (token == ',');
0504:
0505: if (token >= 0)
0506: throw error(L.l("extra token {0} at end of order-by",
0507: tokenName(peekToken())));
0508: }
0509:
0510: /**
0511: * Parses the select method's query.
0512: *
0513: * @param query the source query string.
0514: */
0515: public EjbQuery parseQuery(String query) throws ConfigException {
0516: int token;
0517:
0518: _query = query;
0519: _fromList = new ArrayList<FromItem>();
0520: _pathMap = new HashMap<Expr, Expr>();
0521: _idMap = new HashMap<String, PathExpr>();
0522: _argList = new ArrayList<Expr>();
0523: //_linkList = new ArrayList<LinkItem>();
0524:
0525: _parseIndex = 0;
0526: _unique = 0;
0527: _token = -1;
0528:
0529: setConfig(_bean.getConfig());
0530:
0531: // First pass parses the from
0532: for (; (token = peekToken()) >= 0 && token != FROM; token = scanToken()) {
0533: }
0534:
0535: if (token != FROM)
0536: throw error(L.l("expected FROM at {0}", tokenName(token)));
0537:
0538: scanToken();
0539:
0540: parseFrom();
0541:
0542: token = peekToken();
0543: if (token >= 0 && token != WHERE && token != ORDER)
0544: throw error(L.l("expected WHERE or ORDER at {0}",
0545: tokenName(token)));
0546:
0547: _parseIndex = 0;
0548: _token = -1;
0549: token = scanToken();
0550:
0551: if (token != SELECT)
0552: throw error(L.l("expected SELECT at {0}", tokenName(token)));
0553:
0554: if (peekToken() == DISTINCT) {
0555: scanToken();
0556: _isDistinct = true;
0557: }
0558:
0559: _selectExpr = parseExpr();
0560:
0561: /*
0562: if (_selectExpr instanceof CollectionExpr) {
0563: CollectionExpr expr = (CollectionExpr) _selectExpr;
0564:
0565: _selectExpr = new CollectionIdExpr(this, "foo", expr);
0566: }
0567: */
0568:
0569: if (_selectExpr instanceof PathExpr)
0570: ((PathExpr) _selectExpr).setUsesField();
0571:
0572: /*
0573: if (! (selectExpr instanceof PathExpr) &&
0574: ! (selectExpr instanceof FieldExpr))
0575: throw error(L.l("'{0}' is an illegal SELECT expression. Only path expressions are allowed in SELECT.", selectExpr));
0576: */
0577:
0578: token = peekToken();
0579: if (token != FROM)
0580: throw error(L.l("expected FROM at {0}", tokenName(token)));
0581:
0582: // skip over the from since it's been parsed
0583: for (; (token = peekToken()) >= 0 && token != WHERE
0584: && token != ORDER; token = scanToken()) {
0585: }
0586:
0587: _addArgToQuery = true;
0588:
0589: if ((token = scanToken()) == WHERE) {
0590: _isWhere = true;
0591: _whereExpr = parseExpr();
0592: _isWhere = false;
0593: token = scanToken();
0594: }
0595:
0596: if (_whereExpr != null && !_whereExpr.isBoolean())
0597: throw error(L.l(
0598: "WHERE must be a boolean expression at {0}",
0599: _whereExpr));
0600:
0601: _addArgToQuery = false;
0602:
0603: int oldMaxArg = _maxArg;
0604: if (token == ORDER) {
0605: if (peekToken() == BY)
0606: scanToken();
0607: if (_orderExprList == null)
0608: _orderExprList = new ArrayList<Expr>();
0609:
0610: if (_orderAscendingList == null)
0611: _orderAscendingList = new ArrayList<Boolean>();
0612:
0613: do {
0614: _orderExprList.add(parseExpr());
0615:
0616: token = peekToken();
0617: if (token == DESC) {
0618: token = scanToken();
0619: _orderAscendingList.add(Boolean.FALSE);
0620: } else if (token == ASC) {
0621: token = scanToken();
0622: _orderAscendingList.add(Boolean.TRUE);
0623: } else
0624: _orderAscendingList.add(Boolean.TRUE);
0625: } while ((token = scanToken()) == ',');
0626: }
0627:
0628: if (token == OFFSET) {
0629: _limitOffset = parseExpr();
0630: token = scanToken();
0631:
0632: if (!_limitOffset.getJavaType().getName().equals("int"))
0633: throw error(L.l(
0634: "OFFSET '{0}' must be an integer expression",
0635: _limitMax));
0636: }
0637:
0638: if (token == LIMIT) {
0639: _limitMax = parseExpr();
0640: token = scanToken();
0641:
0642: if (!_limitMax.getJavaType().getName().equals("int"))
0643: throw error(L.l(
0644: "LIMIT '{0}' must be an integer expression",
0645: _limitMax));
0646: }
0647:
0648: if (token >= 0)
0649: throw error(L.l("extra token {0} at end of query",
0650: tokenName(peekToken())));
0651: _maxArg = oldMaxArg;
0652:
0653: EjbSelectQuery ejbQuery = new EjbSelectQuery(_query);
0654:
0655: ejbQuery.setDistinct(_isDistinct);
0656: ejbQuery.setFromList(_fromIds);
0657: ejbQuery.setSelectExpr(_selectExpr);
0658: ejbQuery.setWhereExpr(_whereExpr);
0659:
0660: ejbQuery.setMaxArg(_maxArg);
0661: ejbQuery.setThisExpr(_this Expr);
0662:
0663: ejbQuery.setOrderBy(_orderExprList, _orderAscendingList);
0664:
0665: ejbQuery.setOffset(_limitOffset);
0666: ejbQuery.setLimit(_limitMax);
0667:
0668: return ejbQuery;
0669: }
0670:
0671: /**
0672: * Parses the FROM block. parseFrom's effect is to populate the
0673: * core identifiers.
0674: *
0675: * <pre>
0676: * from-list ::= from-list ',' from-item
0677: * ::= from-item
0678: *
0679: * from-item ::= IDENTIFIER AS? IDENTIFIER
0680: * ::= IN(collection-path) AS? IDENTIFIER
0681: * </pre>
0682: */
0683: private void parseFrom() throws ConfigException {
0684: boolean moreTables = true;
0685:
0686: while (moreTables) {
0687: int token = scanToken();
0688:
0689: if (token == IN) {
0690: if (scanToken() != '(')
0691: throw error(L
0692: .l(
0693: "expected '(' at {0} while parsing IN(<collection-path>).",
0694: tokenName(token)));
0695:
0696: parseFromCollection();
0697: } else if (token == IDENTIFIER) {
0698: String id = lexeme;
0699: if (peekToken() == AS)
0700: scanToken();
0701:
0702: parseFromTable(id);
0703: } else
0704: throw error(L
0705: .l(
0706: "expected identifier at {0} while parsing FROM",
0707: tokenName(token)));
0708:
0709: if (peekToken() == ',') {
0710: scanToken();
0711: moreTables = true;
0712: } else
0713: moreTables = false;
0714: }
0715: }
0716:
0717: /**
0718: * Parses a single-valued table identifier.
0719: */
0720: private void parseFromTable(String table) throws ConfigException {
0721: int token = scanToken();
0722:
0723: if (token != IDENTIFIER) {
0724: throw error(L
0725: .l(
0726: "expected identifier at {0} while parsing FROM {1} [AS] var",
0727: tokenName(token), table));
0728: }
0729:
0730: String name = lexeme;
0731:
0732: EjbConfig ejbConfig = _bean.getConfig();
0733:
0734: EjbEntityBean entity = ejbConfig.findEntityBySchema(table);
0735:
0736: if (entity == null)
0737: throw error(L
0738: .l(
0739: "'{0}' is an unknown entity-bean schema in 'FROM {0} AS {1}'",
0740: table, name));
0741:
0742: // _bean.addBeanDepend(info.getEJBName());
0743:
0744: IdExpr id = new IdExpr(this , name, entity);
0745:
0746: addIdentifier(name, id);
0747: addFromItem(name, entity.getSQLTable());
0748:
0749: _fromIds.add(id);
0750: }
0751:
0752: /**
0753: * Parses a collection-valued table identifier.
0754: */
0755: private void parseFromCollection() throws ConfigException {
0756: Expr expr = parseDotExpr();
0757:
0758: if (scanToken() != ')')
0759: throw error(L
0760: .l(
0761: "expected ')' at {0} while parsing IN(<collection-path>).",
0762: tokenName(_token)));
0763:
0764: if (!(expr instanceof CollectionExpr))
0765: throw error(L.l(
0766: "expected <collection-path> expression at '{0}'",
0767: expr));
0768:
0769: CollectionExpr collectionExpr = (CollectionExpr) expr;
0770:
0771: if (peekToken() == AS)
0772: scanToken();
0773:
0774: int token = scanToken();
0775: if (token != IDENTIFIER)
0776: throw error(L
0777: .l(
0778: "expected identifier expression at {0} while parsing 'IN({1}) AS id'",
0779: tokenName(token), expr));
0780:
0781: String name = lexeme;
0782:
0783: CollectionIdExpr idExpr;
0784: idExpr = new CollectionIdExpr(this , name, collectionExpr);
0785:
0786: addIdentifier(name, idExpr);
0787:
0788: _fromIds.add(idExpr);
0789: }
0790:
0791: /**
0792: * Parses the next expression.
0793: *
0794: * <pre>
0795: * expr ::= or-expr
0796: * </pre>
0797: *
0798: * @return the parsed expression
0799: */
0800: private Expr parseExpr() throws ConfigException {
0801: return parseOrExpr();
0802: }
0803:
0804: /**
0805: * Parses an or expression.
0806: *
0807: * <pre>
0808: * or-expr ::= or-expr AND and-expr
0809: * ::= and-expr
0810: * </pre>
0811: *
0812: * @return the parsed expression
0813: */
0814: private Expr parseOrExpr() throws ConfigException {
0815: Expr expr = parseAndExpr();
0816:
0817: int token = peekToken();
0818: while ((token = peekToken()) == OR) {
0819: scanToken();
0820:
0821: expr = new BinaryExpr(this , token, expr, parseAndExpr());
0822: }
0823:
0824: return expr;
0825: }
0826:
0827: /**
0828: * Parses an and expression.
0829: *
0830: * <pre>
0831: * and-expr ::= and-expr AND not-expr
0832: * ::= not-expr
0833: * </pre>
0834: *
0835: * @return the parsed expression
0836: */
0837: private Expr parseAndExpr() throws ConfigException {
0838: AndExpr oldAnd = _andExpr;
0839:
0840: AndExpr andExpr = new AndExpr(this );
0841:
0842: if (_isWhere)
0843: _andExpr = andExpr;
0844:
0845: Expr expr = parseNotExpr();
0846:
0847: andExpr.add(expr);
0848:
0849: int token = peekToken();
0850: while ((token = peekToken()) == AND) {
0851: scanToken();
0852:
0853: expr = parseNotExpr();
0854:
0855: andExpr.add(expr);
0856: }
0857:
0858: _andExpr = oldAnd;
0859:
0860: return andExpr.getSingleExpr();
0861: }
0862:
0863: /**
0864: * Parses a not expression.
0865: *
0866: * <pre>
0867: * not-expr ::= NOT? cmp-expr
0868: * </pre>
0869: *
0870: * @return the parsed expression
0871: */
0872: private Expr parseNotExpr() throws ConfigException {
0873: int token = peekToken();
0874: if (token == NOT) {
0875: scanToken();
0876:
0877: Expr expr = parseCmpExpr();
0878:
0879: return new UnaryExpr(NOT, expr);
0880: } else
0881: return parseCmpExpr();
0882: }
0883:
0884: /**
0885: * Parses a comparison expression.
0886: *
0887: * <pre>
0888: * cmp-expr ::= add-expr '=' add-expr is-term?
0889: * ::= add-expr 'NOT'? 'BETWEEN' add-expr 'AND' add-expr is-term?
0890: * ::= add-expr 'NOT'? 'LIKE' string ('ESCAPE' string)? is-term?
0891: * ::= add-expr 'NOT'? 'IN' ('lit-1', ..., 'lit-n')
0892: * ::= add-expr
0893: * </pre>
0894: *
0895: * @return the parsed expression
0896: */
0897: private Expr parseCmpExpr() throws ConfigException {
0898: int token = peekToken();
0899: boolean isNot = false;
0900:
0901: Expr expr = parseArithmeticExpr();
0902:
0903: token = peekToken();
0904:
0905: if (token == NOT) {
0906: scanToken();
0907: isNot = true;
0908: token = peekToken();
0909: }
0910:
0911: if (token >= EQ && token <= GE) {
0912: scanToken();
0913:
0914: return parseIs(new BinaryExpr(this , token, expr,
0915: parseAddExpr()));
0916: } else if (token == BETWEEN) {
0917: scanToken();
0918:
0919: Expr a = parseArithmeticExpr();
0920:
0921: if ((token = scanToken()) != AND)
0922: throw error(L.l("Expected 'AND' at {0}",
0923: tokenName(token)));
0924:
0925: Expr b = parseArithmeticExpr();
0926:
0927: return parseIs(new BetweenExpr(expr, a, b, isNot));
0928: } else if (token == LIKE) {
0929: scanToken();
0930:
0931: Expr pattern = parseArithmeticExpr();
0932:
0933: String escape = null;
0934: if (peekToken() == ESCAPE) {
0935: scanToken();
0936:
0937: if ((token = scanToken()) != STRING)
0938: throw error(L.l("Expected string at {0}",
0939: tokenName(token)));
0940:
0941: escape = lexeme.toString();
0942: }
0943:
0944: return parseIs(new LikeExpr(expr, pattern, escape, isNot));
0945: } else if (token == IN) {
0946: scanToken();
0947: token = scanToken();
0948:
0949: if (token != '(')
0950: throw error(L.l("Expected '(' after IN at {0}",
0951: tokenName(token)));
0952:
0953: ArrayList<Expr> args = new ArrayList<Expr>();
0954: while ((token = peekToken()) > 0 && token != ')') {
0955: Expr arg = parseArithmeticExpr();
0956:
0957: args.add(arg);
0958:
0959: token = peekToken();
0960: if (token == ',') {
0961: scanToken();
0962: token = peekToken();
0963: }
0964: }
0965:
0966: if (peekToken() != ')')
0967: throw error(L.l("Expected ')' after IN at {0}",
0968: tokenName(token)));
0969:
0970: scanToken();
0971:
0972: return new InExpr(this , expr, args, isNot);
0973: } else if (token == IS) {
0974: scanToken();
0975:
0976: if (isNot)
0977: throw error(L.l("'NOT IS' is an invalid expression."));
0978:
0979: token = scanToken();
0980: if (token == NOT) {
0981: isNot = true;
0982: token = scanToken();
0983: }
0984:
0985: if (token == NULL)
0986: return parseIs(new IsExpr(this , expr, NULL, isNot));
0987: else if (token == EMPTY) {
0988: if (!(expr instanceof CollectionExpr))
0989: throw error(L
0990: .l(
0991: "IS EMPTY requires collection path at '{0}'",
0992: expr));
0993: return parseIs(new EmptyExpr(expr, isNot));
0994: } else
0995: throw error(L.l("'{0}' unexpected after IS.",
0996: tokenName(token)));
0997: } else if (token == MEMBER) {
0998: scanToken();
0999:
1000: token = peekToken();
1001: if (token == OF)
1002: token = scanToken();
1003:
1004: Expr collection = parseDotExpr();
1005:
1006: return parseIs(new MemberExpr(isNot, expr, collection));
1007: } else
1008: return expr;
1009: }
1010:
1011: /**
1012: * Parses an 'IS' term
1013: *
1014: * <pre>
1015: * is-term ::= IS NOT? (TRUE|FALSE|UNKNOWN)
1016: * </pre>
1017: */
1018: private Expr parseIs(Expr base) throws ConfigException {
1019: if (peekToken() != IS)
1020: return base;
1021:
1022: scanToken();
1023: boolean isNot = peekToken() == NOT;
1024: if (isNot)
1025: scanToken();
1026:
1027: int token = scanToken();
1028: if (token == UNKNOWN)
1029: return new IsExpr(this , base, NULL, isNot);
1030: else if (token == TRUE)
1031: return isNot ? new UnaryExpr(NOT, base) : base;
1032: else if (token == FALSE)
1033: return isNot ? base : new UnaryExpr(NOT, base);
1034:
1035: throw error(L.l("expected TRUE or FALSE at {0}",
1036: tokenName(token)));
1037: }
1038:
1039: /**
1040: * Parses an arithmetic expression.
1041: *
1042: * <pre>
1043: * arithmetic-expr ::= add-expr
1044: * </pre>
1045: */
1046: private Expr parseArithmeticExpr() throws ConfigException {
1047: return parseAddExpr();
1048: }
1049:
1050: /**
1051: * Parses an addition expression.
1052: *
1053: * <pre>
1054: * add-expr ::= add-expr ('+' | '-') mul-expr
1055: * ::= mul-expr
1056: * </pre>
1057: *
1058: * @return the parsed expression
1059: */
1060: private Expr parseAddExpr() throws ConfigException {
1061: Expr expr = parseMulExpr();
1062:
1063: int token = peekToken();
1064: while ((token = peekToken()) == '-' || token == '+') {
1065: scanToken();
1066:
1067: expr = new BinaryExpr(this , token, expr, parseMulExpr());
1068: }
1069:
1070: return expr;
1071: }
1072:
1073: /**
1074: * Parses a multiplication/division expression.
1075: *
1076: * <pre>
1077: * mul-expr ::= mul-expr ('*' | '/') unary-expr
1078: * ::= unary-expr
1079: * </pre>
1080: *
1081: * @return the parsed expression
1082: */
1083: private Expr parseMulExpr() throws ConfigException {
1084: Expr expr = parseUnaryExpr();
1085:
1086: int token = peekToken();
1087: while ((token = peekToken()) == '*' || token == '/') {
1088: scanToken();
1089:
1090: expr = new BinaryExpr(this , token, expr, parseUnaryExpr());
1091: }
1092:
1093: return expr;
1094: }
1095:
1096: /**
1097: * Parses a unary +/-
1098: *
1099: * <pre>
1100: * unary-expr ::= ('+'|'-')? path-expr
1101: * </pre>
1102: *
1103: * @return the parsed expression
1104: */
1105: private Expr parseUnaryExpr() throws ConfigException {
1106: int token = peekToken();
1107:
1108: if (token == '+' || token == '-') {
1109: scanToken();
1110: return new UnaryExpr(token, parseRefExpr());
1111: } else
1112: return parseRefExpr();
1113: }
1114:
1115: /**
1116: * Parses a path expression.
1117: *
1118: * <pre>
1119: * ref-expr ::= path-expr '=>' IDENTIFIER
1120: * ::= path-expr
1121: * </pre>
1122: *
1123: * @return the parsed expression
1124: */
1125: private Expr parseRefExpr() throws ConfigException {
1126: Expr expr = parseDotExpr();
1127:
1128: int token;
1129: if ((token = peekToken()) == EXTERNAL_DOT) {
1130: scanToken();
1131:
1132: token = scanToken();
1133:
1134: if (token != IDENTIFIER)
1135: throw error(L.l("expected field identifier at {0}",
1136: tokenName(token)));
1137:
1138: expr = expr.newField(lexeme);
1139: expr.evalTypes();
1140:
1141: if (!expr.isExternal())
1142: throw error(L.l(
1143: "'{0}' must refer to an external entity bean",
1144: expr));
1145: }
1146:
1147: return expr;
1148: }
1149:
1150: /**
1151: * Parses a path expression.
1152: *
1153: * <pre>
1154: * path-expr ::= path-expr '.' IDENTIFIER
1155: * ::= term
1156: * </pre>
1157: *
1158: * @return the parsed expression
1159: */
1160: private Expr parseDotExpr() throws ConfigException {
1161: Expr expr = parseTerm();
1162:
1163: int token;
1164: while ((token = peekToken()) == '.') {
1165: scanToken();
1166:
1167: token = scanToken();
1168:
1169: if (token != IDENTIFIER)
1170: throw error(L.l("expected field identifier at {0}",
1171: tokenName(token)));
1172:
1173: expr.evalTypes();
1174: if (expr.isExternal())
1175: throw error(L
1176: .l(
1177: "'{0}' must not refer to an external entity bean",
1178: expr));
1179: Expr field = expr.newField(lexeme);
1180: expr = field;
1181:
1182: Expr equiv = _pathMap.get(field);
1183: if (equiv != null)
1184: expr = equiv;
1185: else
1186: _pathMap.put(field, field);
1187: }
1188:
1189: return expr;
1190: }
1191:
1192: /**
1193: * Parses a term
1194: *
1195: * <pre>
1196: * term ::= IDENTIFIER | INTEGER | LONG | DOUBLE | STRING
1197: * ::= THIS '.' IDENTIFIER
1198: * ::= IDENTIFIER '(' args ')'
1199: * ::= '(' args ')'
1200: * </pre>
1201: */
1202: private Expr parseTerm() throws ConfigException {
1203: int token = scanToken();
1204:
1205: switch (token) {
1206: case IDENTIFIER:
1207: String name = lexeme.toString();
1208: if (peekToken() != '(')
1209: return getIdentifier(name);
1210: else
1211: return parseFunction(name);
1212:
1213: case FALSE:
1214: return new LiteralExpr(_booleanFalse, boolean.class);
1215:
1216: case TRUE:
1217: return new LiteralExpr(_booleanTrue, boolean.class);
1218:
1219: case INTEGER:
1220: return new LiteralExpr(lexeme, int.class);
1221:
1222: case LONG:
1223: return new LiteralExpr(lexeme, long.class);
1224:
1225: case DOUBLE:
1226: return new LiteralExpr(lexeme, double.class);
1227:
1228: case STRING:
1229: return new LiteralExpr(lexeme, String.class);
1230:
1231: case ARG: {
1232: ArgExpr arg = new ArgExpr(this , Integer.parseInt(lexeme));
1233: if (_addArgToQuery)
1234: addArg(arg);
1235: return arg;
1236: }
1237:
1238: case THIS: {
1239: if (_this Expr == null) {
1240: _this Expr = new IdExpr(this , "caucho_this", _bean);
1241: addFromItem("caucho_this", _bean.getSQLTable());
1242: _fromIds.add(_this Expr);
1243: _argList.add(0, new ThisExpr(this , _bean));
1244: }
1245:
1246: return _this Expr;
1247: }
1248:
1249: case '(':
1250: Expr expr = parseExpr();
1251: if ((token = scanToken()) != ')')
1252: throw error(L
1253: .l("expected ')' at {0}", tokenName(token)));
1254:
1255: return expr;
1256:
1257: default:
1258: throw error(L.l("expected term at {0}", tokenName(token)));
1259: }
1260: }
1261:
1262: /**
1263: * Parses a function
1264: *
1265: * <pre>
1266: * function ::= IDENTIFIER '(' expr (',' expr)* ')'
1267: * ::= IDENTIFIER '(' ')'
1268: * </pre>
1269: */
1270: private Expr parseFunction(String name) throws ConfigException {
1271: ArrayList<Expr> args = new ArrayList<Expr>();
1272:
1273: int token;
1274: if ((token = scanToken()) != '(')
1275: throw error(L.l(
1276: "expected '(' at {0} while parsing function {1}()",
1277: tokenName(token), name));
1278:
1279: while ((token = peekToken()) != ')' && token > 0) {
1280: Expr expr = parseExpr();
1281:
1282: args.add(expr);
1283:
1284: if ((token = peekToken()) == ',')
1285: scanToken();
1286: }
1287:
1288: if (token != ')')
1289: throw error(L.l(
1290: "expected ')' at {0} while parsing function {1}",
1291: tokenName(token), name));
1292:
1293: scanToken();
1294:
1295: if (name.equalsIgnoreCase("object")) {
1296: if (args.size() != 1)
1297: throw error(L.l("OBJECT() requires a single argument"));
1298:
1299: Expr expr = args.get(0);
1300:
1301: if (!EntityBean.class.isAssignableFrom(expr.getJavaType())
1302: && !EJBLocalObject.class.isAssignableFrom(expr
1303: .getJavaType()))
1304: throw error(L
1305: .l(
1306: "OBJECT({0}) requires an entity bean as its argument at '{1}'",
1307: expr, expr.getJavaType()));
1308:
1309: return expr;
1310: } else {
1311: try {
1312: return new FunExpr(name, args, _functions);
1313: } catch (ConfigException e) {
1314: throw error(e.getMessage());
1315: }
1316: }
1317: }
1318:
1319: /**
1320: * Adds a new identifier
1321: *
1322: * @param name the name of the identifier
1323: *
1324: * @return the IdExpr corresponding to the identifier
1325: */
1326: void addIdentifier(String name, PathExpr expr)
1327: throws ConfigException {
1328: Expr oldExpr = _idMap.get(name);
1329:
1330: if (oldExpr != null)
1331: throw error(L.l("'{0}' is defined twice", name));
1332:
1333: _idMap.put(name, expr);
1334: }
1335:
1336: /**
1337: * Adds a new identifier
1338: *
1339: * @param name the name of the identifier
1340: *
1341: * @return the IdExpr corresponding to the identifier
1342: */
1343: PathExpr getIdentifier(String name) throws ConfigException {
1344: PathExpr expr = _idMap.get(name);
1345:
1346: if (expr == null)
1347: throw error(L.l("'{0}' is an unknown identifier", name));
1348:
1349: return expr;
1350: }
1351:
1352: /**
1353: * Peeks the next token
1354: *
1355: * @return integer code for the token
1356: */
1357: private int peekToken() throws ConfigException {
1358: if (_token > 0)
1359: return _token;
1360:
1361: _token = scanToken();
1362:
1363: return _token;
1364: }
1365:
1366: /**
1367: * Scan the next token. If the lexeme is a string, its string
1368: * representation is in "lexeme".
1369: *
1370: * @return integer code for the token
1371: */
1372: private int scanToken() throws ConfigException {
1373: if (_token > 0) {
1374: int value = _token;
1375: _token = -1;
1376: return value;
1377: }
1378:
1379: int sign = 1;
1380: int ch;
1381:
1382: for (ch = read(); Character.isWhitespace((char) ch); ch = read()) {
1383: }
1384:
1385: switch (ch) {
1386: case -1:
1387: case '(':
1388: case ')':
1389: case '.':
1390: case '*':
1391: case '/':
1392: case ',':
1393: return ch;
1394:
1395: case '+':
1396: if ((ch = read()) >= '0' && ch <= '9')
1397: break;
1398: else {
1399: unread(ch);
1400: return '+';
1401: }
1402:
1403: case '-':
1404: if ((ch = read()) >= '0' && ch <= '9') {
1405: sign = -1;
1406: break;
1407: } else {
1408: unread(ch);
1409: return '-';
1410: }
1411:
1412: case '=':
1413: if ((ch = read()) == '>')
1414: return EXTERNAL_DOT;
1415: else {
1416: unread(ch);
1417: return EQ;
1418: }
1419:
1420: case '<':
1421: if ((ch = read()) == '=')
1422: return LE;
1423: else if (ch == '>')
1424: return NE;
1425: else {
1426: unread(ch);
1427: return LT;
1428: }
1429:
1430: case '>':
1431: if ((ch = read()) == '=')
1432: return GE;
1433: else {
1434: unread(ch);
1435: return GT;
1436: }
1437:
1438: case '?':
1439: CharBuffer cb = CharBuffer.allocate();
1440: int index = 0;
1441: for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
1442: cb.append((char) ch);
1443: index = 10 * index + ch - '0';
1444: }
1445: unread(ch);
1446:
1447: lexeme = cb.close();
1448:
1449: if (index <= 0)
1450: throw error(L.l(
1451: "'{0}' must refer to a positive argument", "?"
1452: + lexeme));
1453:
1454: if (_maxArg < index)
1455: _maxArg = index;
1456:
1457: return ARG;
1458:
1459: // @@ is useless?
1460: case '@':
1461: if ((ch = read()) != '@')
1462: throw error(L.l("'@' expected at {0}", charName(ch)));
1463: return scanToken();
1464: }
1465:
1466: if (Character.isJavaIdentifierStart((char) ch)) {
1467: CharBuffer cb = CharBuffer.allocate();
1468:
1469: for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
1470: cb.append((char) ch);
1471:
1472: unread(ch);
1473:
1474: lexeme = cb.close();
1475: String lower = lexeme.toLowerCase();
1476:
1477: int token = _reserved.get(lower);
1478:
1479: if (token > 0)
1480: return token;
1481: else
1482: return IDENTIFIER;
1483: } else if (ch >= '0' && ch <= '9') {
1484: CharBuffer cb = CharBuffer.allocate();
1485:
1486: int type = INTEGER;
1487:
1488: if (sign < 0)
1489: cb.append('-');
1490:
1491: for (; ch >= '0' && ch <= '9'; ch = read())
1492: cb.append((char) ch);
1493:
1494: if (ch == '.') {
1495: type = DOUBLE;
1496:
1497: cb.append('.');
1498: for (ch = read(); ch >= '0' && ch <= '9'; ch = read())
1499: cb.append((char) ch);
1500: }
1501:
1502: if (ch == 'e' || ch == 'E') {
1503: type = DOUBLE;
1504:
1505: cb.append('e');
1506: if ((ch = read()) == '+' || ch == '-') {
1507: cb.append((char) ch);
1508: ch = read();
1509: }
1510:
1511: if (!(ch >= '0' && ch <= '9'))
1512: throw error(L.l("exponent needs digits at {0}",
1513: charName(ch)));
1514:
1515: for (; ch >= '0' && ch <= '9'; ch = read())
1516: cb.append((char) ch);
1517: }
1518:
1519: if (ch == 'F' || ch == 'D')
1520: type = DOUBLE;
1521: else if (ch == 'L') {
1522: type = LONG;
1523: } else
1524: unread(ch);
1525:
1526: lexeme = cb.close();
1527:
1528: return type;
1529: } else if (ch == '\'') {
1530: CharBuffer cb = CharBuffer.allocate();
1531:
1532: cb.append("'");
1533: for (ch = read(); ch >= 0; ch = read()) {
1534: if (ch == '\'') {
1535: if ((ch = read()) == '\'')
1536: cb.append("''");
1537: else {
1538: unread(ch);
1539: break;
1540: }
1541: } else
1542: cb.append((char) ch);
1543: }
1544: cb.append("'");
1545:
1546: lexeme = cb.close();
1547:
1548: return STRING;
1549: }
1550:
1551: throw error(L.l("unexpected char at {0}", "" + (char) ch));
1552: }
1553:
1554: /**
1555: * Returns the next character.
1556: */
1557: private int read() {
1558: if (_parseIndex < _query.length())
1559: return _query.charAt(_parseIndex++);
1560: else
1561: return -1;
1562: }
1563:
1564: /**
1565: * Unread the last character.
1566: */
1567: private void unread(int ch) {
1568: if (ch >= 0)
1569: _parseIndex--;
1570: }
1571:
1572: /**
1573: * Returns a full method name with arguments.
1574: */
1575: private String getFullMethodName(ApiMethod method) {
1576: return method.getFullName();
1577: }
1578:
1579: /**
1580: * Returns a full method name with arguments.
1581: */
1582: private String getFullMethodName(String methodName, Class[] params) {
1583: String name = methodName + "(";
1584:
1585: for (int i = 0; i < params.length; i++) {
1586: if (i != 0)
1587: name += ", ";
1588:
1589: name += params[i].getSimpleName();
1590: }
1591:
1592: return name + ")";
1593: }
1594:
1595: /**
1596: * Returns a printable version of a class.
1597: */
1598: private String getClassName(Class cl) {
1599: if (cl.isArray())
1600: return getClassName(cl.getComponentType()) + "[]";
1601: else if (cl.getName().startsWith("java")) {
1602: int p = cl.getName().lastIndexOf('.');
1603:
1604: return cl.getName().substring(p + 1);
1605: } else
1606: return cl.getName();
1607: }
1608:
1609: /**
1610: * Creates an error.
1611: */
1612: public ConfigException error(String msg) {
1613: msg += "\nin \"" + _query + "\"";
1614: /*
1615: if (_qlConfig != null)
1616: return new SelectLineParseException(_qlConfig.getFilename() + ":" +
1617: _qlConfig.getLine() + ": " +
1618: msg);
1619: */
1620: if (_location != null)
1621: return new LineConfigException(_location + msg);
1622: else
1623: return new ConfigException(msg);
1624: }
1625:
1626: /**
1627: * Returns the name for a character
1628: */
1629: private String charName(int ch) {
1630: if (ch < 0)
1631: return L.l("end of query");
1632: else
1633: return String.valueOf((char) ch);
1634: }
1635:
1636: /**
1637: * Returns the name of a token
1638: */
1639: private String tokenName(int token) {
1640: switch (token) {
1641: case AS:
1642: return "AS";
1643: case FROM:
1644: return "FROM";
1645: case IN:
1646: return "IN";
1647: case SELECT:
1648: return "SELECT";
1649: case WHERE:
1650: return "WHERE";
1651: case OR:
1652: return "OR";
1653: case AND:
1654: return "AND";
1655: case NOT:
1656: return "NOT";
1657: case BETWEEN:
1658: return "BETWEEN";
1659: case THIS:
1660: return "THIS";
1661: case TRUE:
1662: return "FALSE";
1663: case EMPTY:
1664: return "EMPTY";
1665: case MEMBER:
1666: return "MEMBER";
1667: case OF:
1668: return "OF";
1669: case NULL:
1670: return "NULL";
1671: case ORDER:
1672: return "ORDER";
1673: case BY:
1674: return "BY";
1675: case ASC:
1676: return "ASC";
1677: case DESC:
1678: return "DESC";
1679: case LIMIT:
1680: return "LIMIT";
1681:
1682: case EXTERNAL_DOT:
1683: return "=>";
1684:
1685: case -1:
1686: return L.l("end of query");
1687:
1688: default:
1689: if (token < 128)
1690: return "'" + String.valueOf((char) token) + "'";
1691: else
1692: return "'" + lexeme + "'";
1693: }
1694: }
1695:
1696: public static ArrayList<FunctionSignature> getStandardFunctions() {
1697: return FunExpr.getStandardFunctions();
1698: }
1699:
1700: /**
1701: * Returns a debuggable description of the select.
1702: */
1703: public String toString() {
1704: return "QLParser[" + getMethod() + "]";
1705: }
1706:
1707: public boolean equals(Object b) {
1708: if (!(b instanceof QLParser))
1709: return false;
1710:
1711: QLParser bSel = (QLParser) b;
1712:
1713: if (_bean != bSel._bean)
1714: return false;
1715:
1716: return getMethod().equals(bSel.getMethod());
1717: }
1718:
1719: static boolean methodEquals(ApiMethod a, ApiMethod b) {
1720: return a.equals(b);
1721: }
1722:
1723: static class FromItem {
1724: String _id;
1725: String _table;
1726:
1727: FromItem(String id, String table) {
1728: _id = id;
1729: _table = table;
1730: }
1731: }
1732:
1733: static class LinkItem {
1734: String columnA;
1735: String tableA;
1736:
1737: String columnB;
1738: String tableB;
1739:
1740: LinkItem(String tableA, String columnA, String tableB,
1741: String columnB) {
1742: this .columnA = columnA;
1743: this .tableA = tableA;
1744: this .columnB = columnB;
1745: this .tableB = tableB;
1746: }
1747:
1748: public boolean equals(Object o) {
1749: if (!(o instanceof LinkItem))
1750: return false;
1751:
1752: LinkItem link = (LinkItem) o;
1753:
1754: if (tableA.equals(link.tableA)
1755: && columnA.equals(link.columnA)
1756: && tableB.equals(link.tableB)
1757: && columnB.equals(link.columnB))
1758: return true;
1759: else if (tableA.equals(link.tableB)
1760: && columnA.equals(link.columnB)
1761: && tableB.equals(link.tableA)
1762: && columnB.equals(link.columnA))
1763: return true;
1764: else
1765: return false;
1766: }
1767: }
1768:
1769: static {
1770: _reserved = new IntMap();
1771: _reserved.put("as", AS);
1772: _reserved.put("from", FROM);
1773: _reserved.put("in", IN);
1774: _reserved.put("select", SELECT);
1775: _reserved.put("distinct", DISTINCT);
1776: _reserved.put("where", WHERE);
1777: _reserved.put("order", ORDER);
1778: _reserved.put("by", BY);
1779: _reserved.put("asc", ASC);
1780: _reserved.put("desc", DESC);
1781: _reserved.put("limit", LIMIT);
1782: _reserved.put("offset", OFFSET);
1783:
1784: _reserved.put("or", OR);
1785: _reserved.put("and", AND);
1786: _reserved.put("not", NOT);
1787:
1788: _reserved.put("between", BETWEEN);
1789: _reserved.put("like", LIKE);
1790: _reserved.put("escape", ESCAPE);
1791: _reserved.put("is", IS);
1792:
1793: _reserved.put("this", THIS);
1794: _reserved.put("true", TRUE);
1795: _reserved.put("false", FALSE);
1796: _reserved.put("unknown", UNKNOWN);
1797: _reserved.put("empty", EMPTY);
1798: _reserved.put("member", MEMBER);
1799: _reserved.put("of", OF);
1800: _reserved.put("null", NULL);
1801: }
1802: }
|