public static void CopyTo(DataRow ori, object des, Config.AutoMappingConfig config) { if (des == null) { throw new Exception("El objeto destino a copiar no puede ser null. Utilice 'Get' de no tener el objeto instanciado"); } config = config ?? new Config.DefaultAutomappingConfig(); var tDes = des.GetType(); // Tiene Automapping var props = (from ps in tDes.GetProperties() //Busco atributos en la clase de destino de copia from at in ps.CustomAttributes where typeof(IMappingPropertyAttribute).IsAssignableFrom(at.AttributeType) select new { Type = at.AttributeType, ConfigProp = ps, Attr = (IMappingPropertyAttribute)at.Constructor.Invoke(at.ConstructorArguments.Select(x => x.Value).ToArray()) }); // Copio los datos foreach (var x in props) { x.Attr.Map(ori, tDes, des, x.ConfigProp, config); } }
public void Map(DataRow ori, Type tDes, object oDes, PropertyInfo pDes, Config.AutoMappingConfig config) { if (!ExistsProperty(ori, this.RemoteName ?? pDes.Name, config)) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct][DataRow] No se encuentra la columna '{0}' en el DataRow para la clase", this.RemoteName ?? pDes.Name, tDes.FullName)); } else { return; } } var value = GetPropertyValue(ori, this.RemoteName ?? pDes.Name, config); try { if (pDes.UseComplexConvert()) { pDes.SetValue(oDes, Convert.To(pDes.PropertyType, value)); } else { pDes.SetValue(oDes, value); } } catch (Exception ex) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct][DataRow] Error al copiar un dato '{0}' a {1}.{2}", value, tDes.FullName, pDes.Name), ex); } } }
/// <summary> /// Indica si existe una propiedad en un objeto /// </summary> protected bool ExistsProperty(object obj, string propertyName, Config.AutoMappingConfig config) { if (obj == null) { return(false); } var tObj = obj.GetType(); if (typeof(XmlNode).IsAssignableFrom(tObj)) { return(((XmlNode)obj).Attributes.OfType <XmlAttribute>().Any(p => p.Name == propertyName)); } else if (typeof(Dictionary <string, object>).IsAssignableFrom(tObj)) { return(((Dictionary <string, object>)obj).Keys.Any(p => p == propertyName)); } else if (typeof(DataRow).IsAssignableFrom(tObj)) { if (((DataRow)obj).Table == null || ((DataRow)obj).Table.Columns == null) { return(false); } return(((DataRow)obj).Table.Columns.OfType <DataColumn>().Any(p => p.ColumnName == propertyName)); } else { return(tObj.GetProperties().Any(p => p.Name == propertyName)); } }
/// <summary> /// Devuelve una clase con a estructura del nodo /// </summary> /// <typeparam name="T"></typeparam> /// <param name="nodo"></param> /// <returns></returns> public T Get <T>(XmlNode nodo, Mapping.Config.AutoMappingConfig config) where T : class, new() { if (nodo == default(XmlNode)) { return(null); } var r = YerbaSoft.DTO.Mapping.Map.Get <T>(nodo, config); return(r); }
/// <summary> /// Devuelve los SubNodos de un nodo Tipeado /// </summary> /// <typeparam name="T"></typeparam> /// <param name="nodo"></param> /// <returns></returns> public T[] GetSubNodes <T>(XmlNode nodo, Mapping.Config.AutoMappingConfig config) where T : class, new() { var r = new List <T>(); foreach (var n in GetSubNodes(nodo)) { r.Add(Get <T>(n, config)); } return(r.ToArray()); }
public static void CopyTo(object ori, object des, Config.AutoMappingConfig config) { if (des == null) { throw new Exception("El objeto destino a copiar no puede ser null. Utilice 'Get' de no tener el objeto instanciado"); } if (ori == null) { return; //no copio nada } config = config ?? new Config.DefaultAutomappingConfig(); var tOri = ori.GetType(); var tDes = des.GetType(); Type tConfig = null; //ori o des que contiene la configuración de AutoMapping (Ori por default) if (tOri.CustomAttributes.Any(at => at.AttributeType == typeof(AutoMapping))) { tConfig = tOri; } else if (tDes.CustomAttributes.Any(at => at.AttributeType == typeof(AutoMapping))) { tConfig = tDes; } else { throw new Exception("Ni el objeto de origen ni el de destino poseen el atributo de clase 'AutoMapping'"); } var props = (from ps in tConfig.GetProperties() //Busco atributos en la clase de destino de copia from at in ps.CustomAttributes where typeof(IMappingPropertyAttribute).IsAssignableFrom(at.AttributeType) select new { Type = at.AttributeType, ConfigProp = ps, Attr = (IMappingPropertyAttribute)at.Constructor.Invoke(at.ConstructorArguments.Select(x => x.Value).ToArray()) }); // Copio los datos foreach (var x in props) { try { x.Attr.Map(tConfig, tOri, ori, tDes, des, x.ConfigProp, config); } catch (Exception eee) { throw eee; } } }
public static T Get <T>(DataRow ori, object extraInfo, Config.AutoMappingConfig config) where T : new() { if (ori == null) { return(default(T)); } config = config ?? new Config.DefaultAutomappingConfig(); object des = new T(); CopyTo(ori, des, config); if (config.UseExtraMapping && typeof(Mapping.IExtraMapping).IsAssignableFrom(typeof(T))) { ((Mapping.IExtraMapping)des).ExtraMappingWhenGet(ori, extraInfo); } return((T)des); }
/// <summary> /// devuelve el valor de una propiedad /// </summary> /// <param name="obj"></param> /// <param name="propertyName"></param> /// <param name="config"></param> /// <returns></returns> protected object GetPropertyValue(object obj, string propertyName, Config.AutoMappingConfig config) { if (obj == null) { return(false); } var tObj = obj.GetType(); if (typeof(XmlNode).IsAssignableFrom(tObj)) { var v = ((XmlNode)obj).Attributes.OfType <XmlAttribute>().SingleOrDefault(p => p.Name == propertyName); return(v == null ? null : v.Value); } else if (typeof(Dictionary <string, object>).IsAssignableFrom(tObj)) { var v = ((Dictionary <string, object>)obj).Keys.SingleOrDefault(p => p == propertyName); return(v == null ? null : ((Dictionary <string, object>)obj)[v]); } else if (typeof(DataRow).IsAssignableFrom(tObj)) { if (((DataRow)obj).Table == null || ((DataRow)obj).Table.Columns == null) { return(false); } var c = ((DataRow)obj).Table.Columns.OfType <DataColumn>().SingleOrDefault(p => p.ColumnName == propertyName); return(((DataRow)obj)[c]); } else { var pr = tObj.GetProperties().SingleOrDefault(p => p.Name == propertyName); if (pr == null) { return(null); } return(pr.GetValue(obj)); } }
public void Map(XmlNode ori, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { var tpDes = pDes.PropertyType; // Tipos Soportados bool isIList = false; bool isICollection = false; if (tpDes.GenericTypeArguments.Length > 0) { var tList = typeof(IList <>).MakeGenericType(tpDes.GenericTypeArguments); isIList = tList.IsAssignableFrom(tpDes); var tCollection = typeof(ICollection <>).MakeGenericType(tpDes.GenericTypeArguments); isICollection = tCollection.IsAssignableFrom(tpDes); } if (!isIList && !isICollection) { throw new ApplicationException(string.Format("AutoMapping[XmlNode] La propiedad de destino debe ser de tipo IList o ICollection {1}.{0}", RemoteName ?? pDes.Name, tDes.FullName)); } dynamic vDes = (dynamic)Activator.CreateInstance(tpDes); var vOri = ori.ChildNodes.OfType <XmlNode>().Where(p => p.Name == (RemoteName ?? pDes.Name)); if (vOri.Count() == 0 && this.MustExists) { throw new ApplicationException(string.Format("AutoMapping[XmlNode] No se encuentra el valor {0} en la lista de valores para mapear {1}", RemoteName ?? pDes.Name, tDes.FullName)); } int i = 0; foreach (var oItem in vOri) { var ctpDes = tpDes.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[XmlNode] El sub-tipo de la lista de la propiedad de destino '{0}' de la clase '{1}' no tiene un constructor accesible sin parámetros", RemoteName ?? pDes.Name, tDes.FullName)); } var dItem = Activator.CreateInstance(tpDes.GenericTypeArguments[0]); Mapping.Map.CopyTo(oItem, dItem, config); if (config.UseExtraMapping && typeof(Mapping.IExtraMapping).IsAssignableFrom(tpDes.GenericTypeArguments[0])) { ((Mapping.IExtraMapping)dItem).ExtraMappingWhenGet(oItem, null); } if (isIList) { var mDes = tpDes.GetMethod("Insert", new Type[] { typeof(int), tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { i++, dItem }); } else if (isICollection) { var mDes = tpDes.GetMethod("Add", new Type[] { tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { dItem }); } } pDes.SetValue(oDes, vDes); }
public void Map(Dictionary <string, object> values, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { if (!values.Keys.Contains(RemoteName ?? pDes.Name)) { throw new ApplicationException(string.Format("AutoMapping[SubClass][Dictionary] La lista de valores no contiene la clave {0}", RemoteName ?? pDes.Name)); } var vOri = values[RemoteName ?? pDes.Name]; if (vOri == null && MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubClass][Dictionary] La lista de valores contiene valor null en la clave {0}", RemoteName ?? pDes.Name)); } var ctpDes = pDes.PropertyType.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass][Dictionary] La clase {0} no posee un constructor accesible sin argumentos", pDes.PropertyType.FullName)); } var vDes = ctpDes.Invoke(null); Mapping.Map.CopyTo(vOri, vDes, config); pDes.SetValue(oDes, vDes); }
public void Map(XmlNode ori, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { XmlNode vOri = null; var childs = ori.ChildNodes.OfType <XmlNode>().Where(p => p.Name == (RemoteName ?? pDes.Name)); if (childs.Count() > 1) { throw new ApplicationException(string.Format("AutoMapping[SubClass][XMLNode] El nodo {0} posee más de un subnodo con el atributo {1}", ori.Name, RemoteName ?? pDes.Name)); } vOri = childs.SingleOrDefault(); if (vOri == null) { if (MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubClass][XMLNode] El nodo {0} no posee un subnodo con el atributo {1}", ori.Name, RemoteName ?? pDes.Name)); } else { return; } } var ctpDes = pDes.PropertyType.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass][Dictionary] La clase {0} no posee un constructor accesible sin argumentos", pDes.PropertyType.FullName)); } var vDes = ctpDes.Invoke(null); Mapping.Map.CopyTo(vOri, vDes, config); if (config.UseExtraMapping && typeof(Mapping.IExtraMapping).IsAssignableFrom(pDes.PropertyType)) { ((Mapping.IExtraMapping)vDes).ExtraMappingWhenGet(vOri, null); } pDes.SetValue(oDes, vDes); }
public void Map(Type tConfig, Type tOri, object oOri, Type tDes, object oDes, PropertyInfo configProp, Config.AutoMappingConfig config) { #region Obtengo la propiedad de Destino PropertyInfo dProp = configProp; // default: tDes == tConfig if (tOri == tConfig) //configProp es parte de tOri (utilizar el RemoteName, en caso de no tener, utilizar el mosmo nombre de la propiedad) { dProp = tDes.GetProperty(this.RemoteName ?? configProp.Name); if (dProp == null) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct] No se encuentra la propiedad '{0}' para el Objeto {1}", this.RemoteName ?? configProp.Name, tDes.FullName)); } else { return; } } } #endregion #region Obtengo el valor a guardar var oProp = tOri == tConfig ? configProp : tOri.GetProperty(this.RemoteName ?? configProp.Name); if (oProp == null) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct] No se encuentra la propiedad '{0}' para el Objeto {1}", this.RemoteName ?? configProp.Name, tOri.FullName)); } else { return; } } var value = oProp.GetValue(oOri); #endregion #region Guardo el valor en la propiedad try { if (dProp.UseComplexConvert()) { dProp.SetValue(oDes, Convert.To(dProp.PropertyType, value)); } else { dProp.SetValue(oDes, value); } } catch (Exception ex) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct] Error al copiar un dato de {0}.{1} a {2}.{3}", tOri.FullName, oProp.Name, tDes.FullName, dProp.Name), ex); } } #endregion }
public void Map(Dictionary <string, object> values, Type tDes, object oDes, PropertyInfo pDes, Config.AutoMappingConfig config) { if (!ExistsProperty(values, this.RemoteName ?? pDes.Name, config)) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct][ValueList] No se encuentra el valor '{0}' en la lista de valores", this.RemoteName ?? pDes.Name)); } else { return; } } var value = GetPropertyValue(values, this.RemoteName ?? pDes.Name, config); try { if (pDes.UseComplexConvert()) { pDes.SetValue(oDes, Convert.To(pDes.PropertyType, value)); } else { pDes.SetValue(oDes, value); } } catch (Exception ex) { if (config.ThrowErrorOnDirect) { throw new ApplicationException(string.Format("AutoMapping[Direct][ValueList] Error al copiar un dato '{0}' a {1}.{2}", value, tDes.FullName, pDes.Name), ex); } } }
public void Map(Type tConfig, Type tOri, object oOri, Type tDes, object oDes, System.Reflection.PropertyInfo cProp, Config.AutoMappingConfig config) { var pOri = tConfig == tOri ? cProp : tOri.GetProperty(RemoteName ?? cProp.Name); var pDes = tConfig == tDes ? cProp : tDes.GetProperty(RemoteName ?? cProp.Name); if (pOri == null) { throw new ApplicationException(string.Format("AutoMapping[SubList] No se encuentra la propiedad {0} en {1}", RemoteName ?? cProp.Name, tOri.FullName)); } if (pDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubList] No se encuentra la propiedad {0} en {1}", RemoteName ?? cProp.Name, tDes.FullName)); } var tpDes = pDes.PropertyType; // Tipos Soportados bool isIList = false; bool isICollection = false; if (tpDes.GenericTypeArguments.Length > 0) { var tList = typeof(IList <>).MakeGenericType(tpDes.GenericTypeArguments); isIList = tList.IsAssignableFrom(tpDes); var tCollection = typeof(ICollection <>).MakeGenericType(tpDes.GenericTypeArguments); isICollection = tCollection.IsAssignableFrom(tpDes); } if (!isIList && !isICollection) { throw new ApplicationException(string.Format("AutoMapping[SubList] La propiedad de destino debe ser de tipo IList o ICollection {1}.{0}", RemoteName ?? cProp.Name, tDes.FullName)); } dynamic vDes = (dynamic)Activator.CreateInstance(tpDes); var vOri = (dynamic)pOri.GetValue(oOri); if (vOri == null) { if (MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubList] El valor de la propiedad de origen {0} debe tener algún valor para {1}", RemoteName ?? cProp.Name, tDes.FullName)); } else { vDes = null; } } else { int i = 0; foreach (var oItem in vOri) { var ctpDes = tpDes.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubList] El sub-tipo de la lista de la propiedad de destino '{0}' de la clase '{1}' no tiene un constructor accesible sin parámetros", RemoteName ?? cProp.Name, tDes.FullName)); } var dItem = Activator.CreateInstance(tpDes.GenericTypeArguments[0]); Mapping.Map.CopyTo((object)oItem, dItem, config); if (isIList) { vDes.Insert(i++, (dynamic)dItem); } else if (isICollection) { vDes.Add((dynamic)dItem); } } } pDes.SetValue(oDes, vDes); }
public void Map(Type tConfig, Type tOri, object oOri, Type tDes, object oDes, System.Reflection.PropertyInfo configProp, Config.AutoMappingConfig config) { var pOri = tConfig == tOri ? configProp : tOri.GetProperty(RemoteName ?? configProp.Name); var pDes = tConfig == tDes ? configProp : tDes.GetProperty(RemoteName ?? configProp.Name); if (pOri == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass] No se encuentra la propiedad {0} en {1}", RemoteName ?? configProp.Name, tOri.FullName)); } if (pDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass] No se encuentra la propiedad {0} en {1}", RemoteName ?? configProp.Name, tDes.FullName)); } var vOri = pOri.GetValue(oOri); if (vOri == null && MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubClass] La propiedad de origen {0}.{1} debe tener un valor", tOri.FullName, pOri.Name)); } var vDes = pDes.PropertyType.GetConstructor(new Type[] { }).Invoke(null); Mapping.Map.CopyTo(vOri, vDes, config); pDes.SetValue(oDes, vDes); }
/// <summary> /// Devuelve el XmlNode principal del XML convertido en T /// </summary> /// <returns></returns> public T GetMainElement <T>(Mapping.Config.AutoMappingConfig config) where T : class, new() { this.Xml.Load(this.FilePath); return(Get <T>(Xml.DocumentElement, config)); }
public void Map(Dictionary <string, object> values, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { var tpDes = pDes.PropertyType; // Tipos Soportados bool isIList = false; bool isICollection = false; if (tpDes.GenericTypeArguments.Length > 0) { var tList = typeof(IList <>).MakeGenericType(tpDes.GenericTypeArguments); isIList = tList.IsAssignableFrom(tpDes); var tCollection = typeof(ICollection <>).MakeGenericType(tpDes.GenericTypeArguments); isICollection = tCollection.IsAssignableFrom(tpDes); } if (!isIList && !isICollection) { throw new ApplicationException(string.Format("AutoMapping[SubList] La propiedad de destino debe ser de tipo IList o ICollection {1}.{0}", RemoteName ?? pDes.Name, tDes.FullName)); } dynamic vDes = (dynamic)Activator.CreateInstance(tpDes); if (!values.Keys.Contains(RemoteName ?? pDes.Name)) { throw new ApplicationException(string.Format("AutoMapping[SubList][ValueList] No se encuentra el valor {0} en la lista de valores para mapear {1}", RemoteName ?? pDes.Name, tDes.FullName)); } var vOri = (dynamic)values[RemoteName ?? pDes.Name]; if (vOri == null) { if (MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubList][ValueList] El valor '{0}' en la lista de valores debe tener algún valor para mapear {1}", RemoteName ?? pDes.Name, tDes.FullName)); } else { vDes = null; } } else { int i = 0; foreach (var oItem in vOri) { var ctpDes = tpDes.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubList] El sub-tipo de la lista de la propiedad de destino '{0}' de la clase '{1}' no tiene un constructor accesible sin parámetros", RemoteName ?? pDes.Name, tDes.FullName)); } var dItem = Activator.CreateInstance(tpDes.GenericTypeArguments[0]); Mapping.Map.CopyTo((object)oItem, dItem, config); if (isIList) { var mDes = tpDes.GetMethod("Insert", new Type[] { typeof(int), tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { i++, dItem }); } else if (isICollection) { var mDes = tpDes.GetMethod("Add", new Type[] { tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { dItem }); } } } pDes.SetValue(oDes, vDes); }
public void Map(DataRow ori, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { var tpDes = pDes.PropertyType; // Tipos Soportados bool isIList = false; bool isICollection = false; if (tpDes.GenericTypeArguments.Length > 0) { var tList = typeof(IList <>).MakeGenericType(tpDes.GenericTypeArguments); isIList = tList.IsAssignableFrom(tpDes); var tCollection = typeof(ICollection <>).MakeGenericType(tpDes.GenericTypeArguments); isICollection = tCollection.IsAssignableFrom(tpDes); } if (!isIList && !isICollection) { throw new ApplicationException(string.Format("AutoMapping[DataRow] La propiedad de destino debe ser de tipo IList o ICollection {1}.{0}", RemoteName ?? pDes.Name, tDes.FullName)); } dynamic vDes = (dynamic)Activator.CreateInstance(tpDes); var col = ori.Table.Columns.OfType <DataColumn>().SingleOrDefault(p => p.ColumnName == (RemoteName ?? pDes.Name)); if (col == null) { throw new ApplicationException(string.Format("AutoMapping[DataRow] No se encuentra la columna {0} en el data row para mapear {1}", RemoteName ?? pDes.Name, tDes.FullName)); } var vOri = (dynamic)ori[col]; if (vOri == null && this.MustExists) { throw new ApplicationException(string.Format("AutoMapping[DataRow] La columna {0} no posee un valor válido para mapear {1}", RemoteName ?? pDes.Name, tDes.FullName)); } int i = 0; foreach (var oItem in vOri) { var ctpDes = tpDes.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[DataRow] El sub-tipo de la lista de la propiedad de destino '{0}' de la clase '{1}' no tiene un constructor accesible sin parámetros", RemoteName ?? pDes.Name, tDes.FullName)); } var dItem = Activator.CreateInstance(tpDes.GenericTypeArguments[0]); Mapping.Map.CopyTo(oItem, dItem, config); if (isIList) { var mDes = tpDes.GetMethod("Insert", new Type[] { typeof(int), tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { i++, dItem }); } else if (isICollection) { var mDes = tpDes.GetMethod("Add", new Type[] { tpDes.GenericTypeArguments[0] }); mDes.Invoke(vDes, new object[] { dItem }); } } pDes.SetValue(oDes, vDes); }
public void Map(DataRow oOri, Type tDes, object oDes, System.Reflection.PropertyInfo pDes, Config.AutoMappingConfig config) { var col = oOri.Table.Columns.OfType <DataColumn>().SingleOrDefault(p => p.ColumnName == (RemoteName ?? pDes.Name)); if (col == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass][DataRow] No se encuentra la columna {0} en DataRow de origen de datos para la clase {1}", RemoteName ?? pDes.Name, tDes.FullName)); } var vOri = oOri[col]; if (vOri == null && MustExists) { throw new ApplicationException(string.Format("AutoMapping[SubClass][Datarow] El valor de la columna {0} en el DataRow debe tener un valor para la clase {1}", RemoteName ?? pDes.Name, tDes.FullName)); } var ctpDes = pDes.PropertyType.GetConstructor(new Type[] { }); if (ctpDes == null) { throw new ApplicationException(string.Format("AutoMapping[SubClass][DataRow] La clase {0} no posee un constructor accesible sin argumentos", pDes.PropertyType.FullName)); } var vDes = ctpDes.Invoke(null); Mapping.Map.CopyTo(vOri, vDes, config); pDes.SetValue(oDes, vDes); }