11using Microsoft . CodeAnalysis ;
22using Microsoft . CodeAnalysis . CSharp ;
33using Microsoft . CodeAnalysis . CSharp . Syntax ;
4+ using System . Text ;
45
56namespace RoyalCode . SmartSelector . Generators . Generators ;
67
@@ -14,7 +15,6 @@ internal static class AutoPropertiesGenerator
1415 internal static MatchOptions MatchOptions { get ; } = new ( )
1516 {
1617 OriginPropertiesRetriever = new AutoPropertyOriginPropertiesRetriever ( ) ,
17- //TargetPropertiesRetriever = new AutoPropertyTargetPropertiesRetriever(),
1818 } ;
1919
2020 internal static bool Predicate ( SyntaxNode node , CancellationToken token )
@@ -111,19 +111,24 @@ classSymbol is null
111111 internal static AutoPropertiesInformation CreateInformation (
112112 TypeDescriptor modelType ,
113113 TypeDescriptor fromType ,
114- AttributeSyntax properties )
114+ AttributeSyntax autoPropertyAttribute )
115115 {
116116 // collect excluded property names using extension helpers
117117 var excluded = new HashSet < string > ( StringComparer . Ordinal ) ;
118118
119- // removido por hora
120- ////foreach (var name in properties.GetConstructorStringSet())
121- //// excluded.Add(name);
122-
123- foreach ( var name in properties . GetNamedArgumentStringSet ( "Exclude" ) )
119+ foreach ( var name in autoPropertyAttribute . GetNamedArgumentStrings ( "Exclude" ) )
124120 excluded . Add ( name ) ;
125121
126- return CreateInformation ( modelType , fromType , excluded ) ;
122+ // collect flattening property names using extension helpers
123+ HashSet < string > ? flattening = null ;
124+ var flatteningNames = autoPropertyAttribute . GetNamedArgumentStrings ( "Flattening" ) ;
125+ foreach ( var name in flatteningNames )
126+ {
127+ flattening ??= new HashSet < string > ( StringComparer . Ordinal ) ;
128+ flattening . Add ( name ) ;
129+ }
130+
131+ return CreateInformation ( modelType , fromType , excluded , flattening ) ;
127132 }
128133
129134 internal static AutoPropertiesInformation CreateInformation (
@@ -132,32 +137,30 @@ internal static AutoPropertiesInformation CreateInformation(
132137 AttributeData autoPropertyAttribute )
133138 {
134139 var excluded = new HashSet < string > ( StringComparer . Ordinal ) ;
135-
136- // removido por hora
137- ////// obtém excluded do ctor ou propriedade Exclude
138- ////foreach (var name in autoPropertyAttribute.ConstructorArguments)
139- //// if (name.Kind == TypedConstantKind.Array && name.Values != null)
140- //// foreach (var v in name.Values)
141- //// if (v.Value is string s)
142- //// excluded.Add(s);
140+ var flattening = new HashSet < string > ( StringComparer . Ordinal ) ;
143141
144142 // obtém Exclude de NamedArguments
145143 foreach ( var namedArg in autoPropertyAttribute . NamedArguments )
146144 if ( namedArg . Key == "Exclude" && namedArg . Value . Kind == TypedConstantKind . Array && namedArg . Value . Values != null )
147145 foreach ( var v in namedArg . Value . Values )
148146 if ( v . Value is string s )
149147 excluded . Add ( s ) ;
148+ else if ( namedArg . Key == "Flattening" && namedArg . Value . Kind == TypedConstantKind . Array && namedArg . Value . Values != null )
149+ foreach ( var fv in namedArg . Value . Values )
150+ if ( fv . Value is string fs )
151+ flattening . Add ( fs ) ;
150152
151153 // gera o TypeDescriptor do fromType
152154 var fromTypeDescriptor = fromType . CreateTypeDescriptor ( ) ;
153155
154- return CreateInformation ( modelType , fromTypeDescriptor , excluded ) ;
156+ return CreateInformation ( modelType , fromTypeDescriptor , excluded , flattening ) ;
155157 }
156158
157159 internal static AutoPropertiesInformation CreateInformation (
158160 TypeDescriptor modelType ,
159161 TypeDescriptor fromType ,
160- HashSet < string > excluded )
162+ HashSet < string > excluded ,
163+ HashSet < string > ? flattening )
161164 {
162165 // declared properties in model type are always excluded
163166 foreach ( var p in modelType . CreateProperties ( p => p . SetMethod is not null ) )
@@ -167,23 +170,67 @@ internal static AutoPropertiesInformation CreateInformation(
167170
168171 // filtra propriedades do source,
169172 // remove propriedades que estão na lista de excluídas,
173+ // remove propriedades de flattening (serão recriadas depois)
170174 // removendo o que não for tipo primitivo, string, decimal, DateTime,
171175 // enum ou nullable desses tipos, além de coleções de tipos primitivos,
172176 // aceita structs também.
173- sourceProps = sourceProps
177+ var autoProps = sourceProps
174178 . Where ( p => ! excluded . Contains ( p . Name ) )
179+ . Where ( p => flattening is null || ! flattening . Contains ( p . Name ) )
175180 . Where ( IsSupportedType )
176- . ToArray ( ) ;
181+ . ToList ( ) ;
182+
183+ // processa flattening se houver
184+ if ( flattening is not null )
185+ foreach ( var fp in CreateFlattening ( fromType , sourceProps , flattening ) )
186+ if ( ! autoProps . Any ( p => p . Name == fp . Name ) )
187+ autoProps . Add ( fp ) ;
177188
178189 var generated = new List < PropertyDescriptor > ( ) ;
179- foreach ( var p in sourceProps )
190+ foreach ( var p in autoProps )
180191 {
181192 generated . Add ( new PropertyDescriptor ( p . Type , p . Name , p . Symbol ) ) ;
182193 }
183194
184195 return new AutoPropertiesInformation ( modelType , [ .. generated ] ) ;
185196 }
186197
198+ private static IReadOnlyList < PropertyDescriptor > CreateFlattening (
199+ TypeDescriptor fromType ,
200+ IReadOnlyList < PropertyDescriptor > sourceProps ,
201+ HashSet < string > flattening )
202+ {
203+ var list = new List < PropertyDescriptor > ( ) ;
204+ var matchTypeInfo = new MatchTypeInfo ( fromType , sourceProps , MatchOptions . Default ) ;
205+
206+ foreach ( var flattenPropName in flattening )
207+ {
208+ var flattenProp = new PropertyDescriptor ( TypeDescriptor . Void ( ) , flattenPropName , null ) ;
209+ var selection = PropertySelection . Select ( flattenProp , matchTypeInfo ) ;
210+
211+ if ( selection == null )
212+ continue ;
213+
214+ if ( ! selection . PropertyType . Type . HasNamedTypeSymbol ( out var namedType ) )
215+ continue ;
216+
217+ // se não for classe ou struct, não faz flattening
218+ if ( namedType . TypeKind != TypeKind . Class && namedType . TypeKind != TypeKind . Struct )
219+ continue ;
220+
221+ // obtém as propriedades do tipo
222+ var nestedProps = namedType . CreateTypeDescriptor ( ) . CreateProperties ( p => p . GetMethod is not null ) ;
223+ foreach ( var np in nestedProps . Where ( IsSupportedType ) )
224+ {
225+ // cria nova propriedade com o nome composto
226+ var newPropName = $ "{ flattenPropName } { np . Name } ";
227+ list . Add ( new PropertyDescriptor ( np . Type , newPropName , np . Symbol ) ) ;
228+ }
229+ }
230+
231+ return list ;
232+ }
233+
187234 private static readonly HashSet < string > SupportedPrimitiveTypes = new ( StringComparer . Ordinal )
188235 {
189236 "bool" ,
@@ -369,11 +416,3 @@ public IReadOnlyList<PropertyDescriptor> GetProperties(TypeDescriptor origin)
369416 return MatchOptions . GetOriginProperties ( origin ) ;
370417 }
371418}
372-
373- //internal class AutoPropertyTargetPropertiesRetriever : ITargetPropertiesRetriever
374- //{
375- // public IReadOnlyList<PropertyDescriptor> GetProperties(TypeDescriptor target)
376- // {
377- // throw new NotImplementedException();
378- // }
379- //}
0 commit comments