1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Reflection ;
5+ using Umbraco . Core . Models ;
6+
7+ namespace Our . Umbraco . Ditto
8+ {
9+ internal sealed class DittoTypeInfo
10+ {
11+ public static DittoTypeInfo Create < T > ( )
12+ {
13+ return Create ( typeof ( T ) ) ;
14+ }
15+
16+ public static DittoTypeInfo Create ( Type type )
17+ {
18+ var config = new DittoTypeInfo
19+ {
20+ TargetType = type
21+ } ;
22+
23+ // constructor
24+ //
25+ // Check the validity of the mapped type constructor as early as possible.
26+ var constructorParams = type . GetConstructorParameters ( ) ;
27+ if ( constructorParams != null )
28+ {
29+ // Is it a PublishedContent or similar?
30+ if ( constructorParams . Length == 1 && constructorParams [ 0 ] . ParameterType == typeof ( IPublishedContent ) )
31+ {
32+ config . ConstructorHasPublishedContentParameter = true ;
33+ }
34+
35+ if ( constructorParams . Length == 0 || config . ConstructorHasPublishedContentParameter )
36+ {
37+ config . ConstructorIsValid = true ;
38+ }
39+ }
40+
41+ // No valid constructor, but see if the value can be cast to the type
42+ if ( type . IsAssignableFrom ( typeof ( IPublishedContent ) ) )
43+ {
44+ config . IsOfTypePublishedContent = true ;
45+ config . ConstructorIsValid = true ;
46+ }
47+
48+ // attributes
49+ //
50+ config . CustomAttributes = type . GetCustomAttributes ( ) ;
51+
52+ // cacheable
53+ //
54+ var conversionHandlers = new List < DittoConversionHandler > ( ) ;
55+
56+ foreach ( var attr in config . CustomAttributes )
57+ {
58+ if ( attr is DittoCacheAttribute )
59+ {
60+ config . IsCacheable = true ;
61+ config . CacheInfo = ( DittoCacheAttribute ) attr ;
62+ }
63+
64+ // Check for class level DittoConversionHandlerAttribute
65+ if ( attr is DittoConversionHandlerAttribute )
66+ {
67+ conversionHandlers . Add ( ( ( DittoConversionHandlerAttribute ) attr ) . HandlerType . GetInstance < DittoConversionHandler > ( ) ) ;
68+ }
69+ }
70+
71+ // properties (lazy & eager)
72+ //
73+ var lazyProperties = new List < DittoTypePropertyInfo > ( ) ;
74+ var lazyPropertyNames = new List < string > ( ) ;
75+ var eagerProperties = new List < DittoTypePropertyInfo > ( ) ;
76+
77+ // Collect all the properties of the given type and loop through writable ones.
78+ foreach ( var property in type . GetProperties ( BindingFlags . Public | BindingFlags . Instance ) )
79+ {
80+ if ( property . CanWrite == false )
81+ continue ;
82+
83+ if ( property . GetSetMethod ( ) == null )
84+ continue ;
85+
86+ var attributes = new List < Attribute > ( property . GetCustomAttributes ( ) ) ;
87+
88+ if ( attributes . Any ( x => x is DittoIgnoreAttribute ) )
89+ continue ;
90+
91+ var propertyType = property . PropertyType ;
92+
93+ var propertyConfig = new DittoTypePropertyInfo
94+ {
95+ CustomAttributes = attributes ,
96+ PropertyInfo = property ,
97+ } ;
98+
99+
100+ // Check the property for any explicit processor attributes
101+ var processors = attributes . Where ( x => x is DittoProcessorAttribute ) . Cast < DittoProcessorAttribute > ( ) . ToList ( ) ;
102+ if ( processors . Count == 0 )
103+ {
104+ // Adds the default processor for this conversion
105+ var defaultProcessor = DittoProcessorRegistry . Instance . GetDefaultProcessorFor ( type ) ;
106+ // Forces the default processor to be the very first processor
107+ defaultProcessor . Order = - 1 ;
108+ processors . Add ( defaultProcessor ) ;
109+ }
110+
111+ // Check for registered processors on the property's type
112+ processors . AddRange ( propertyType . GetCustomAttributes < DittoProcessorAttribute > ( true ) ) ;
113+
114+ // Check any type arguments in generic enumerable types.
115+ // This should return false against typeof(string) etc also.
116+ if ( propertyType . IsCastableEnumerableType ( ) )
117+ {
118+ propertyConfig . IsEnumerable = true ;
119+ propertyConfig . EnumerableType = propertyType . GenericTypeArguments [ 0 ] ;
120+
121+ processors . AddRange ( propertyConfig . EnumerableType . GetCustomAttributes < DittoProcessorAttribute > ( true ) ) ;
122+ }
123+
124+ // Sort the order of the processors
125+ processors . Sort ( ( x , y ) => x . Order . CompareTo ( y . Order ) ) ;
126+
127+ // Check for globally registered processors
128+ processors . AddRange ( DittoProcessorRegistry . Instance . GetRegisteredProcessorAttributesFor ( propertyType ) ) ;
129+
130+ // Add any core processors onto the end
131+ processors . AddRange ( DittoProcessorRegistry . Instance . GetPostProcessorAttributes ( ) ) ;
132+
133+ propertyConfig . Processors = processors ;
134+
135+
136+ var propertyCache = attributes . Where ( x => x is DittoCacheAttribute ) . Cast < DittoCacheAttribute > ( ) . FirstOrDefault ( ) ;
137+ if ( propertyCache != null )
138+ {
139+ propertyConfig . IsCacheable = true ;
140+ propertyConfig . CacheInfo = propertyCache ;
141+ }
142+
143+ // detect if the property should be lazy-loaded
144+ if ( property . ShouldAttemptLazyLoad ( ) )
145+ {
146+ lazyProperties . Add ( propertyConfig ) ;
147+ lazyPropertyNames . Add ( property . Name ) ;
148+ }
149+ else
150+ {
151+ eagerProperties . Add ( propertyConfig ) ;
152+ }
153+ }
154+
155+ if ( lazyProperties . Count > 0 )
156+ {
157+ config . ConstructorRequiresProxyType = true ;
158+ config . HasLazyProperties = true ;
159+ config . LazyPropertyNames = lazyPropertyNames ; // lazyProperties.Select(x => x.PropertyInfo.Name);
160+ config . LazyProperties = lazyProperties ;
161+ }
162+
163+ if ( eagerProperties . Count > 0 )
164+ {
165+ config . HasEagerProperties = true ;
166+ config . EagerProperties = eagerProperties ;
167+ }
168+
169+
170+
171+ // conversion handlers
172+ //
173+
174+ // Check for globaly registered handlers
175+ foreach ( var handlerType in DittoConversionHandlerRegistry . Instance . GetRegisteredHandlerTypesFor ( type ) )
176+ {
177+ conversionHandlers . Add ( handlerType . GetInstance < DittoConversionHandler > ( ) ) ;
178+ }
179+
180+ config . ConversionHandlers = conversionHandlers ;
181+
182+ var before = new List < MethodInfo > ( ) ;
183+ var after = new List < MethodInfo > ( ) ;
184+
185+ // Check for method level DittoOnConvert[ing|ed]Attribute
186+ foreach ( var method in type . GetMethods ( BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) )
187+ {
188+ var ing = method . GetCustomAttribute < DittoOnConvertingAttribute > ( ) ;
189+ var ed = method . GetCustomAttribute < DittoOnConvertedAttribute > ( ) ;
190+
191+ if ( ing == null && ed == null )
192+ continue ;
193+
194+ var p = method . GetParameters ( ) ;
195+ if ( p . Length == 1 && p [ 0 ] . ParameterType == typeof ( DittoConversionHandlerContext ) )
196+ {
197+ if ( ing != null )
198+ before . Add ( method ) ;
199+
200+ if ( ed != null )
201+ after . Add ( method ) ;
202+ }
203+ }
204+
205+ config . ConvertingMethods = before ;
206+ config . ConvertedMethods = after ;
207+
208+
209+ return config ;
210+ }
211+
212+ public Type TargetType { get ; set ; }
213+
214+ public bool IsCacheable { get ; set ; }
215+ public DittoCacheAttribute CacheInfo { get ; set ; }
216+
217+ public bool ConstructorIsValid { get ; set ; }
218+ public bool ConstructorHasPublishedContentParameter { get ; set ; }
219+ public bool ConstructorRequiresProxyType { get ; set ; }
220+
221+ public bool IsOfTypePublishedContent { get ; set ; }
222+
223+ public IEnumerable < Attribute > CustomAttributes { get ; set ; }
224+
225+ public bool HasLazyProperties { get ; set ; }
226+ public IEnumerable < DittoTypePropertyInfo > LazyProperties { get ; set ; }
227+ public IEnumerable < string > LazyPropertyNames { get ; set ; }
228+
229+ public bool HasEagerProperties { get ; set ; }
230+ public IEnumerable < DittoTypePropertyInfo > EagerProperties { get ; set ; }
231+
232+ public IEnumerable < DittoConversionHandler > ConversionHandlers { get ; set ; }
233+ public IEnumerable < MethodInfo > ConvertingMethods { get ; set ; }
234+ public IEnumerable < MethodInfo > ConvertedMethods { get ; set ; }
235+
236+ internal sealed class DittoTypePropertyInfo
237+ {
238+ public bool IsCacheable { get ; set ; }
239+ public DittoCacheAttribute CacheInfo { get ; set ; }
240+
241+ public IEnumerable < Attribute > CustomAttributes { get ; set ; }
242+
243+ public IEnumerable < DittoProcessorAttribute > Processors { get ; set ; }
244+ public PropertyInfo PropertyInfo { get ; set ; }
245+
246+ public bool IsEnumerable { get ; set ; }
247+ public Type EnumerableType { get ; set ; }
248+ }
249+ }
250+ }
0 commit comments