blob: 2c4e22875bf5160d356d834d3fd60175f3d4ebb1 [file] [log] [blame]
using Lucene.Net.Support;
using System;
using System.Diagnostics.CodeAnalysis;
#if FEATURE_SERIALIZABLE_EXCEPTIONS
using System.Runtime.Serialization;
using System.Security.Permissions;
#endif
using System.Text;
namespace Lucene.Net.QueryParsers.Classic
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// This exception is thrown when parse errors are encountered.
/// You can explicitly create objects of this exception type by
/// calling the method GenerateParseException in the generated
/// parser.
///
/// You can modify this class to customize your error reporting
/// mechanisms so long as you retain the public fields.
/// </summary>
// LUCENENET: It is no longer good practice to use binary serialization.
// See: https://github.com/dotnet/corefx/issues/23584#issuecomment-325724568
#if FEATURE_SERIALIZABLE_EXCEPTIONS
[Serializable]
#endif
public class ParseException : Exception
{
/// <summary>
/// This constructor is used by the method GenerateParseException()
/// in the generated parser. Calling this constructor generates
/// a new object of this type with the fields <paramref name="currentToken"/>,
/// <paramref name="expectedTokenSequences"/>, and <paramref name="tokenImage"/> set.
/// </summary>
public ParseException(Token currentToken,
int[][] expectedTokenSequences,
string[] tokenImage)
: base(Initialize(currentToken, expectedTokenSequences, tokenImage))
{
this.CurrentToken = currentToken;
this.ExpectedTokenSequences = expectedTokenSequences;
this.TokenImage = tokenImage;
}
/**
* The following constructors are for use by you for whatever
* purpose you can think of. Constructing the exception in this
* manner makes the exception behave in the normal way - i.e., as
* documented in the class "Throwable". The fields "errorToken",
* "expectedTokenSequences", and "tokenImage" do not contain
* relevant information. The JavaCC generated code does not use
* these constructors.
*/
public ParseException()
{ }
public ParseException(string message)
: base(message)
{ }
public ParseException(string message, Exception innerException)
: base(message, innerException)
{ }
#if FEATURE_SERIALIZABLE_EXCEPTIONS
/// <summary>
/// Initializes a new instance of this class with serialized data.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
protected ParseException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
CurrentToken = (Token)info.GetValue("CurrentToken", typeof(Token));
ExpectedTokenSequences = (int[][])info.GetValue("ExpectedTokenSequences", typeof(int[][]));
TokenImage = (string[])info.GetValue("TokenImage", typeof(string[]));
}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("CurrentToken", CurrentToken, typeof(Token));
info.AddValue("ExpectedTokenSequences", ExpectedTokenSequences, typeof(int[][]));
info.AddValue("TokenImage", TokenImage, typeof(string[]));
}
#endif
/// <summary>
/// This is the last token that has been consumed successfully. If
/// this object has been created due to a parse error, the token
/// following this token will (therefore) be the first error token.
/// </summary>
public Token CurrentToken { get; set; }
/// <summary>
/// Each entry in this array is an array of integers. Each array
/// of integers represents a sequence of tokens (by their ordinal
/// values) that is expected at this point of the parse.
/// </summary>
[WritableArray]
[SuppressMessage("Microsoft.Performance", "CA1819", Justification = "Lucene's design requires some writable array properties")]
public int[][] ExpectedTokenSequences { get; set; }
/// <summary>
/// This is a reference to the "tokenImage" array of the generated
/// parser within which the parse error occurred. This array is
/// defined in the generated ...Constants interface.
/// </summary>
[WritableArray]
[SuppressMessage("Microsoft.Performance", "CA1819", Justification = "Lucene's design requires some writable array properties")]
public string[] TokenImage { get; set; }
/// <summary>
/// It uses <paramref name="currentToken"/> and <paramref name="expectedTokenSequences"/> to generate a parse
/// error message and returns it. If this object has been created
/// due to a parse error, and you do not catch it (it gets thrown
/// from the parser) the correct error message
/// gets displayed.
/// </summary>
private static string Initialize(Token currentToken,
int[][] expectedTokenSequences,
string[] tokenImage)
{
StringBuilder expected = new StringBuilder();
int maxSize = 0;
for (int i = 0; i < expectedTokenSequences.Length; i++)
{
if (maxSize < expectedTokenSequences[i].Length)
{
maxSize = expectedTokenSequences[i].Length;
}
for (int j = 0; j < expectedTokenSequences[i].Length; j++)
{
expected.Append(tokenImage[expectedTokenSequences[i][j]]).Append(' ');
}
if (expectedTokenSequences[i][expectedTokenSequences[i].Length - 1] != 0)
{
expected.Append("...");
}
expected.Append(eol).Append(" ");
}
string retval = "Encountered \"";
Token tok = currentToken.Next;
for (int i = 0; i < maxSize; i++)
{
if (i != 0)
retval += " ";
if (tok.Kind == 0)
{
retval += tokenImage[0];
break;
}
retval += (" " + tokenImage[tok.Kind]);
retval += " \"";
retval += AddEscapes(tok.Image);
retval += " \"";
tok = tok.Next;
}
retval += ("\" at line " + currentToken.Next.BeginLine + ", column " + currentToken.Next.BeginColumn);
retval += ("." + eol);
if (expectedTokenSequences.Length == 1)
{
retval += ("Was expecting:" + eol + " ");
}
else
{
retval += ("Was expecting one of:" + eol + " ");
}
retval += expected.ToString();
return retval;
}
/// <summary>
/// The end of line string for this machine.
/// </summary>
protected static string eol = Environment.NewLine;
/// <summary>
/// Used to convert raw characters to their escaped version
/// when these raw version cannot be used as part of an ASCII
/// string literal.
/// </summary>
internal static string AddEscapes(string str)
{
StringBuilder retval = new StringBuilder();
char ch;
for (int i = 0; i < str.Length; i++)
{
switch (str[i])
{
case (char) (0):
continue;
case '\b':
retval.Append("\\b");
continue;
case '\t':
retval.Append("\\t");
continue;
case '\n':
retval.Append("\\n");
continue;
case '\f':
retval.Append("\\f");
continue;
case '\r':
retval.Append("\\r");
continue;
case '\"':
retval.Append("\\\"");
continue;
case '\'':
retval.Append("\\\'");
continue;
case '\\':
retval.Append("\\\\");
continue;
default:
if ((ch = str[i]) < 0x20 || ch > 0x7e)
{
string s = "0000" + Convert.ToString(ch, 16);
retval.Append("\\u" + s.Substring(s.Length - 4, (s.Length) - (s.Length - 4)));
}
else
{
retval.Append(ch);
}
continue;
}
}
return retval.ToString();
}
}
}