@@ -39,65 +39,83 @@ public PropertySerializer(SerializationContext ctx) : base(ctx) { }
3939 var result = new StringBuilder ( ) ;
4040 foreach ( var v in prop . Values . Where ( value => value != null ) )
4141 {
42- // Get a serializer to serialize the property's value.
43- // If we can't serialize the property's value, the next step is worthless anyway.
44- var valueSerializer = sf . Build ( v . GetType ( ) , SerializationContext ) as IStringSerializer ;
45-
46- // Iterate through each value to be serialized,
47- // and give it a property (with parameters).
48- // FIXME: this isn't always the way this is accomplished.
49- // Multiple values can often be serialized within the
50- // same property. How should we fix this?
51-
52- // NOTE:
53- // We Serialize the property's value first, as during
54- // serialization it may modify our parameters.
55- // FIXME: the "parameter modification" operation should
56- // be separated from serialization. Perhaps something
57- // like PreSerialize(), etc.
58- var value = valueSerializer ? . SerializeToString ( v ) ;
59-
60- // Get the list of parameters we'll be serializing
61- var parameterList = prop . Parameters ;
62- if ( v is ICalendarDataType )
63- {
64- parameterList = ( ( ICalendarDataType ) v ) . Parameters ;
65- }
42+ SerializeValue ( result , prop , v , sf ) ;
43+ }
6644
67- // The TZID property of an RDATE/EXDATE collection is owned by the PeriodList that contains it.
68- // It is allowed to have multiple EXDATE or RDATE collections, each with a different TZID.
69- // Using RecurrenceDate and ExceptionDate classes ensures, that all Periods in the
70- // PeriodList have the same TZID and PeriodKind, which allows PeriodList be serialized in one go.
71- // Here, to determine the timezone, we can safely use the first Period's timezone.
72- if ( v is PeriodList periodList && periodList [ 0 ] . TzId != null && periodList [ 0 ] . TzId != "UTC" &&
73- parameterList . All ( p => string . Equals ( "TZID" , p . Value , StringComparison . OrdinalIgnoreCase ) ) )
74- {
75- parameterList . Set ( "TZID" , periodList [ 0 ] . TzId ) ;
76- }
45+ // Pop the object off the serialization context.
46+ SerializationContext . Pop ( ) ;
47+ return result . ToString ( ) ;
48+ }
49+
50+ private void SerializeValue ( StringBuilder result , ICalendarProperty prop , object value , ISerializerFactory sf )
51+ {
52+ // Get a serializer to serialize the property's value.
53+ // If we can't serialize the property's value, the next step is worthless anyway.
54+ var valueSerializer = sf . Build ( value . GetType ( ) , SerializationContext ) as IStringSerializer ;
55+
56+ // Iterate through each value to be serialized,
57+ // and give it a property (with parameters).
58+ // FIXME: this isn't always the way this is accomplished.
59+ // Multiple values can often be serialized within the
60+ // same property. How should we fix this?
61+
62+ // NOTE:
63+ // We Serialize the property's value first, as during
64+ // serialization it may modify our parameters.
65+ // FIXME: the "parameter modification" operation should
66+ // be separated from serialization. Perhaps something
67+ // like PreSerialize(), etc.
68+ var serializedValue = valueSerializer ? . SerializeToString ( value ) ;
69+
70+ // Get the list of parameters we'll be serializing
71+ var parameterList = GetParameterList ( prop , value ) ;
72+
73+ // The TZID property of an RDATE/EXDATE collection is owned by the PeriodList that contains it.
74+ // It is allowed to have multiple EXDATE or RDATE collections, each with a different TZID.
75+ // Using RecurrenceDate and ExceptionDate classes ensures, that all Periods in the
76+ // PeriodList have the same TZID and PeriodKind, which allows PeriodList be serialized in one go.
77+ // Here, to determine the timezone, we can safely use the first Period's timezone.
78+ if ( value is PeriodList periodList )
79+ {
80+ UpdateTzId ( parameterList , periodList ) ;
81+ }
7782
78- var sb = new StringBuilder ( ) ;
79- sb . Append ( prop . Name ) ;
80- if ( parameterList . Any ( ) )
83+ var sb = new StringBuilder ( ) ;
84+ sb . Append ( prop . Name ) ;
85+ if ( parameterList . Count != 0 )
86+ {
87+ // Get a serializer for parameters
88+ var parameterSerializer = sf . Build ( typeof ( CalendarParameter ) , SerializationContext ) as IStringSerializer ;
89+ if ( parameterSerializer != null )
8190 {
82- // Get a serializer for parameters
83- var parameterSerializer = sf . Build ( typeof ( CalendarParameter ) , SerializationContext ) as IStringSerializer ;
84- if ( parameterSerializer != null )
91+ sb . Append ( ';' ) ;
92+ var first = true ;
93+ // Serialize each parameter and append to the StringBuilder
94+ foreach ( var param in parameterList )
8595 {
86- // Serialize each parameter
87- // Separate parameters with semicolons
88- sb . Append ( ";" ) ;
89- sb . Append ( string . Join ( ";" , parameterList . Select ( param => parameterSerializer . SerializeToString ( param ) ) ) ) ;
96+ if ( ! first ) sb . Append ( ';' ) ;
97+
98+ sb . Append ( parameterSerializer . SerializeToString ( param ) ) ;
99+ first = false ;
90100 }
91101 }
92- sb . Append ( ":" ) ;
93- sb . Append ( value ) ;
94-
95- result . Append ( TextUtil . FoldLines ( sb . ToString ( ) ) ) ;
96102 }
103+ sb . Append ( ':' ) ;
104+ sb . Append ( serializedValue ) ;
97105
98- // Pop the object off the serialization context.
99- SerializationContext . Pop ( ) ;
100- return result . ToString ( ) ;
106+ result . FoldLines ( sb . ToString ( ) ) ;
107+ }
108+
109+ private static IParameterCollection GetParameterList ( ICalendarProperty prop , object value )
110+ => value is ICalendarDataType dataType ? dataType . Parameters : prop . Parameters ;
111+
112+ private static void UpdateTzId ( IParameterCollection parameterList , PeriodList periodList )
113+ {
114+ if ( periodList [ 0 ] . TzId != null && periodList [ 0 ] . TzId != "UTC" &&
115+ parameterList . All ( p => string . Equals ( "TZID" , p . Value , StringComparison . OrdinalIgnoreCase ) ) )
116+ {
117+ parameterList . Set ( "TZID" , periodList [ 0 ] . TzId ) ;
118+ }
101119 }
102120
103121 public override object ? Deserialize ( TextReader tr ) => null ;
0 commit comments