antlr/Antlr4.Runtime/Misc/RuleDependencyChecker.cs

328 lines
9.5 KiB
C#

/*
* [The "BSD license"]
* Copyright (c) 2013 Terence Parr
* Copyright (c) 2013 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security;
using System.Text;
using Antlr4.Runtime;
using Sharpen;
using Sharpen.Annotation;
using Sharpen.Logging;
using Sharpen.Reflect;
namespace Antlr4.Runtime.Misc
{
/// <author>Sam Harwell</author>
public class RuleDependencyChecker
{
private static readonly Logger Logger = Logger.GetLogger(typeof(Antlr4.Runtime.Misc.RuleDependencyChecker
).FullName);
private static readonly ICollection<Type> checkedTypes = new HashSet<Type>();
public static void CheckDependencies<_T0>(Type<_T0> dependentClass)
{
if (IsChecked(dependentClass))
{
return;
}
IList<Type> typesToCheck = new List<Type>();
typesToCheck.AddItem(dependentClass);
Sharpen.Collections.AddAll(typesToCheck, dependentClass.GetDeclaredClasses());
foreach (Type clazz in typesToCheck)
{
if (IsChecked(clazz))
{
continue;
}
IList<Tuple<RuleDependency, IAnnotatedElement>> dependencies = GetDependencies(clazz
);
if (dependencies.IsEmpty())
{
continue;
}
CheckDependencies(dependencies, dependencies[0].GetItem1().Recognizer());
}
}
private static bool IsChecked<_T0>(Type<_T0> clazz)
{
lock (checkedTypes)
{
return checkedTypes.Contains(clazz);
}
}
private static void MarkChecked<_T0>(Type<_T0> clazz)
{
lock (checkedTypes)
{
checkedTypes.AddItem(clazz);
}
}
private static void CheckDependencies<_T0>(IList<Tuple<RuleDependency, IAnnotatedElement
>> dependencies, Type<_T0> recognizerClass) where _T0:Recognizer<object, object>
{
string[] ruleNames = GetRuleNames(recognizerClass);
int[] ruleVersions = GetRuleVersions(recognizerClass, ruleNames);
StringBuilder incompatible = new StringBuilder();
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
{
if (!recognizerClass.IsAssignableFrom(dependency.GetItem1().Recognizer()))
{
continue;
}
if (dependency.GetItem1().Rule() < 0 || dependency.GetItem1().Rule() >= ruleVersions
.Length)
{
incompatible.Append(string.Format("Element %s dependent on unknown rule %d@%d in %s\n"
, dependency.GetItem2().ToString(), dependency.GetItem1().Rule(), dependency.GetItem1
().Version(), dependency.GetItem1().Recognizer().Name));
}
else
{
if (ruleVersions[dependency.GetItem1().Rule()] != dependency.GetItem1().Version())
{
incompatible.Append(string.Format("Element %s dependent on rule %s@%d (found @%d) in %s\n"
, dependency.GetItem2().ToString(), ruleNames[dependency.GetItem1().Rule()], dependency
.GetItem1().Version(), ruleVersions[dependency.GetItem1().Rule()], dependency.GetItem1
().Recognizer().Name));
}
}
}
if (incompatible.Length != 0)
{
throw new InvalidOperationException(incompatible.ToString());
}
MarkChecked(recognizerClass);
}
private static int[] GetRuleVersions<_T0>(Type<_T0> recognizerClass, string[] ruleNames
) where _T0:Recognizer<object, object>
{
int[] versions = new int[ruleNames.Length];
FieldInfo[] fields = recognizerClass.GetFields();
foreach (FieldInfo field in fields)
{
bool isStatic = (field.GetModifiers() & Modifier.Static) != 0;
bool isInteger = field.FieldType == typeof(int);
if (isStatic && isInteger && field.Name.StartsWith("RULE_"))
{
try
{
string name = Sharpen.Runtime.Substring(field.Name, "RULE_".Length);
if (name.IsEmpty() || !System.Char.IsLower(name[0]))
{
continue;
}
int index = field.GetInt(null);
if (index < 0 || index >= versions.Length)
{
object[] @params = new object[] { index, field.Name, recognizerClass.Name };
Logger.Log(Level.Warning, "Rule index {0} for rule ''{1}'' out of bounds for recognizer {2}."
, @params);
continue;
}
MethodInfo ruleMethod = GetRuleMethod(recognizerClass, name);
if (ruleMethod == null)
{
object[] @params = new object[] { name, recognizerClass.Name };
Logger.Log(Level.Warning, "Could not find rule method for rule ''{0}'' in recognizer {1}."
, @params);
continue;
}
RuleVersion ruleVersion = ruleMethod.GetAnnotation<RuleVersion>();
int version = ruleVersion != null ? ruleVersion.Value() : 0;
versions[index] = version;
}
catch (ArgumentException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (MemberAccessException ex)
{
Logger.Log(Level.Warning, null, ex);
}
}
}
return versions;
}
private static MethodInfo GetRuleMethod<_T0>(Type<_T0> recognizerClass, string name
) where _T0:Recognizer<object, object>
{
MethodInfo[] declaredMethods = recognizerClass.GetMethods();
foreach (MethodInfo method in declaredMethods)
{
if (method.Name.Equals(name) && method.IsAnnotationPresent(typeof(RuleVersion)))
{
return method;
}
}
return null;
}
private static string[] GetRuleNames<_T0>(Type<_T0> recognizerClass) where _T0:Recognizer
<object, object>
{
try
{
FieldInfo ruleNames = recognizerClass.GetField("ruleNames");
return (string[])ruleNames.GetValue(null);
}
catch (NoSuchFieldException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (SecurityException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (ArgumentException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (MemberAccessException ex)
{
Logger.Log(Level.Warning, null, ex);
}
return new string[0];
}
public static IList<Tuple<RuleDependency, IAnnotatedElement>> GetDependencies<_T0
>(Type<_T0> clazz)
{
IList<Tuple<RuleDependency, IAnnotatedElement>> result = new List<Tuple<RuleDependency
, IAnnotatedElement>>();
IList<ElementType> supportedTarget = Arrays.AsList(typeof(RuleDependency).GetAnnotation
<Target>().Value());
foreach (ElementType target in supportedTarget)
{
switch (target)
{
case ElementType.Type:
{
if (!clazz.IsAnnotation())
{
GetElementDependencies(clazz, result);
}
break;
}
case ElementType.AnnotationType:
{
if (!clazz.IsAnnotation())
{
GetElementDependencies(clazz, result);
}
break;
}
case ElementType.Constructor:
{
foreach (Constructor<object> ctor in clazz.GetDeclaredConstructors())
{
GetElementDependencies(ctor, result);
}
break;
}
case ElementType.Field:
{
foreach (FieldInfo field in Sharpen.Runtime.GetDeclaredFields(clazz))
{
GetElementDependencies(field, result);
}
break;
}
case ElementType.LocalVariable:
{
System.Console.Error.WriteLine("Runtime rule dependency checking is not supported for local variables."
);
break;
}
case ElementType.Method:
{
foreach (MethodInfo method in Sharpen.Runtime.GetDeclaredMethods(clazz))
{
GetElementDependencies(method, result);
}
break;
}
case ElementType.Package:
{
// package is not a subset of class, so nothing to do here
break;
}
case ElementType.Parameter:
{
System.Console.Error.WriteLine("Runtime rule dependency checking is not supported for parameters."
);
break;
}
}
}
return result;
}
private static void GetElementDependencies(IAnnotatedElement annotatedElement, IList
<Tuple<RuleDependency, IAnnotatedElement>> result)
{
RuleDependency dependency = annotatedElement.GetAnnotation<RuleDependency>();
if (dependency != null)
{
result.AddItem(Tuple.Create(dependency, annotatedElement));
}
RuleDependencies dependencies = annotatedElement.GetAnnotation<RuleDependencies>(
);
if (dependencies != null)
{
foreach (RuleDependency d in dependencies.Value())
{
if (d != null)
{
result.AddItem(Tuple.Create(d, annotatedElement));
}
}
}
}
public RuleDependencyChecker()
{
}
}
}