public override ImmutableDictionary <string, string> Generate(CodeIndexerService codeIndexerService) { var result = new Dictionary <string, string>(); var theClass = codeIndexerService.GetClassDeclaration(_settings.BaseClass); var theClassSemanticModel = codeIndexerService.GetSemanticModel(theClass.SyntaxTree); var baseInterfaceName = theClass.BaseList.Types.Select(baseType => { var key = baseType.Type.ToString(); if (key.Contains("<")) { key = key.Substring(0, key.IndexOf('<')); } if (codeIndexerService.TryGetInterfaceDeclaration(key, out var _)) { return(key); } return(""); }).First(str => !string.IsNullOrWhiteSpace(str)); var baseInterface = codeIndexerService.GetInterfaceDeclaration(baseInterfaceName); var baseInterfaceSymbol = codeIndexerService.GetSymbol(baseInterface); var subInterfaces = new List <InterfaceDeclarationSyntax>(); foreach (var interfaceDeclaration in codeIndexerService.GetAllInterfaceDeclarations()) { if (Utilities.IsBaseInterface(codeIndexerService.GetSymbol(interfaceDeclaration), baseInterfaceSymbol)) { subInterfaces.Add(interfaceDeclaration); } } if (subInterfaces.Contains(baseInterface)) { subInterfaces.Remove(baseInterface); } var delegateMemberService = new DelegateMemberService(); var memberDeduplicationService = new MemberDeduplicationService(); var constructors = theClass.Members.OfType <ConstructorDeclarationSyntax>() .ToImmutableList(); var baseInterfaces = Utilities.GetBaseInterfaces(theClassSemanticModel.GetDeclaredSymbol(theClass)); var baseMembers = baseInterfaces.SelectMany(baseInterface => baseInterface.GetMembers()); var baseMemberExplicitImplementationProfiles = baseMembers.Select(baseMember => memberDeduplicationService.GetExplicitImplementationProfile(baseMember)).Distinct().ToImmutableHashSet(); var baseMemberImplementationProfiles = baseMembers.Select(baseMember => memberDeduplicationService.GetImplementationProfile(baseMember)).Distinct().ToImmutableHashSet(); foreach (var subInterface in subInterfaces) { var usings = new List <string>(); var classDefinition = new List <string>(); var subClassName = subInterface.Identifier.Text.Substring(1); foreach (var modifier in _settings.ClassNameModifiers) { subClassName = Regex.Replace(subClassName, modifier.Search ?? "", modifier.Replace ?? ""); } if (_settings.ClassNameBlacklist.Any(classNameBlacklistItem => Regex.IsMatch(subClassName, classNameBlacklistItem))) { continue; } if (!_settings.ClassNameWhitelist.All(classNameWhitelistItem => Regex.IsMatch(subClassName, classNameWhitelistItem))) { continue; } usings.AddRange(Utilities.GetDescendantsOfType <UsingDirectiveSyntax>(subInterface.SyntaxTree.GetRoot()) .Select(us => $"using {us.Name};\n")); usings.AddRange(Utilities.GetDescendantsOfType <UsingDirectiveSyntax>(theClass.SyntaxTree.GetRoot()) .Select(us => $"using {us.Name};\n")); classDefinition.Add($"\nnamespace {_settings.Namespace} {{\n"); var subInterfaceTypeArgs = string.Join(", ", subInterface.TypeParameterList.Parameters.Select(p => p.Identifier)); if (!string.IsNullOrWhiteSpace(subInterfaceTypeArgs)) { subInterfaceTypeArgs = $"<{subInterfaceTypeArgs}>"; } classDefinition.Add($"public class {subClassName}{theClass.TypeParameterList} : {theClass.Identifier}{theClass.TypeParameterList}, {subInterface.Identifier}{subInterfaceTypeArgs} {{\n"); var stuffAddedForSubInterface = Utilities.GetBaseInterfaces(codeIndexerService.GetSemanticModel(subInterface.SyntaxTree).GetDeclaredSymbol(subInterface)) .Except(Utilities.GetBaseInterfaces(theClassSemanticModel.GetDeclaredSymbol(theClass))); var adaptedParameter = constructors.First().ParameterList.Parameters.First(); var desiredAdaptedBaseInterfaces = Utilities .GetBaseInterfaces( theClassSemanticModel.GetSymbolInfo(adaptedParameter.Type).Symbol as INamedTypeSymbol) .Concat(stuffAddedForSubInterface).Select(x => x.ToString()).ToImmutableHashSet(); var adaptedParameterTypeArgs = ""; var tmp = adaptedParameter.Type.ToString(); if (tmp.Contains("<")) { adaptedParameterTypeArgs = tmp.Substring(tmp.IndexOf('<')); } if (_settings.AllowDifferentTypeParameters) { desiredAdaptedBaseInterfaces = desiredAdaptedBaseInterfaces.Select(Utilities.GetWithoutTypeArguments).ToImmutableHashSet(); } InterfaceDeclarationSyntax bestAdaptedInterface = null; foreach (var iface in codeIndexerService.GetAllInterfaceDeclarations()) { var ifaceBaseInterfaces = Utilities .GetBaseInterfaces(codeIndexerService.GetSemanticModel(iface.SyntaxTree) .GetDeclaredSymbol(iface)) .Select(x => x.ToString()).ToImmutableHashSet(); if (_settings.AllowDifferentTypeParameters) { ifaceBaseInterfaces = ifaceBaseInterfaces.Select(Utilities.GetWithoutTypeArguments).ToImmutableHashSet(); } //if (iface.Identifier == subInterface.Identifier) if (subInterface.Identifier.Text == "IDisposableDictionary") { int a = 3; var union = ifaceBaseInterfaces.Union(desiredAdaptedBaseInterfaces); var except1 = ifaceBaseInterfaces.Except(desiredAdaptedBaseInterfaces); var except2 = desiredAdaptedBaseInterfaces.Except(ifaceBaseInterfaces); } if (desiredAdaptedBaseInterfaces.Count == ifaceBaseInterfaces.Count) { if (ifaceBaseInterfaces.All(ifaceBaseInterface => desiredAdaptedBaseInterfaces.Contains(ifaceBaseInterface))) { bestAdaptedInterface = iface; break; } } } classDefinition.Add( $"private readonly {bestAdaptedInterface.Identifier}{adaptedParameterTypeArgs} _adapted;\n"); foreach (var constructor in constructors) { var constructorParameters = new List <string>(); var baseConstructorArguments = new List <string>(); constructorParameters.Add( $"{bestAdaptedInterface.Identifier}{adaptedParameterTypeArgs} adapted"); baseConstructorArguments.Add("adapted"); for (var i = 1; i < constructor.ParameterList.Parameters.Count; i++) { var parameter = constructor.ParameterList.Parameters[i]; constructorParameters.Add($"{parameter.Type} {parameter.Identifier}"); baseConstructorArguments.Add(parameter.Identifier.ToString()); } classDefinition.Add($"public {subClassName}("); classDefinition.Add(string.Join(", ", constructorParameters)); classDefinition.Add(") : base(" + string.Join(", ", baseConstructorArguments) + ") {\n"); classDefinition.Add("_adapted = adapted;"); classDefinition.Add("}\n"); } foreach (var member in memberDeduplicationService.GetDeduplicatedMembers(codeIndexerService.GetSemanticModel(bestAdaptedInterface.SyntaxTree).GetDeclaredSymbol(bestAdaptedInterface))) { if (member.Duplicates.All(duplicate => baseMemberImplementationProfiles.Contains(memberDeduplicationService.GetImplementationProfile(duplicate)))) { continue; } usings.AddRange(member.Duplicates.SelectMany(duplicate => duplicate.DeclaringSyntaxReferences).SelectMany(syntaxRef => { var root = syntaxRef.SyntaxTree.GetRoot(); var usingDirectives = Utilities.GetDescendantsOfType <UsingDirectiveSyntax>(root); return(usingDirectives.Select(usingDirective => $"{usingDirective}\n")); })); var sourceCodeBuilder = new StringBuilder(); var shouldOverride = member.Duplicates.Any(duplicate => duplicate.ContainingType.TypeKind == TypeKind.Class); delegateMemberService.DelegateMember(member.Value, "_adapted", null, member.ImplementExplicitly, sourceCodeBuilder, usings, DelegateType.DelegateObject, shouldOverride); classDefinition.Add(sourceCodeBuilder + "\n"); } classDefinition.Add("}\n}\n"); result[subClassName + ".g.cs"] = string.Join("", usings.Distinct().Concat(classDefinition)); } return(result.ToImmutableDictionary()); }
public override ImmutableDictionary <string, string> Generate(CodeIndexerService codeIndexerService) { var usings = new List <string>(); var theClasses = _settings.BaseClasses.SelectMany(theClassName => { var theClass = codeIndexerService.GetClassDeclaration(theClassName); var theClassSemanticModel = codeIndexerService.GetSemanticModel(theClass.SyntaxTree); var theClassSymbol = theClassSemanticModel.GetDeclaredSymbol(theClass); return(codeIndexerService.GetAllClassDeclarations().Where(classDecl => { var usingStatementSyntaxes = Utilities.GetDescendantsOfType <UsingDirectiveSyntax>(classDecl.SyntaxTree.GetRoot()); usings.AddRange(usingStatementSyntaxes .Select(us => us.ToString() + "\n")); var aClassSymbol = codeIndexerService.GetSemanticModel(classDecl.SyntaxTree).GetDeclaredSymbol(classDecl); return Utilities.IsBaseClass(aClassSymbol, theClassSymbol); })); }).ToImmutableDictionary(x => x.Identifier.Text); var extensionMethods = new List <string>(); extensionMethods.Add($"namespace {_settings.Namespace} {{\n"); extensionMethods.Add($"public static class {_settings.ExtensionMethodName}Extensions {{\n"); var constructors = new List <Tuple <ClassDeclarationSyntax, ConstructorDeclarationSyntax, SemanticModel> >(); foreach (var aClass in theClasses.Values) { var aClassSemanticModel = codeIndexerService.GetSemanticModel(aClass.SyntaxTree); usings.Add($"using {aClassSemanticModel.GetDeclaredSymbol(aClass).ContainingNamespace};\n"); foreach (var constructor in aClass.Members.OfType <ConstructorDeclarationSyntax>().Where(constructor => constructor.Modifiers.Any(SyntaxKind.PublicKeyword))) { constructors.Add(Tuple.Create(aClass, constructor, aClassSemanticModel)); } } var memberDeduplicationService = new MemberDeduplicationService(); var deduplicatedConstructors = memberDeduplicationService.DeduplicateMembers(constructors, x => x.Item3.GetDeclaredSymbol(x.Item2)); foreach (var deduplicatedMember in deduplicatedConstructors) { var(aClass, constructor, aClassSemanticModel) = deduplicatedMember.Value; var constructorArguments = string.Join(", ", constructor.ParameterList.Parameters.Select(parameter => parameter.Identifier.Text)); var parameters = string.Join(", ", constructor.ParameterList.Parameters.Select(parameter => $"{parameter.Type} {parameter.Identifier.Text}")); usings.AddRange(constructor.ParameterList.Parameters .Select(parameter => aClassSemanticModel.GetSymbolInfo(parameter.Type).Symbol) .Where(symbol => symbol != null) .Select(symbol => $"using {symbol.ContainingNamespace};\n")); var theInterface = aClass.BaseList.Types.First(type => { var symbol = aClassSemanticModel.GetSymbolInfo(type.Type).Symbol as INamedTypeSymbol; return(symbol.TypeKind == TypeKind.Interface); }); var typeArgs = string.Join(", ", aClass.TypeParameterList.Parameters.Select(parameter => parameter.Identifier.Text)); if (!string.IsNullOrWhiteSpace(typeArgs)) { typeArgs = $"<{typeArgs}>"; } extensionMethods.Add($"public static {theInterface.Type} {_settings.ExtensionMethodName}{typeArgs}(this {parameters}) {{\n"); extensionMethods.Add($"return new {aClass.Identifier}{typeArgs}({constructorArguments});"); extensionMethods.Add("}\n"); } extensionMethods.Add("}\n}\n"); return(ImmutableDictionary <string, string> .Empty .Add($"{_settings.ExtensionMethodName}Extensions.g.cs", string.Join("", usings.Distinct().OrderBy(x => x).Concat(extensionMethods)))); }
public override ImmutableDictionary <string, string> Generate(CodeIndexerService codeIndexerService) { var results = new Dictionary <string, string>(); foreach (var iface in _settings.InterfacesToImplement) { var interfaceDeclaration = codeIndexerService.GetInterfaceDeclaration(iface); var className = $"Anonymous{iface.Substring(1)}"; var sourceCodeBuilder = new StringBuilder(); var typeParameters = interfaceDeclaration.TypeParameterList.Parameters.Select(tps => tps.Identifier.Text).ToImmutableList(); var genericParams = ""; if (typeParameters.Count > 0) { genericParams = $"<{string.Join(", ", typeParameters)}>"; } var usings = new List <string>(); sourceCodeBuilder.AppendLine( $"namespace {_settings.Namespace} {{\npublic class {className}{genericParams} : {iface}{genericParams} {{"); var semanticModel = codeIndexerService.GetSemanticModel(interfaceDeclaration.SyntaxTree); var delegateMemberCandidates = new List <MemberToBeDelegated>(); var candidateParameters = new List <Parameter>(); GetInterfacesToImplementWith(semanticModel.GetDeclaredSymbol(interfaceDeclaration), candidateParameters, delegateMemberCandidates); var delegateMemberService = new DelegateMemberService(); var memberDeduplicationService = new MemberDeduplicationService(); var deduplicatedDelegatedMembers = memberDeduplicationService .DeduplicateMembers(delegateMemberCandidates, delegated => delegated.Member).ToImmutableList(); var parameters = candidateParameters.Where(candidateParameter => deduplicatedDelegatedMembers.Any(deduplicatedDelegatedMember => deduplicatedDelegatedMember.Value.MemberName == candidateParameter.MemberName)) .ToImmutableList(); var getEnumerator = deduplicatedDelegatedMembers.FirstOrDefault(delegateMemberCandidate => delegateMemberCandidate.Value.Member.Name == "GetEnumerator"); foreach (var baseInterface in Utilities.GetBaseInterfaces(semanticModel.GetDeclaredSymbol(interfaceDeclaration))) { var newUsing = $"using {string.Join(".", baseInterface.ContainingNamespace.ConstituentNamespaces)};"; if (!newUsing.Contains("global namespace")) { usings.Add(newUsing); } } foreach (var usingDirective in Utilities.GetBaseInterfaces(semanticModel.GetDeclaredSymbol(interfaceDeclaration)) .SelectMany(symbol => symbol.DeclaringSyntaxReferences).Select(syntaxRef => syntaxRef.SyntaxTree) .SelectMany(syntaxTree => Utilities.GetDescendantsOfType <UsingDirectiveSyntax>(syntaxTree.GetRoot()))) { var newUsing = usingDirective.ToString(); if (!newUsing.Contains("global namespace")) { usings.Add(newUsing); } } var parameterString = string.Join(", ", parameters .OrderBy(parameter => parameter.DelegateType).ThenBy(parameter => parameter.ParameterName) .Select(parameter => $"{parameter.Type} {parameter.ParameterName}")); sourceCodeBuilder.AppendLine(string.Join("\n", parameters .OrderBy(parameter => parameter.DelegateType).ThenBy(parameter => parameter.ParameterName) .Select(parameter => parameter.ToMemberDeclaration()))); sourceCodeBuilder.AppendLine($"public {className}({parameterString}) {{"); var memberAssignments = parameters .Select(parameter => $"{parameter.MemberName} = {parameter.ParameterName};"); sourceCodeBuilder.AppendLine(string.Join("\n", memberAssignments)); sourceCodeBuilder.AppendLine("}"); foreach (var deduplicatedMember in deduplicatedDelegatedMembers) { delegateMemberService.DelegateMember(deduplicatedMember.Value.Member, deduplicatedMember.Value.MemberName, deduplicatedMember.Value.SetMemberName, deduplicatedMember.ImplementExplicitly, sourceCodeBuilder, usings, deduplicatedMember.Value.Type, null); } if (getEnumerator != null) { sourceCodeBuilder.AppendLine("IEnumerator IEnumerable.GetEnumerator() {"); sourceCodeBuilder.Append($"return {getEnumerator.Value.MemberName}.GetEnumerator();"); sourceCodeBuilder.AppendLine("}"); } sourceCodeBuilder.AppendLine("}\n}\n"); usings = usings.Distinct().OrderBy(x => x).ToList(); var filePath = $"{className}.g.cs"; results.Add(filePath, $"{string.Join("\n",usings)}\n{sourceCodeBuilder}"); } return(results.ToImmutableDictionary()); }