|
//------------------------------------------------------------------------------
// <copyright file="MobileErrorInfo.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Security.Permissions;
namespace System.Web.Mobile
{
/*
* Mobile Error Info
* Contains information about an error that occurs in a mobile application.
* This information can be used to format the error for the target device.
*
*
*/
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
public class MobileErrorInfo
{
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.ContextKey"]/*' />
public static readonly String ContextKey = "MobileErrorInfo";
private static object _lockObject = new object();
private const String _errorType = "Type";
private const String _errorDescription = "Description";
private const String _errorMiscTitle = "MiscTitle";
private const String _errorMiscText = "MiscText";
private const String _errorFile = "File";
private const String _errorLineNumber = "LineNumber";
private static Regex[] _searchExpressions = null;
private static bool _searchExpressionsBuilt = false;
private const int _expressionCount = 3;
private StringDictionary _dictionary = new StringDictionary();
internal MobileErrorInfo(Exception e)
{
// Don't want any failure to escape here...
try
{
// For some reason, the compile exception lives in the
// InnerException.
HttpCompileException compileException =
e.InnerException as HttpCompileException;
if (compileException != null)
{
this.Type = SR.GetString(SR.MobileErrorInfo_CompilationErrorType);
this.Description = SR.GetString(SR.MobileErrorInfo_CompilationErrorDescription);
this.MiscTitle = SR.GetString(SR.MobileErrorInfo_CompilationErrorMiscTitle);
CompilerErrorCollection errors = compileException.Results.Errors;
if (errors != null && errors.Count >= 1)
{
CompilerError error = errors[0];
this.LineNumber = error.Line.ToString(CultureInfo.InvariantCulture);
this.File = error.FileName;
this.MiscText = error.ErrorNumber + ":" + error.ErrorText;
}
else
{
this.LineNumber = SR.GetString(SR.MobileErrorInfo_Unknown);
this.File = SR.GetString(SR.MobileErrorInfo_Unknown);
this.MiscText = SR.GetString(SR.MobileErrorInfo_Unknown);
}
return;
}
HttpParseException parseException = e as HttpParseException;
if (parseException != null)
{
this.Type = SR.GetString(SR.MobileErrorInfo_ParserErrorType);
this.Description = SR.GetString(SR.MobileErrorInfo_ParserErrorDescription);
this.MiscTitle = SR.GetString(SR.MobileErrorInfo_ParserErrorMiscTitle);
this.LineNumber = parseException.Line.ToString(CultureInfo.InvariantCulture);
this.File = parseException.FileName;
this.MiscText = parseException.Message;
return;
}
// We try to use the hacky way of parsing an HttpException of an
// unknown subclass.
HttpException httpException = e as HttpException;
if (httpException != null && ParseHttpException(httpException))
{
return;
}
}
catch
{
// Don't need to do anything here, just continue to base case
// below.
}
// Default to the most basic if none of the above succeed.
this.Type = e.GetType().FullName;
this.Description = e.Message;
this.MiscTitle = SR.GetString(SR.MobileErrorInfo_SourceObject);
String s = e.StackTrace;
if(s != null) {
int i = s.IndexOf('\r');
if (i != -1)
{
s = s.Substring(0, i);
}
this.MiscText = s;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.this"]/*' />
public String this[String key]
{
get
{
String s = _dictionary[key];
return (s == null) ? String.Empty : s;
}
set
{
_dictionary[key] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.Type"]/*' />
public String Type
{
get
{
return this[_errorType];
}
set
{
this[_errorType] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.Description"]/*' />
public String Description
{
get
{
return this[_errorDescription];
}
set
{
this[_errorDescription] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.MiscTitle"]/*' />
public String MiscTitle
{
get
{
return this[_errorMiscTitle];
}
set
{
this[_errorMiscTitle] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.MiscText"]/*' />
public String MiscText
{
get
{
return this[_errorMiscText];
}
set
{
this[_errorMiscText] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.File"]/*' />
public String File
{
get
{
return this[_errorFile];
}
set
{
this[_errorFile] = value;
}
}
/// <include file='doc\MobileErrorInfo.uex' path='docs/doc[@for="MobileErrorInfo.LineNumber"]/*' />
public String LineNumber
{
get
{
return this[_errorLineNumber];
}
set
{
this[_errorLineNumber] = value;
}
}
// Return true if we succeed
private bool ParseHttpException(HttpException e)
{
int i;
Match match = null;
String errorMessage = e.GetHtmlErrorMessage();
if (errorMessage == null)
{
return false;
}
// Use regular expressions to scrape the message output
// for meaningful data. One problem: Some parts of the
// output are optional, and any regular expression that
// uses the ()? syntax doesn't pick it up. So, we have
// to have all the different combinations of expressions,
// and use each one in order.
EnsureSearchExpressions();
for (i = 0; i < _expressionCount; i++)
{
match = _searchExpressions[i].Match(errorMessage);
if (match.Success)
{
break;
}
}
if (i == _expressionCount)
{
return false;
}
this.Type = TrimAndClean(match.Result("${title}"));
this.Description = TrimAndClean(match.Result("${description}"));
if (i <= 1)
{
// These expressions were able to match the miscellaneous
// title/text section.
this.MiscTitle = TrimAndClean(match.Result("${misctitle}"));
this.MiscText = TrimAndClean(match.Result("${misctext}"));
}
if (i == 0)
{
// This expression was able to match the file/line #
// section.
this.File = TrimAndClean(match.Result("${file}"));
this.LineNumber = TrimAndClean(match.Result("${linenumber}"));
}
return true;
}
private static void EnsureSearchExpressions()
{
// Create precompiled search expressions. They're here
// rather than in static variables, so that we can load
// them from resources on demand. But once they're loaded,
// they're compiled and always available.
if (!_searchExpressionsBuilt)
{
lock(_lockObject)
{
if (!_searchExpressionsBuilt)
{
//
// Why three similar expressions? See ParseHttpException above.
_searchExpressions = new Regex[_expressionCount];
_searchExpressions[0] = new Regex(
"<title>(?'title'.*?)</title>.*?" +
": </b>(?'description'.*?)<br>.*?" +
"(<b>(?'misctitle'.*?): </b>(?'misctext'.*?)<br)+.*?" +
"(Source File:</b>(?'file'.*?) <b>Line:</b>(?'linenumber'.*?)<br)+",
RegexOptions.Singleline |
RegexOptions.IgnoreCase |
RegexOptions.CultureInvariant |
RegexOptions.Compiled);
_searchExpressions[1] = new Regex(
"<title>(?'title'.*?)</title>.*?" +
": </b>(?'description'.*?)<br>.*?" +
"(<b>(?'misctitle'.*?): </b>(?'misctext'.*?)<br)+.*?",
RegexOptions.Singleline |
RegexOptions.IgnoreCase |
RegexOptions.CultureInvariant |
RegexOptions.Compiled);
_searchExpressions[2] = new Regex(
"<title>(?'title'.*?)</title>.*?: </b>(?'description'.*?)<br>",
RegexOptions.Singleline |
RegexOptions.IgnoreCase |
RegexOptions.CultureInvariant |
RegexOptions.Compiled);
_searchExpressionsBuilt = true;
}
}
}
}
private static String TrimAndClean(String s)
{
return s.Replace("\r\n", " ").Trim();
}
}
}
|