using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using CppSharp.AST;
using CppSharp.AST.Extensions;
namespace CppSharp.Passes
{
///
/// Base class for transform that perform renames of declarations.
///
public abstract class RenamePass : TranslationUnitPass
{
public class ParameterComparer : IEqualityComparer
{
public bool Equals(Parameter x, Parameter y)
{
return x.QualifiedType == y.QualifiedType && x.GenerationKind == y.GenerationKind;
}
public int GetHashCode(Parameter obj)
{
return obj.Type.GetHashCode();
}
}
public RenameTargets Targets = RenameTargets.Any;
protected RenamePass()
{
VisitOptions.VisitFunctionReturnType = false;
VisitOptions.VisitTemplateArguments = false;
// these need to be visited but in a different order (see VisitClassDecl) so disable the default order
VisitOptions.VisitClassProperties = false;
VisitOptions.VisitClassMethods = false;
VisitOptions.VisitNamespaceEvents = false;
}
protected RenamePass(RenameTargets targets)
: this()
{
Targets = targets;
}
public virtual bool Rename(Declaration decl, out string newName)
{
var method = decl as Method;
if (method != null && !method.IsStatic)
{
var rootBaseMethod = ((Class) method.Namespace).GetBaseMethod(method);
if (rootBaseMethod != null && rootBaseMethod != method)
{
newName = rootBaseMethod.Name;
return true;
}
}
var property = decl as Property;
if (property != null && !property.IsStatic)
{
var rootBaseProperty = ((Class) property.Namespace).GetBaseProperty(property);
if (rootBaseProperty != null && rootBaseProperty != property)
{
newName = rootBaseProperty.Name;
return true;
}
}
newName = decl.Name;
return false;
}
public bool IsRenameableDecl(Declaration decl)
{
if (decl is Class)
return Targets.HasFlag(RenameTargets.Class);
var method = decl as Method;
if (method != null)
{
return Targets.HasFlag(RenameTargets.Method) &&
method.Kind == CXXMethodKind.Normal &&
method.Name != "dispose";
}
var function = decl as Function;
if (function != null)
{
// Special case the IDisposable.Dispose method.
return Targets.HasFlag(RenameTargets.Function) &&
(!function.IsOperator && function.Name != "dispose");
}
if (decl is Parameter)
return Targets.HasFlag(RenameTargets.Parameter);
if (decl is Enumeration.Item)
return Targets.HasFlag(RenameTargets.EnumItem);
if (decl is Enumeration)
return Targets.HasFlag(RenameTargets.Enum);
var property = decl as Property;
if (property != null)
return Targets.HasFlag(RenameTargets.Property) && !property.IsIndexer;
if (decl is Event)
return Targets.HasFlag(RenameTargets.Event);
if (decl is TypedefDecl)
return Targets.HasFlag(RenameTargets.Delegate);
if (decl is Namespace && !(decl is TranslationUnit)) return true;
if (decl is Variable)
return Targets.HasFlag(RenameTargets.Variable);
var field = decl as Field;
if (field != null)
{
if (!Targets.HasFlag(RenameTargets.Field))
return false;
var fieldProperty = ((Class) field.Namespace).Properties.FirstOrDefault(
p => p.Field == field);
return (fieldProperty != null &&
fieldProperty.IsInRefTypeAndBackedByValueClassField());
}
return false;
}
public override bool VisitDeclaration(Declaration decl)
{
if (AlreadyVisited(decl))
return false;
if (!IsRenameableDecl(decl))
return true;
if (decl.Name == null)
return true;
var renamed = Rename(decl);
return true;
}
private bool Rename(Declaration decl)
{
string newName;
if (!Rename(decl, out newName) || AreThereConflicts(decl, newName))
return false;
decl.Name = newName;
return true;
}
private static bool AreThereConflicts(Declaration decl, string newName)
{
if (decl is Parameter)
return false;
var declarations = new List();
declarations.AddRange(decl.Namespace.Classes.Where(c => !c.IsIncomplete));
declarations.AddRange(decl.Namespace.Enums);
declarations.AddRange(decl.Namespace.Events);
var function = decl as Function;
if (function != null && function.SynthKind != FunctionSynthKind.AdjustedMethod)
{
declarations.Add(function.Namespace);
// account for overloads
declarations.AddRange(GetFunctionsWithTheSameParams(function));
}
else
declarations.AddRange(decl.Namespace.Functions);
declarations.AddRange(decl.Namespace.Variables);
declarations.AddRange(from typedefDecl in decl.Namespace.Typedefs
let pointerType = typedefDecl.Type.Desugar() as PointerType
where pointerType != null && pointerType.GetFinalPointee() is FunctionType
select typedefDecl);
var specialization = decl as ClassTemplateSpecialization;
if (specialization != null)
declarations.RemoveAll(d => specialization.TemplatedDecl.TemplatedDecl == d);
var @class = decl.Namespace as Class;
if (@class != null && @class.IsDependent)
declarations.AddRange(@class.TemplateParameters);
var result = declarations.Any(d => d != decl && d.Name == newName);
if (result)
return true;
if (decl is Method && decl.IsGenerated)
return @class.GetPropertyByName(newName) != null;
var property = decl as Property;
if (property != null && property.Field != null)
return ((Class) decl.Namespace).Properties.FirstOrDefault(
p => p != decl && p.Name == newName) != null;
return false;
}
private static IEnumerable GetFunctionsWithTheSameParams(Function function)
{
var method = function as Method;
if (method != null)
{
return ((Class) method.Namespace).Methods.Where(
m => !m.Ignore && m.Parameters.SequenceEqual(function.Parameters, new ParameterComparer()));
}
return function.Namespace.Functions.Where(
f => !f.Ignore && f.Parameters.SequenceEqual(function.Parameters, new ParameterComparer()));
}
public override bool VisitClassDecl(Class @class)
{
if (!base.VisitClassDecl(@class))
return false;
foreach (var property in @class.Properties.OrderByDescending(p => p.Access))
VisitProperty(property);
foreach (var method in @class.Methods)
VisitMethodDecl(method);
foreach (var @event in @class.Events)
VisitEvent(@event);
return true;
}
public override bool VisitParameterDecl(Parameter parameter)
{
return base.VisitDeclaration(parameter);
}
}
[Flags]
public enum RenameTargets
{
Class = 1 << 0,
Field = 1 << 1,
Method = 1 << 2,
Function = 1 << 3,
Parameter = 1 << 4,
Enum = 1 << 5,
EnumItem = 1 << 6,
Event = 1 << 7,
Property = 1 << 8,
Delegate = 1 << 9,
Variable = 1 << 10,
Any = Function | Method | Parameter | Class | Field | Enum | EnumItem | Event | Property | Delegate | Variable
}
///
/// Renames a declaration based on a regular expression pattern.
///
public class RegexRenamePass : RenamePass
{
public string Pattern;
public string Replacement;
public RegexRenamePass(string pattern, string replacement)
{
Pattern = pattern;
Replacement = replacement;
}
public RegexRenamePass(string pattern, string replacement,
RenameTargets targets)
: this(pattern, replacement)
{
Targets = targets;
}
public override bool Rename(Declaration decl, out string newName)
{
if (base.Rename(decl, out newName))
return true;
var replace = Regex.Replace(decl.Name, Pattern, Replacement);
if (!decl.Name.Equals(replace))
{
newName = replace;
return true;
}
newName = null;
return false;
}
}
public enum RenameCasePattern
{
UpperCamelCase,
LowerCamelCase
}
///
/// Renames a declaration based on a pre-defined pattern.
///
public class CaseRenamePass : RenamePass
{
public RenameCasePattern Pattern;
public CaseRenamePass(RenameTargets targets, RenameCasePattern pattern)
: base(targets)
{
Pattern = pattern;
}
public override bool Rename(Declaration decl, out string newName)
{
if (base.Rename(decl, out newName))
return true;
newName = ConvertCaseString(decl, Pattern);
return true;
}
///
/// Converts the phrase to specified convention.
///
///
/// The cases.
/// string
static string ConvertCaseString(Declaration decl, RenameCasePattern pattern)
{
if (decl.Name.All(c => !char.IsLetter(c)))
return decl.Name;
var typedef = decl as TypedefDecl;
if (typedef != null && typedef.IsSynthetized)
return decl.Name;
var property = decl as Property;
if (property != null && property.GetMethod != null &&
property.GetMethod.SynthKind == FunctionSynthKind.InterfaceInstance)
return decl.Name;
var sb = new StringBuilder(decl.Name);
// check if it's been renamed to avoid a keyword
if (sb[0] == '@')
sb.Remove(0, 1);
for (int i = sb.Length - 1; i >= 0; i--)
{
// ensure separation by not ending up with more capitals or digits in a row than before
if (sb[i] != '_' || (i > 0 && (char.IsUpper(sb[i - 1]) ||
(i < sb.Length - 1 && char.IsDigit(sb[i + 1]) && char.IsDigit(sb[i - 1])))))
continue;
if (i < sb.Length - 1)
sb[i + 1] = char.ToUpperInvariant(sb[i + 1]);
sb.Remove(i, 1);
}
var @class = decl as Class;
switch (pattern)
{
case RenameCasePattern.UpperCamelCase:
// ensure separation in enum items by not ending up with more capitals in a row than before
if (sb.Length == 1 || !char.IsUpper(sb[1]) || !(decl is Enumeration.Item))
sb[0] = char.ToUpperInvariant(sb[0]);
if (@class != null && @class.Type == ClassType.Interface)
sb[1] = char.ToUpperInvariant(sb[1]);
break;
case RenameCasePattern.LowerCamelCase:
sb[0] = char.ToLowerInvariant(sb[0]);
if (@class != null && @class.Type == ClassType.Interface)
sb[1] = char.ToLowerInvariant(sb[1]);
break;
}
return sb.ToString();
}
}
public static class RenamePassExtensions
{
public static void RenameWithPattern(this PassBuilder builder,
string pattern, string replacement, RenameTargets targets)
{
builder.AddPass(new RegexRenamePass(pattern, replacement, targets));
}
public static void RemovePrefix(this PassBuilder builder, string prefix,
RenameTargets targets = RenameTargets.Any)
{
builder.AddPass(new RegexRenamePass("^" + prefix, string.Empty,
targets));
}
public static void RenameDeclsCase(this PassBuilder builder,
RenameTargets targets, RenameCasePattern pattern)
{
builder.AddPass(new CaseRenamePass(targets, pattern));
}
public static void RenameDeclsUpperCase(this PassBuilder builder,
RenameTargets targets)
{
builder.AddPass(new CaseRenamePass(targets,
RenameCasePattern.UpperCamelCase));
}
public static void RenameDeclsLowerCase(this PassBuilder builder,
RenameTargets targets)
{
builder.AddPass(new CaseRenamePass(targets,
RenameCasePattern.LowerCamelCase));
}
}
}