2 // System.Xml.Serialization.XmlSerializationWriter.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // Copyright (C) Tim Coleman, 2002
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Globalization;
37 using System.Xml.Schema;
38 using System.Runtime.Serialization;
39 using System.Reflection;
41 namespace System.Xml.Serialization
43 public abstract class XmlSerializationWriter
45 : XmlSerializationGeneratedCode
51 ObjectIDGenerator idGenerator;
53 bool topLevelElement = false;
57 Queue referencedElements;
59 Hashtable serializedObjects;
60 const string xmlNamespace = "http://www.w3.org/2000/xmlns/";
61 const string unexpectedTypeError = "The type {0} was not expected. Use the" +
62 " XmlInclude or SoapInclude attribute to specify types that are not known statically.";
68 protected XmlSerializationWriter ()
71 serializedObjects = new Hashtable ();
74 internal void Initialize (XmlWriter writer, XmlSerializerNamespaces nss)
79 namespaces = new ArrayList ();
80 foreach (XmlQualifiedName ns in nss.ToArray())
81 if (ns.Name != "" && ns.Namespace != "")
86 #endregion // Constructors
90 protected ArrayList Namespaces {
91 get { return namespaces; }
92 set { namespaces = value; }
95 protected XmlWriter Writer {
96 get { return writer; }
97 set { writer = value; }
100 #endregion // Properties
104 protected void AddWriteCallback (Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)
106 WriteCallbackInfo info = new WriteCallbackInfo ();
108 info.TypeName = typeName;
109 info.TypeNs = typeNs;
110 info.Callback = callback;
112 if (callbacks == null) callbacks = new Hashtable ();
113 callbacks.Add (type, info);
116 protected Exception CreateChoiceIdentifierValueException (string value, string identifier, string name, string ns)
118 string message = string.Format ("Value '{0}' of the choice"
119 + " identifier '{1}' does not match element '{2}'"
120 + " from namespace '{3}'.", value, identifier,
122 return new InvalidOperationException (message);
125 protected Exception CreateInvalidChoiceIdentifierValueException (string type, string identifier)
127 string message = string.Format ("Invalid or missing choice"
128 + " identifier '{0}' of type '{1}'.", identifier,
130 return new InvalidOperationException (message);
133 protected Exception CreateMismatchChoiceException (string value, string elementName, string enumValue)
135 string message = String.Format ("Value of {0} mismatches the type of {1}, you need to set it to {2}.", elementName, value, enumValue);
136 return new InvalidOperationException (message);
139 protected Exception CreateUnknownAnyElementException (string name, string ns)
141 string message = String.Format ("The XML element named '{0}' from namespace '{1}' was not expected. The XML element name and namespace must match those provided via XmlAnyElementAttribute(s).", name, ns);
142 return new InvalidOperationException (message);
145 protected Exception CreateUnknownTypeException (object o)
147 return CreateUnknownTypeException (o.GetType ());
150 protected Exception CreateUnknownTypeException (Type type)
152 string message = String.Format ("The type {0} may not be used in this context.", type);
153 return new InvalidOperationException (message);
156 protected static byte[] FromByteArrayBase64 (byte[] value)
161 protected static string FromByteArrayHex (byte[] value)
163 return XmlCustomFormatter.FromByteArrayHex (value);
166 protected static string FromChar (char value)
168 return XmlCustomFormatter.FromChar (value);
171 protected static string FromDate (DateTime value)
173 return XmlCustomFormatter.FromDate (value);
176 protected static string FromDateTime (DateTime value)
178 return XmlCustomFormatter.FromDateTime (value);
181 protected static string FromEnum (long value, string[] values, long[] ids)
183 return XmlCustomFormatter.FromEnum (value, values, ids);
186 protected static string FromTime (DateTime value)
188 return XmlCustomFormatter.FromTime (value);
191 protected static string FromXmlName (string name)
193 return XmlCustomFormatter.FromXmlName (name);
196 protected static string FromXmlNCName (string ncName)
198 return XmlCustomFormatter.FromXmlNCName (ncName);
201 protected static string FromXmlNmToken (string nmToken)
203 return XmlCustomFormatter.FromXmlNmToken (nmToken);
206 protected static string FromXmlNmTokens (string nmTokens)
208 return XmlCustomFormatter.FromXmlNmTokens (nmTokens);
211 protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName)
213 if (xmlQualifiedName == null || xmlQualifiedName == XmlQualifiedName.Empty)
216 return GetQualifiedName (xmlQualifiedName.Name, xmlQualifiedName.Namespace);
219 private string GetId (object o, bool addToReferencesList)
221 if (idGenerator == null) idGenerator = new ObjectIDGenerator ();
224 long lid = idGenerator.GetId (o, out firstTime);
225 return String.Format (CultureInfo.InvariantCulture, "id{0}", lid);
229 bool AlreadyQueued (object ob)
231 if (idGenerator == null) return false;
234 idGenerator.HasId (ob, out firstTime);
238 private string GetNamespacePrefix (string ns)
240 string prefix = Writer.LookupPrefix (ns);
243 prefix = String.Format (CultureInfo.InvariantCulture, "q{0}", ++qnameCount);
244 WriteAttribute ("xmlns", prefix, null, ns);
249 private string GetQualifiedName (string name, string ns)
251 if (ns == String.Empty) return name;
253 string prefix = GetNamespacePrefix (ns);
254 if (prefix == String.Empty)
257 return String.Format ("{0}:{1}", prefix, name);
260 protected abstract void InitCallbacks ();
262 protected void TopLevelElement ()
264 topLevelElement = true;
267 protected void WriteAttribute (string localName, byte[] value)
269 WriteAttribute (localName, String.Empty, value);
272 protected void WriteAttribute (string localName, string value)
274 WriteAttribute (String.Empty, localName, String.Empty, value);
277 protected void WriteAttribute (string localName, string ns, byte[] value)
282 Writer.WriteStartAttribute (localName, ns);
284 Writer.WriteEndAttribute ();
287 protected void WriteAttribute (string localName, string ns, string value)
289 WriteAttribute (null, localName, ns, value);
292 protected void WriteAttribute (string prefix, string localName, string ns, string value)
297 Writer.WriteAttributeString (prefix, localName, ns, value);
300 protected void WriteElementEncoded (XmlNode node, string name, string ns, bool isNullable, bool any)
302 if (name != string.Empty)
307 WriteNullTagEncoded (name, ns);
311 Writer.WriteStartElement (name, ns);
312 node.WriteTo (Writer);
313 Writer.WriteEndElement ();
317 node.WriteTo (Writer);
320 protected void WriteElementLiteral (XmlNode node, string name, string ns, bool isNullable, bool any)
322 if (name != string.Empty)
327 WriteNullTagLiteral (name, ns);
331 Writer.WriteStartElement (name, ns);
332 node.WriteTo (Writer);
333 Writer.WriteEndElement ();
337 node.WriteTo (Writer);
340 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value)
342 WriteElementQualifiedName (localName, String.Empty, value, null);
345 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value)
347 WriteElementQualifiedName (localName, ns, value, null);
350 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value, XmlQualifiedName xsiType)
352 WriteElementQualifiedName (localName, String.Empty, value, xsiType);
355 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
357 localName = XmlCustomFormatter.FromXmlNCName (localName);
358 WriteStartElement (localName, ns);
359 if (xsiType != null) WriteXsiType (xsiType.Name, xsiType.Namespace);
360 Writer.WriteString (FromXmlQualifiedName (value));
364 protected void WriteElementString (string localName, string value)
366 WriteElementString (localName, String.Empty, value, null);
369 protected void WriteElementString (string localName, string ns, string value)
371 WriteElementString (localName, ns, value, null);
374 protected void WriteElementString (string localName, string value, XmlQualifiedName xsiType)
376 WriteElementString (localName, String.Empty, value, xsiType);
379 protected void WriteElementString (string localName, string ns, string value, XmlQualifiedName xsiType)
381 if (value == null) return;
383 if (xsiType != null) {
384 localName = XmlCustomFormatter.FromXmlNCName (localName);
385 WriteStartElement (localName, ns);
386 WriteXsiType (xsiType.Name, xsiType.Namespace);
387 Writer.WriteString (value);
391 Writer.WriteElementString (localName, ns, value);
394 protected void WriteElementStringRaw (string localName, byte[] value)
396 WriteElementStringRaw (localName, String.Empty, value, null);
399 protected void WriteElementStringRaw (string localName, string value)
401 WriteElementStringRaw (localName, String.Empty, value, null);
404 protected void WriteElementStringRaw (string localName, byte[] value, XmlQualifiedName xsiType)
406 WriteElementStringRaw (localName, String.Empty, value, xsiType);
409 protected void WriteElementStringRaw (string localName, string ns, byte[] value)
411 WriteElementStringRaw (localName, ns, value, null);
414 protected void WriteElementStringRaw (string localName, string ns, string value)
416 WriteElementStringRaw (localName, ns, value, null);
419 protected void WriteElementStringRaw (string localName, string value, XmlQualifiedName xsiType)
421 WriteElementStringRaw (localName, String.Empty, value, null);
424 protected void WriteElementStringRaw (string localName, string ns, byte[] value, XmlQualifiedName xsiType)
429 WriteStartElement (localName, ns);
432 WriteXsiType (xsiType.Name, xsiType.Namespace);
434 if (value.Length > 0)
435 Writer.WriteBase64(value,0,value.Length);
439 protected void WriteElementStringRaw (string localName, string ns, string value, XmlQualifiedName xsiType)
441 localName = XmlCustomFormatter.FromXmlNCName (localName);
442 WriteStartElement (localName, ns);
445 WriteXsiType (xsiType.Name, xsiType.Namespace);
447 Writer.WriteRaw (value);
451 protected void WriteEmptyTag (string name)
453 WriteEmptyTag (name, String.Empty);
456 protected void WriteEmptyTag (string name, string ns)
458 name = XmlCustomFormatter.FromXmlName (name);
459 WriteStartElement (name, ns);
463 protected void WriteEndElement ()
465 WriteEndElement (null);
468 protected void WriteEndElement (object o)
471 serializedObjects.Remove (o);
473 Writer.WriteEndElement ();
476 protected void WriteId (object o)
478 WriteAttribute ("id", GetId (o, true));
481 protected void WriteNamespaceDeclarations (XmlSerializerNamespaces ns)
486 ICollection namespaces = ns.Namespaces.Values;
487 foreach (XmlQualifiedName qn in namespaces) {
488 if (qn.Namespace != String.Empty && Writer.LookupPrefix (qn.Namespace) != qn.Name)
489 WriteAttribute ("xmlns", qn.Name, xmlNamespace, qn.Namespace);
493 protected void WriteNullableQualifiedNameEncoded (string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
496 WriteElementQualifiedName (name, ns, value, xsiType);
498 WriteNullTagEncoded (name, ns);
501 protected void WriteNullableQualifiedNameLiteral (string name, string ns, XmlQualifiedName value)
504 WriteElementQualifiedName (name, ns, value);
506 WriteNullTagLiteral (name, ns);
509 protected void WriteNullableStringEncoded (string name, string ns, string value, XmlQualifiedName xsiType)
512 WriteElementString (name, ns, value, xsiType);
514 WriteNullTagEncoded (name, ns);
517 protected void WriteNullableStringEncodedRaw (string name, string ns, byte[] value, XmlQualifiedName xsiType)
520 WriteNullTagEncoded (name, ns);
522 WriteElementStringRaw (name, ns, value, xsiType);
525 protected void WriteNullableStringEncodedRaw (string name, string ns, string value, XmlQualifiedName xsiType)
528 WriteNullTagEncoded (name, ns);
530 WriteElementStringRaw (name, ns, value, xsiType);
533 protected void WriteNullableStringLiteral (string name, string ns, string value)
536 WriteElementString (name, ns, value, null);
538 WriteNullTagLiteral (name, ns);
541 protected void WriteNullableStringLiteralRaw (string name, string ns, byte[] value)
544 WriteNullTagLiteral (name, ns);
546 WriteElementStringRaw (name, ns, value);
549 protected void WriteNullableStringLiteralRaw (string name, string ns, string value)
552 WriteNullTagLiteral (name, ns);
554 WriteElementStringRaw (name, ns, value);
557 protected void WriteNullTagEncoded (string name)
559 WriteNullTagEncoded (name, String.Empty);
562 protected void WriteNullTagEncoded (string name, string ns)
564 Writer.WriteStartElement (name, ns);
566 Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
568 Writer.WriteAttributeString ("null", XmlSchema.InstanceNamespace, "1");
570 Writer.WriteEndElement ();
573 protected void WriteNullTagLiteral (string name)
575 WriteNullTagLiteral (name, String.Empty);
578 protected void WriteNullTagLiteral (string name, string ns)
580 WriteStartElement (name, ns);
581 Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
585 protected void WritePotentiallyReferencingElement (string n, string ns, object o)
587 WritePotentiallyReferencingElement (n, ns, o, null, false, false);
590 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType)
592 WritePotentiallyReferencingElement (n, ns, o, ambientType, false, false);
595 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference)
597 WritePotentiallyReferencingElement (n, ns, o, ambientType, suppressReference, false);
600 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable)
604 if (isNullable) WriteNullTagEncoded (n, ns);
608 WriteStartElement (n, ns, true);
610 CheckReferenceQueue ();
612 if (callbacks.ContainsKey (o.GetType ()))
614 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
615 if (o.GetType ().IsEnum) {
618 else if (suppressReference) {
619 Writer.WriteAttributeString ("id", GetId (o, false));
620 if (ambientType != o.GetType ()) WriteXsiType(info.TypeName, info.TypeNs);
624 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
625 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
630 // Must be a primitive type or array of primitives
631 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
632 if (td.SchemaType == SchemaTypes.Primitive) {
633 WriteXsiType (td.XmlType, XmlSchema.Namespace);
634 Writer.WriteString (XmlCustomFormatter.ToXmlString (td, o));
635 } else if (IsPrimitiveArray (td)) {
636 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
637 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
639 throw new InvalidOperationException ("Invalid type: " + o.GetType().FullName);
645 protected void WriteReferencedElements ()
647 if (referencedElements == null) return;
648 if (callbacks == null) return;
650 while (referencedElements.Count > 0)
652 object o = referencedElements.Dequeue ();
653 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
654 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
657 WriteStartElement (info.TypeName, info.TypeNs, true);
658 Writer.WriteAttributeString ("id", GetId (o, false));
660 if (td.SchemaType != SchemaTypes.Array) // Array use its own "arrayType" attribute
661 WriteXsiType(info.TypeName, info.TypeNs);
665 } else if (IsPrimitiveArray (td)) {
671 bool IsPrimitiveArray (TypeData td)
673 if (td.SchemaType == SchemaTypes.Array) {
674 if (td.ListItemTypeData.SchemaType == SchemaTypes.Primitive || td.ListItemType == typeof(object))
676 return IsPrimitiveArray (td.ListItemTypeData);
681 void WriteArray (object o, TypeData td)
683 TypeData itemTypeData = td;
688 itemTypeData = itemTypeData.ListItemTypeData;
689 xmlType = itemTypeData.XmlType;
692 while (itemTypeData.SchemaType == SchemaTypes.Array );
697 WriteStartElement("Array", XmlSerializer.EncodingNamespace, true);
698 Writer.WriteAttributeString("id", GetId(o, false));
699 if (td.SchemaType == SchemaTypes.Array) {
702 Writer.WriteAttributeString("arrayType", XmlSerializer.EncodingNamespace, GetQualifiedName(xmlType, XmlSchema.Namespace) + "[" + len.ToString() + "]");
703 for (int i = 0; i < len; i++) {
704 WritePotentiallyReferencingElement("Item", "", a.GetValue(i), td.ListItemType, false, true);
711 protected void WriteReferencingElement (string n, string ns, object o)
713 WriteReferencingElement (n, ns, o, false);
716 protected void WriteReferencingElement (string n, string ns, object o, bool isNullable)
720 if (isNullable) WriteNullTagEncoded (n, ns);
725 CheckReferenceQueue ();
726 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
728 Writer.WriteStartElement (n, ns);
729 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
730 Writer.WriteEndElement ();
734 void CheckReferenceQueue ()
736 if (referencedElements == null)
738 referencedElements = new Queue ();
744 protected void WriteRpcResult (string name, string ns)
746 throw new NotImplementedException ();
749 protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable)
751 if (serializable == null)
753 if (isNullable) WriteNullTagLiteral (name, ns);
758 Writer.WriteStartElement (name, ns);
759 serializable.WriteXml (Writer);
760 Writer.WriteEndElement ();
764 protected void WriteStartDocument ()
766 if (Writer.WriteState == WriteState.Start)
767 Writer.WriteStartDocument ();
770 protected void WriteStartElement (string name)
772 WriteStartElement (name, String.Empty, null, false);
775 protected void WriteStartElement (string name, string ns)
777 WriteStartElement (name, ns, null, false);
780 protected void WriteStartElement (string name, string ns, bool writePrefixed)
782 WriteStartElement (name, ns, null, writePrefixed);
785 protected void WriteStartElement (string name, string ns, object o)
787 WriteStartElement (name, ns, o, false);
790 protected void WriteStartElement (string name, string ns, object o, bool writePrefixed)
794 if (serializedObjects.Contains (o))
795 throw new InvalidOperationException ("A circular reference was detected while serializing an object of type " + o.GetType().Name);
797 serializedObjects [o] = o;
800 string prefix = null;
802 if (topLevelElement && ns != null && ns.Length != 0)
804 foreach (XmlQualifiedName qn in namespaces)
805 if (qn.Namespace == ns) {
807 writePrefixed = true;
812 if (writePrefixed && ns != string.Empty)
814 name = XmlCustomFormatter.FromXmlName (name);
817 prefix = Writer.LookupPrefix (ns);
818 if (prefix == null || prefix.Length == 0)
819 prefix = "q" + (++qnameCount);
820 Writer.WriteStartElement (prefix, name, ns);
822 Writer.WriteStartElement (name, ns);
826 if (namespaces != null) {
827 foreach (XmlQualifiedName qn in namespaces)
829 string currentPrefix = Writer.LookupPrefix (qn.Namespace);
830 if (currentPrefix != null && currentPrefix.Length != 0) continue;
832 WriteAttribute ("xmlns",qn.Name,xmlNamespace,qn.Namespace);
835 topLevelElement = false;
839 protected void WriteTypedPrimitive (string name, string ns, object o, bool xsiType)
842 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
844 name = XmlCustomFormatter.FromXmlName (name);
845 Writer.WriteStartElement (name, ns);
847 if (o is XmlNode[]) {
848 foreach (XmlNode node in (XmlNode[])o)
849 node.WriteTo (Writer);
852 if (o is XmlQualifiedName)
853 value = FromXmlQualifiedName ((XmlQualifiedName) o);
855 value = XmlCustomFormatter.ToXmlString (td, o);
859 if (td.SchemaType != SchemaTypes.Primitive)
860 throw new InvalidOperationException (string.Format (unexpectedTypeError, o.GetType().FullName));
861 WriteXsiType (td.XmlType, XmlSchema.Namespace);
866 Writer.WriteEndElement ();
869 protected void WriteValue (byte[] value)
871 Writer.WriteBase64 (value, 0, value.Length);
874 protected void WriteValue (string value)
877 Writer.WriteString (value);
880 protected void WriteXmlAttribute (XmlNode node)
882 WriteXmlAttribute (node, null);
885 protected void WriteXmlAttribute (XmlNode node, object container)
887 XmlAttribute attr = node as XmlAttribute;
889 throw new InvalidOperationException ("The node must be either type XmlAttribute or a derived type.");
891 if (attr.NamespaceURI == XmlSerializer.WsdlNamespace)
893 // The wsdl arrayType attribute needs special handling
894 if (attr.LocalName == "arrayType") {
895 string ns, type, dimensions;
896 TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
897 string value = GetQualifiedName (type + dimensions, ns);
898 WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, value);
903 WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value);
906 protected void WriteXsiType (string name, string ns)
908 if (ns != null && ns != string.Empty)
909 WriteAttribute ("type", XmlSchema.InstanceNamespace, GetQualifiedName (name, ns));
911 WriteAttribute ("type", XmlSchema.InstanceNamespace, name);
917 protected Exception CreateInvalidAnyTypeException (object o)
919 throw new NotImplementedException ();
923 protected Exception CreateInvalidAnyTypeException (Type t)
925 throw new NotImplementedException ();
928 protected Exception CreateInvalidEnumValueException (object value, string typeName)
930 return new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
931 "'{0}' is not a valid value for {1}.", value, typeName));
934 protected static string FromEnum (long value, string[] values, long[] ids, string typeName)
936 return XmlCustomFormatter.FromEnum (value, values, ids, typeName);
940 protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName, bool ignoreEmpty)
942 throw new NotImplementedException ();
946 protected static Assembly ResolveDynamicAssembly (string assemblyFullName)
948 throw new NotImplementedException ();
952 protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable, bool any)
954 throw new NotImplementedException ();
958 protected bool EscapeName
960 get { throw new NotImplementedException(); }
961 set { throw new NotImplementedException(); }
967 class WriteCallbackInfo
970 public string TypeName;
971 public string TypeNs;
972 public XmlSerializationWriteCallback Callback;