001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.kernel.jpql;
020:
021: import java.util.TreeSet;
022:
023: import org.apache.openjpa.lib.util.Localizer;
024: import org.apache.openjpa.lib.util.Localizer.Message;
025: import org.apache.openjpa.util.UserException;
026:
027: /**
028: * Signifies that a lexical error occurred when parsing the JPQL statement.
029: *
030: * @author Marc Prud'hommeaux
031: */
032: public class ParseException extends UserException {
033:
034: private static final Localizer _loc = Localizer
035: .forPackage(ParseException.class);
036:
037: /**
038: * This constructor is used by the method "generateParseException"
039: * in the generated parser. Calling this constructor generates
040: * a new object of this type with the fields "currentToken",
041: * "expectedTokenSequences", and "tokenImage" set.
042: * This constructor calls its super class with the empty string
043: * to force the "toString" method of parent class "Throwable" to
044: * print the error message in the form:
045: * ParseException: <result of getMessage>
046: */
047: public ParseException(Token currentTokenVal,
048: int[][] expectedTokenSequencesVal, String[] tokenImageVal) {
049: super (initMessage(currentTokenVal, expectedTokenSequencesVal,
050: tokenImageVal));
051: }
052:
053: /**
054: * The following constructors are for use by you for whatever
055: * purpose you can think of. Constructing the exception in this
056: * manner makes the exception behave in the normal way - i.e., as
057: * documented in the class "Throwable". The fields "errorToken",
058: * "expectedTokenSequences", and "tokenImage" do not contain
059: * relevant information. The JavaCC generated code does not use
060: * these constructors.
061: */
062: public ParseException() {
063: super ();
064: }
065:
066: /**
067: * String constructor. Constructing the exception in this
068: * manner makes the exception behave in the normal way - i.e., as
069: * documented in the class "Throwable". The fields "errorToken",
070: * "expectedTokenSequences", and "tokenImage" do not contain
071: * relevant information. The JavaCC generated code does not use
072: * these constructors.
073: */
074: public ParseException(String message) {
075: super (message);
076: }
077:
078: /**
079: * This method has the standard behavior when this object has been
080: * created using the standard constructors. Otherwise, it uses
081: * "currentToken" and "expectedTokenSequences" to generate a parse
082: * error message and returns it. If this object has been created
083: * due to a parse error, and you do not catch it (it gets thrown
084: * from the parser), then this method is called during the printing
085: * of the final stack trace, and hence the correct error message
086: * gets displayed.
087: */
088: private static Message initMessage(Token currentToken,
089: int[][] expectedTokenSequences, String[] tokenImage) {
090: TreeSet expected = new TreeSet();
091:
092: int maxSize = 0;
093:
094: for (int i = 0; i < expectedTokenSequences.length; i++) {
095: if (maxSize < expectedTokenSequences[i].length)
096: maxSize = expectedTokenSequences[i].length;
097:
098: for (int j = 0; j < expectedTokenSequences[i].length; j++)
099: expected.add(tokenImage[expectedTokenSequences[i][j]]);
100: }
101:
102: Token tok = currentToken.next;
103:
104: String curtok = "";
105: for (int i = 0; i < maxSize; i++) {
106: if (i != 0)
107: curtok += " ";
108: if (tok.kind == 0) {
109: curtok += tokenImage[0];
110: break;
111: }
112:
113: curtok += escape(tok.image);
114: tok = tok.next;
115: }
116:
117: return _loc.get("bad-parse", new Object[] { curtok,
118: new Integer(currentToken.next.beginColumn), expected });
119: }
120:
121: /**
122: * Used to convert raw characters to their escaped version
123: * when these raw version cannot be used as part of an ASCII string literal.
124: */
125: private static String escape(String str) {
126: StringBuffer retval = new StringBuffer();
127: char ch;
128:
129: for (int i = 0; i < str.length(); i++) {
130: switch (str.charAt(i)) {
131: case 0:
132: continue;
133: case '\b':
134: retval.append("\\b");
135: continue;
136: case '\t':
137: retval.append("\\t");
138: continue;
139: case '\n':
140: retval.append("\\n");
141: continue;
142: case '\f':
143: retval.append("\\f");
144: continue;
145: case '\r':
146: retval.append("\\r");
147: continue;
148: case '\"':
149: retval.append("\\\"");
150: continue;
151: case '\'':
152: retval.append("\\\'");
153: continue;
154: case '\\':
155: retval.append("\\\\");
156: continue;
157: default:
158: if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
159: String s = "0000" + Integer.toString(ch, 16);
160:
161: retval.append("\\u"
162: + s.substring(s.length() - 4, s.length()));
163: } else {
164: retval.append(ch);
165: }
166: continue;
167: }
168: }
169: return retval.toString();
170: }
171:
172: }
|