22using System . Collections . Generic ;
33using System . Linq ;
44using System . Reflection ;
5+ using System . Text ;
56
67namespace Octopus . Ocl . Converters
78{
89 public abstract class OclConverter : IOclConverter
910 {
11+ /// <summary>
12+ /// Returns true if the converter can be used for the provided type
13+ /// </summary>
14+ /// <param name="type">The model type being converted</param>
1015 public abstract bool CanConvert ( Type type ) ;
1116
1217 public virtual IEnumerable < IOclElement > ToElements ( OclConversionContext context , PropertyInfo ? propertyInfo , object obj )
@@ -17,6 +22,13 @@ public virtual IEnumerable<IOclElement> ToElements(OclConversionContext context,
1722 : Array . Empty < IOclElement > ( ) ;
1823 }
1924
25+ /// <summary>
26+ /// Converts the provided object to an OCL root document.
27+ /// </summary>
28+ /// <param name="context"></param>
29+ /// <param name="obj"></param>
30+ /// <returns></returns>
31+ /// <exception cref="NotSupportedException"></exception>
2032 public virtual OclDocument ToDocument ( OclConversionContext context , object obj )
2133 => throw new NotSupportedException ( "This type does not support conversion to the OCL root document" ) ;
2234
@@ -32,20 +44,25 @@ protected virtual string GetName(OclConversionContext context, PropertyInfo? pro
3244 protected virtual IEnumerable < IOclElement > GetElements ( object obj , IEnumerable < PropertyInfo > properties , OclConversionContext context )
3345 {
3446 var elements = from p in properties
35- from element in context . ToElements ( p , p . GetValue ( obj ) )
47+ from element in PropertyToElements ( obj , context , p )
3648 orderby
3749 element is OclBlock ,
3850 element . Name
3951 select element ;
4052 return elements ;
4153 }
4254
55+ protected virtual IEnumerable < IOclElement > PropertyToElements ( object obj , OclConversionContext context , PropertyInfo p )
56+ => context . ToElements ( p , p . GetValue ( obj ) ) ;
57+
58+
4359 protected virtual IReadOnlyList < IOclElement > SetProperties (
4460 OclConversionContext context ,
4561 IEnumerable < IOclElement > elements ,
4662 object target ,
4763 IReadOnlyList < PropertyInfo > properties )
4864 {
65+
4966 var notFound = new List < IOclElement > ( ) ;
5067 foreach ( var element in elements )
5168 {
@@ -68,52 +85,70 @@ protected virtual IReadOnlyList<IOclElement> SetProperties(
6885 if ( ! propertyToSet . CanWrite )
6986 throw new OclException ( $ "The property '{ propertyToSet . Name } ' on '{ target . GetType ( ) . Name } ' does not have a setter") ;
7087
71- propertyToSet . SetValue ( target , CoerceValue ( valueToSet , propertyToSet . PropertyType ) ) ;
88+ propertyToSet . SetValue ( target , CoerceValue ( context , valueToSet , propertyToSet . PropertyType ) ) ;
7289 }
7390 }
7491 }
7592
7693 return notFound ;
7794 }
7895
79- object ? CoerceValue ( object ? valueToSet , Type type )
96+ object ? CoerceValue ( OclConversionContext context , object ? sourceValue , Type targetType )
8097 {
81- if ( valueToSet is OclStringLiteral literal )
82- valueToSet = literal . Value ;
98+ if ( sourceValue is OclStringLiteral literal )
99+ sourceValue = literal . Value ;
100+
101+ if ( sourceValue is OclFunctionCall functionCall )
102+ {
103+ var result = context . GetFunctionCallFor ( functionCall . Name ) . ToValue ( functionCall ) ;
104+ return CoerceValue ( context , result , targetType ) ;
105+ }
83106
84- if ( valueToSet == null )
107+ if ( sourceValue == null )
85108 return null ;
86109
87- if ( type . IsInstanceOfType ( valueToSet ) )
88- return valueToSet ;
110+ if ( targetType . IsInstanceOfType ( sourceValue ) )
111+ return sourceValue ;
89112
90- if ( valueToSet is Dictionary < string , object ? > dict )
113+ if ( sourceValue is Dictionary < string , object ? > dict )
91114 {
92- if ( type . IsAssignableFrom ( typeof ( Dictionary < string , string > ) ) )
93- return dict . ToDictionary ( kvp => kvp . Key , kvp => ( string ? ) CoerceValue ( kvp . Value , typeof ( string ) ) ) ;
115+ if ( targetType . IsAssignableFrom ( typeof ( Dictionary < string , string > ) ) )
116+ return dict . ToDictionary ( kvp => kvp . Key , kvp => ( string ? ) CoerceValue ( context , kvp . Value , typeof ( string ) ) ) ;
94117
95- throw new OclException ( $ "Could not coerce dictionary to { type . Name } . Only Dictionary<string, string> and Dictionary<string, object?> are supported.") ;
118+ throw new OclException ( $ "Could not coerce dictionary to { targetType . Name } . Only Dictionary<string, string> and Dictionary<string, object?> are supported.") ;
96119 }
97120
98- if ( type == typeof ( string ) && valueToSet . GetType ( ) . IsPrimitive )
99- return valueToSet . ToString ( ) ;
121+ if ( targetType == typeof ( string ) )
122+ {
123+ if ( sourceValue . GetType ( ) . IsPrimitive )
124+ return sourceValue . ToString ( ) ;
100125
126+ if ( sourceValue is byte [ ] bytes )
127+ return Encoding . UTF8 . GetString ( bytes ) ;
128+ }
129+
101130 object ? FromArray < T > ( )
102131 {
103- if ( valueToSet is T [ ] array )
132+ if ( sourceValue is T [ ] array )
104133 {
105- if ( type == typeof ( List < T > ) )
134+ if ( targetType == typeof ( List < T > ) )
106135 return array . ToList ( ) ;
107- if ( type == typeof ( HashSet < T > ) )
136+ if ( targetType == typeof ( HashSet < T > ) )
108137 return array . ToHashSet ( ) ;
109138 }
110139
111140 return null ;
112141 }
113142
114- return FromArray < string > ( ) ?? FromArray < decimal > ( ) ?? FromArray < int > ( ) ?? throw new Exception ( $ "Could not coerce value of type { valueToSet . GetType ( ) . Name } to { type . Name } ") ;
143+ return FromArray < string > ( ) ?? FromArray < decimal > ( ) ?? FromArray < int > ( ) ?? throw new Exception ( $ "Could not coerce value of type { sourceValue . GetType ( ) . Name } to { targetType . Name } ") ;
115144 }
116145
146+ /// <summary>
147+ /// Get the properties for the given type.
148+ /// TODO: The virtual accessor can probably be removed and replaced with a ShouldSerialize method that seems to be used.
149+ /// </summary>
150+ /// <param name="type"></param>
151+ /// <returns></returns>
117152 protected virtual IEnumerable < PropertyInfo > GetProperties ( Type type )
118153 {
119154 var defaultProperties = type . GetDefaultMembers ( ) . OfType < PropertyInfo > ( ) ;
0 commit comments