| | |
| | | if (revertObj.StateCode.ToUpper () == "SUCCESS") { |
| | | var DATA = Newtonsoft.Json.JsonConvert.DeserializeObject<AddBackupNameResult> (revertObj.ResponseData.ToString()); |
| | | var addFolderID = DATA.BackupClassId; |
| | | UpLoadBackupFileToDB (addFolderID); |
| | | //UpLoadBackupFileToDB (addFolderID); |
| | | UpLoadBackupFileToDBNew (addFolderID); |
| | | |
| | | |
| | | } else { |
| | |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 上传备份文件 |
| | | /// </summary> |
| | | /// <param name="mBackupClassId"></param> |
| | | void UpLoadBackupFileToDB (string mBackupClassId) |
| | | { |
| | | ///// <summary> |
| | | ///// 上传备份文件 |
| | | ///// </summary> |
| | | ///// <param name="mBackupClassId"></param> |
| | | //void UpLoadBackupFileToDB (string mBackupClassId) |
| | | //{ |
| | | |
| | | var backuplist = IO.FileUtils.ReadFiles (); |
| | | int index = 0; |
| | | foreach (var fileName in backuplist) { |
| | | index++; |
| | | /// <summary> |
| | | /// 如果是特殊的注册登陆文件,则不需要备份到服务器 |
| | | /// </summary> |
| | | if (fileName == UserInfo.GlobalRegisterFile) { |
| | | continue; |
| | | } |
| | | //整理数据 |
| | | var datainfo = new FileInfoData (); |
| | | datainfo.BackupFileName = fileName; |
| | | datainfo.BackupFileContent = Shared.IO.FileUtils.ReadFile (fileName); |
| | | |
| | | var list = new List<FileInfoData> (); |
| | | list.Add (datainfo); |
| | | |
| | | var upData = new UploadHomeAppGatewaySubFilesObj (); |
| | | upData.HomeId = UserConfig.Instance.CurrentRegion.Id; |
| | | upData.BackupClassId = mBackupClassId; |
| | | upData.UploadSubFileLists = list; |
| | | //获取控制主人账号的Token |
| | | upData.LoginAccessToken = MainPage.LoginUser.LoginTokenString; |
| | | string urlHead = MainPage.RequestHttpsHost; |
| | | if (upData.IsOtherAccountCtrl) { |
| | | urlHead = UserConfig.Instance.MasterAccountRequestBaseUrl; |
| | | upData.LoginAccessToken = UserConfig.Instance.MasterAccountToken; |
| | | } |
| | | |
| | | //AddUserBackupObj requestObj2 = new AddUserBackupObj () { LevelID = Convert.ToInt32 (revertObj.ResponseData), Name = fileName, DetailByte = IO.FileUtils.ReadFile (fileName) }; |
| | | var requestJson2 = Newtonsoft.Json.JsonConvert.SerializeObject (upData); |
| | | var revertObj2 = MainPage.RequestHttps (API.UploadHomeAppGatewaySubFiles, requestJson2, urlHead); |
| | | if (revertObj2.StateCode.ToUpper () == "SUCCESS") { |
| | | // var backuplist = IO.FileUtils.ReadFiles (); |
| | | // int index = 0; |
| | | // foreach (var fileName in backuplist) { |
| | | //index++; |
| | | Application.RunOnMainThread (() => { |
| | | int pro = (int)(index * 1.0 / backuplist.Count * 100); |
| | | MainPage.Loading.Text = pro.ToString () + "%"; |
| | | }); |
| | | } else { |
| | | //提示错误 |
| | | break; |
| | | } |
| | | } |
| | | if (index != backuplist.Count) { |
| | | //2020-01-11 |
| | | DeleteFolderDataAfterUploadFailed (mBackupClassId); |
| | | Shared.Application.RunOnMainThread (() => { |
| | | new Alert ("", "Upload failed!", Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | MainPage.Loading.Hide (); |
| | | }); |
| | | // /// <summary> |
| | | // /// 如果是特殊的注册登陆文件,则不需要备份到服务器 |
| | | // /// </summary> |
| | | // if (fileName == UserInfo.GlobalRegisterFile) { |
| | | // continue; |
| | | // } |
| | | // //整理数据 |
| | | // var datainfo = new FileInfoData (); |
| | | // datainfo.BackupFileName = fileName; |
| | | // datainfo.BackupFileContent = Shared.IO.FileUtils.ReadFile (fileName); |
| | | |
| | | } |
| | | // var list = new List<FileInfoData> (); |
| | | // list.Add (datainfo); |
| | | |
| | | //Shared.Application.RunOnMainThread (() => { |
| | | // if (index == 0) { |
| | | // new Alert ("", "Upload failed!", Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | // var upData = new UploadHomeAppGatewaySubFilesObj (); |
| | | // upData.HomeId = UserConfig.Instance.CurrentRegion.Id; |
| | | // upData.BackupClassId = mBackupClassId; |
| | | // upData.UploadSubFileLists = list; |
| | | // //获取控制主人账号的Token |
| | | // upData.LoginAccessToken = MainPage.LoginUser.LoginTokenString; |
| | | // string urlHead = MainPage.RequestHttpsHost; |
| | | // if (upData.IsOtherAccountCtrl) { |
| | | // urlHead = UserConfig.Instance.MasterAccountRequestBaseUrl; |
| | | // upData.LoginAccessToken = UserConfig.Instance.MasterAccountToken; |
| | | // } |
| | | |
| | | // //AddUserBackupObj requestObj2 = new AddUserBackupObj () { LevelID = Convert.ToInt32 (revertObj.ResponseData), Name = fileName, DetailByte = IO.FileUtils.ReadFile (fileName) }; |
| | | // var requestJson2 = Newtonsoft.Json.JsonConvert.SerializeObject (upData); |
| | | // var revertObj2 = MainPage.RequestHttps (API.UploadHomeAppGatewaySubFiles, requestJson2, urlHead); |
| | | // if (revertObj2.StateCode.ToUpper () == "SUCCESS") { |
| | | // //index++; |
| | | // Application.RunOnMainThread (() => { |
| | | // int pro = (int)(index * 1.0 / backuplist.Count * 100); |
| | | // MainPage.Loading.Text = pro.ToString () + "%"; |
| | | // }); |
| | | // } else { |
| | | // //提示错误 |
| | | // break; |
| | | // } |
| | | // } |
| | | // if (index != backuplist.Count) { |
| | | // //2020-01-11 |
| | | // DeleteFolderDataAfterUploadFailed (mBackupClassId); |
| | | // Shared.Application.RunOnMainThread (() => { |
| | | // new Alert ("", "Upload failed!", Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | // MainPage.Loading.Hide (); |
| | | //}); |
| | | } |
| | | |
| | | // } |
| | | |
| | | // //Shared.Application.RunOnMainThread (() => { |
| | | // // if (index == 0) { |
| | | // // new Alert ("", "Upload failed!", Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | // // } |
| | | |
| | | // // MainPage.Loading.Hide (); |
| | | // //}); |
| | | //} |
| | | |
| | | |
| | | |
| | |
| | | mDownLoad.LoginAccessToken = UserConfig.Instance.MasterAccountToken; |
| | | } |
| | | |
| | | int index = 0; |
| | | MultiThreadDownload (responseDataObj, mDownLoad, mUrlHead); |
| | | |
| | | |
| | | //int index = 0; |
| | | |
| | | //foreach (var file in responseDataObj) { |
| | | // index++; |
| | | // if (file.BackupFileName == "null" || "UserConfig" == file.BackupFileName) { |
| | | // continue; |
| | | // } |
| | |
| | | // var requestJson3 = Newtonsoft.Json.JsonConvert.SerializeObject (mDownLoad); |
| | | // var revertObj3 = MainPage.RequestHttpsReturnByte (API.DownloadSomeDataBackup, requestJson3, false, mUrlHead); |
| | | // if (revertObj3 != null) { |
| | | // index++; |
| | | |
| | | // IO.FileUtils.WriteFileByBytes (file.BackupFileName, revertObj3); |
| | | // Application.RunOnMainThread (() => { |
| | | // int pro = (int)(index * 1.0 / responseDataObj.Count * 100); |
| | | // MainPage.Loading.Text = pro.ToString () + "%"; |
| | | // }); |
| | | // } else { |
| | | |
| | | // break; |
| | | // } |
| | | |
| | | // } |
| | | |
| | | // //if (revertObj3.StateCode.ToUpper() == "SUCCESS") { |
| | | // // var jsonBytes = Newtonsoft.Json.JsonConvert.SerializeObject (revertObj3.ResponseData); |
| | | // // var byresss = Newtonsoft.Json.JsonConvert.DeserializeObject<byte[]> (jsonBytes); |
| | | // // index++; |
| | | // // //新增加了住宅区域,旧的备份数据没有区域属性,当获取到这个文件时,把当前到住宅信息更到当前备份到数据里面 |
| | | // // if ("UserConfig" == file.BackupFileName){ |
| | | // // //var regionTemp = UserConfig.Instance.CurrentRegion; |
| | | // // //UserConfig.Instance.RefreshUserConfig (); |
| | | // // //UserConfig.Instance.CurrentRegion = regionTemp; |
| | | // // //UserConfig.Instance.SaveUserConfig (); |
| | | // // ////UserConfig.Instance.GatewayList |
| | | // // continue; |
| | | // // } |
| | | // // IO.FileUtils.WriteFileByBytes (file.BackupFileName, byresss); |
| | | // // Application.RunOnMainThread (() => { |
| | | // // int pro = (int)(index * 1.0 / responseDataObj.Count * 100); |
| | | // // MainPage.Loading.Text = pro.ToString () + "%"; |
| | | // // }); |
| | | // //} else { |
| | | // // //错误提示 |
| | | |
| | | // //} |
| | | //if (index != responseDataObj.Count) { |
| | | // //删除已经下载的文件个数错误 |
| | | // var fileNames2 = IO.FileUtils.ReadFiles (); |
| | | // foreach (var fileName in fileNames2) { |
| | | // if (fileName == UserInfo.GlobalRegisterFile) { |
| | | // continue; |
| | | //} |
| | | foreach (var file in responseDataObj) { |
| | | index++; |
| | | if (file.BackupFileName == "null" || "UserConfig" == file.BackupFileName) { |
| | | continue; |
| | | } |
| | | mDownLoad.BackupFileName = file.BackupFileName; |
| | | //var requestObj3 = new BackupDetailObj () { Id = file.Id }; |
| | | var requestJson3 = Newtonsoft.Json.JsonConvert.SerializeObject (mDownLoad); |
| | | var revertObj3 = MainPage.RequestHttpsReturnByte (API.DownloadSomeDataBackup, requestJson3, false, mUrlHead); |
| | | if (revertObj3 != null) { |
| | | // IO.FileUtils.DeleteFile (fileName); |
| | | // } |
| | | |
| | | IO.FileUtils.WriteFileByBytes (file.BackupFileName, revertObj3); |
| | | Application.RunOnMainThread (() => { |
| | | int pro = (int)(index * 1.0 / responseDataObj.Count * 100); |
| | | MainPage.Loading.Text = pro.ToString () + "%"; |
| | | }); |
| | | } else { |
| | | break; |
| | | } |
| | | // MainPage.ShowAlertOnMainThread ("Download failed!"); |
| | | |
| | | } |
| | | // return; |
| | | //} |
| | | |
| | | |
| | | if (index != responseDataObj.Count) { |
| | | //删除已经下载的文件个数错误 |
| | | var fileNames2 = IO.FileUtils.ReadFiles (); |
| | | foreach (var fileName in fileNames2) { |
| | | if (fileName == UserInfo.GlobalRegisterFile) { |
| | | continue; |
| | | } |
| | | IO.FileUtils.DeleteFile (fileName); |
| | | } |
| | | |
| | | MainPage.ShowAlertOnMainThread ("Download failed!"); |
| | | |
| | | return; |
| | | } |
| | | |
| | | MainPage.LoginUser.SaveUserInfo (); |
| | | Room.InitAllRoom (); |
| | | Application.RunOnMainThread (() => { |
| | | this.RemoveFromParent (); |
| | | CommonPage.IsRemote = false; |
| | | UserMiddle.Init (); |
| | | EquipmentPublicClass.CheckLinkRemote (UserConfig.Instance.internetStatus); |
| | | new Alert ("", Language.StringByID (R.MyInternationalizationString.RestoreFileIsSuccessfull), |
| | | Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | }); |
| | | MainPage.LoginUser = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfo> (System.Text.Encoding.UTF8.GetString (Shared.IO.FileUtils.ReadFile (UserInfo.GlobalRegisterFile))); |
| | | if (MainPage.LoginUser != null) { |
| | | MainPage.LoginUser.LastTime = System.DateTime.Now; |
| | | MainPage.LoginUser.SaveUserInfo (); |
| | | } |
| | | //MainPage.LoginUser.SaveUserInfo (); |
| | | //Room.InitAllRoom (); |
| | | //Application.RunOnMainThread (() => { |
| | | // this.RemoveFromParent (); |
| | | // CommonPage.IsRemote = false; |
| | | // UserMiddle.Init (); |
| | | // EquipmentPublicClass.CheckLinkRemote (UserConfig.Instance.internetStatus); |
| | | // new Alert ("", Language.StringByID (R.MyInternationalizationString.RestoreFileIsSuccessfull), |
| | | // Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | //}); |
| | | //MainPage.LoginUser = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfo> (System.Text.Encoding.UTF8.GetString (Shared.IO.FileUtils.ReadFile (UserInfo.GlobalRegisterFile))); |
| | | //if (MainPage.LoginUser != null) { |
| | | // MainPage.LoginUser.LastTime = System.DateTime.Now; |
| | | // MainPage.LoginUser.SaveUserInfo (); |
| | | //} |
| | | } |
| | | } catch (Exception ex) { |
| | | Shared.Application.RunOnMainThread (() => { |
| | |
| | | }; |
| | | alert.Show (); |
| | | } |
| | | |
| | | |
| | | bool DownloadSomeDataBackup (DownloadSomeDataBackupObj mmDownLoad, string mUrlHead, LoadBackupFileNameInfo file) |
| | | { |
| | | DownloadSomeDataBackupObj mDownLoad = mmDownLoad; |
| | | mDownLoad.BackupFileName = file.BackupFileName; |
| | | //var requestObj3 = new BackupDetailObj () { Id = file.Id }; |
| | | var requestJson3 = Newtonsoft.Json.JsonConvert.SerializeObject (mDownLoad); |
| | | var revertObj3 = MainPage.RequestHttpsReturnByte (API.DownloadSomeDataBackup, requestJson3, false, mUrlHead); |
| | | if (revertObj3 != null) { |
| | | |
| | | IO.FileUtils.WriteFileByBytes (file.BackupFileName, revertObj3); |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 删除本地文件 |
| | | /// </summary> |
| | | void ReadFilesAndDelete () |
| | | { |
| | | |
| | | var fileNames = IO.FileUtils.ReadFiles (); |
| | | foreach (var fileName in fileNames) { |
| | | if (fileName == UserInfo.GlobalRegisterFile || fileName == UserConfig.configFile) { |
| | | continue; |
| | | } |
| | | IO.FileUtils.DeleteFile (fileName); |
| | | } |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 多线程下载 |
| | | /// </summary> |
| | | void MultiThreadDownload (List<LoadBackupFileNameInfo> dataList, DownloadSomeDataBackupObj mDownLoad, string mUrlHead, int maxThreads = 5) |
| | | { |
| | | |
| | | int index = 0; |
| | | int indexCount = 0; |
| | | |
| | | var mFileList = dataList.FindAll ((obj) => obj.BackupFileName != "null" && obj.BackupFileName != UserConfig.configFile && obj.BackupFileName != UserInfo.GlobalRegisterFile && obj.BackupFileName != "AccountListDB"); |
| | | if (maxThreads > mFileList.Count) { |
| | | maxThreads = mFileList.Count; |
| | | } |
| | | |
| | | if (mFileList.Count <= 0) return; |
| | | |
| | | |
| | | int resultCount = mFileList.Count / maxThreads; |
| | | |
| | | //int residue = mFileList.Count % maxThreads; |
| | | |
| | | /*开启若干线程,分别下载对应的资源*/ |
| | | for (int i = 1; i <= maxThreads; i++) { |
| | | int startIndex = ((i - 1) * resultCount + 0); |
| | | int endIndex = resultCount * i - 1; |
| | | if (i == maxThreads) { |
| | | /* |
| | | * 最后一个线程 |
| | | * */ |
| | | endIndex = mFileList.Count - 1; |
| | | } |
| | | var id = i; |
| | | var startIndex1 = startIndex; |
| | | var endIndex1 = endIndex; |
| | | //Utlis.WriteLine ($"下载线程:{id} startIndex1:{startIndex1} endIndex1:{endIndex1}"); |
| | | new System.Threading.Thread (() => { |
| | | |
| | | for (var j = startIndex1; j <= endIndex1; j++) { |
| | | var result = DownloadSomeDataBackup (mDownLoad, mUrlHead, dataList [j]); |
| | | if (result) { |
| | | index++; |
| | | Application.RunOnMainThread (() => { |
| | | int pro = (int)(index * 1.0 / mFileList.Count * 100); |
| | | MainPage.Loading.Text = pro.ToString () + "%"; |
| | | }); |
| | | |
| | | } |
| | | indexCount++; |
| | | //Utlis.WriteLine ($"下载线程:{id} 完成index:{j} indexCount: {indexCount}"); |
| | | } |
| | | |
| | | }) { IsBackground = true }.Start (); |
| | | |
| | | System.Threading.Thread.Sleep (100); |
| | | |
| | | } |
| | | |
| | | while (indexCount < mFileList.Count) { |
| | | System.Threading.Thread.Sleep (200); |
| | | } |
| | | Utlis.WriteLine ($"下载完成"); |
| | | |
| | | if (index != mFileList.Count) { |
| | | //删除已经下载的文件个数错误 |
| | | ReadFilesAndDelete (); |
| | | |
| | | Application.RunOnMainThread (() => { |
| | | new Alert (Language.StringByID (R.MyInternationalizationString.Tip), Language.StringByID (R.MyInternationalizationString.DownloadFailed), |
| | | Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | }); |
| | | |
| | | |
| | | return; |
| | | } |
| | | |
| | | |
| | | MainPage.LoginUser.SaveUserInfo (); |
| | | Room.InitAllRoom (); |
| | | Application.RunOnMainThread (() => { |
| | | this.RemoveFromParent (); |
| | | CommonPage.IsRemote = false; |
| | | UserMiddle.Init (); |
| | | EquipmentPublicClass.CheckLinkRemote (UserConfig.Instance.internetStatus); |
| | | new Alert ("", Language.StringByID (R.MyInternationalizationString.RestoreFileIsSuccessfull), |
| | | Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | }); |
| | | MainPage.LoginUser = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfo> (System.Text.Encoding.UTF8.GetString (Shared.IO.FileUtils.ReadFile (UserInfo.GlobalRegisterFile))); |
| | | if (MainPage.LoginUser != null) { |
| | | MainPage.LoginUser.LastTime = System.DateTime.Now; |
| | | MainPage.LoginUser.SaveUserInfo (); |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | | |
| | | bool UploadDataBackup (UploadHomeAppGatewaySubFilesObj upData, string mUrlHead, List<string> fileList, int startIndex1, int endIndex1) |
| | | { |
| | | try { |
| | | |
| | | var list = new List<FileInfoData> (); |
| | | for (var j = startIndex1; j <= endIndex1; j++) { |
| | | var datainfo = new FileInfoData (); |
| | | datainfo.BackupFileName = fileList [j]; |
| | | datainfo.BackupFileContent = Shared.IO.FileUtils.ReadFile (fileList [j]); |
| | | list.Add (datainfo); |
| | | } |
| | | |
| | | //整理数据 |
| | | UploadHomeAppGatewaySubFilesObj mUpData = upData; |
| | | mUpData.UploadSubFileLists = list; |
| | | |
| | | var requestJson2 = Newtonsoft.Json.JsonConvert.SerializeObject (mUpData); |
| | | var revertObj2 = MainPage.RequestHttps (API.UploadHomeAppGatewaySubFiles, requestJson2, mUrlHead); |
| | | if (revertObj2.StateCode.ToUpper () == "SUCCESS") { |
| | | return true; |
| | | } else { |
| | | //提示错误 |
| | | return false; |
| | | } |
| | | } catch { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// 上传备份文件 |
| | | /// 多次上传多个文件 |
| | | /// </summary> |
| | | /// <param name="mBackupClassId"></param> |
| | | void UpLoadBackupFileToDBNew (string mBackupClassId, int maxThreads = 30) |
| | | { |
| | | var backuplist = IO.FileUtils.ReadFiles (); |
| | | var mFileList = backuplist.FindAll ((obj) => obj != "null" && obj != UserConfig.configFile && obj != UserInfo.GlobalRegisterFile && obj != "AccountListDB"); |
| | | if (mFileList.Count <= 0) return; |
| | | |
| | | if (maxThreads > mFileList.Count) { |
| | | maxThreads = mFileList.Count; |
| | | } |
| | | |
| | | int index = 0; |
| | | int indexCount = 0; |
| | | int resultCount = mFileList.Count / maxThreads; |
| | | |
| | | var upData = new UploadHomeAppGatewaySubFilesObj (); |
| | | upData.HomeId = UserConfig.Instance.CurrentRegion.Id; |
| | | upData.BackupClassId = mBackupClassId; |
| | | //获取控制主人账号的Token |
| | | upData.LoginAccessToken = MainPage.LoginUser.LoginTokenString; |
| | | string urlHead = MainPage.RequestHttpsHost; |
| | | if (upData.IsOtherAccountCtrl) { |
| | | urlHead = UserConfig.Instance.MasterAccountRequestBaseUrl; |
| | | upData.LoginAccessToken = UserConfig.Instance.MasterAccountToken; |
| | | } |
| | | |
| | | |
| | | for (int i = 1; i <= resultCount; i++) { |
| | | int startIndex = ((i - 1) * maxThreads + 0); |
| | | int endIndex = maxThreads * i - 1; |
| | | if (i == resultCount) { |
| | | /*最后一个线程* */ |
| | | endIndex = mFileList.Count - 1; |
| | | } |
| | | var id = i; |
| | | var startIndex1 = startIndex; |
| | | var endIndex1 = endIndex; |
| | | |
| | | var result = UploadDataBackup (upData, urlHead, mFileList, startIndex1, endIndex1); |
| | | if (result) { |
| | | index++; |
| | | Application.RunOnMainThread (() => { |
| | | int pro = (int)(index * 1.0 / resultCount * 100); |
| | | MainPage.Loading.Text = pro.ToString () + "%"; |
| | | }); |
| | | |
| | | } |
| | | indexCount++; |
| | | |
| | | } |
| | | |
| | | |
| | | Utlis.WriteLine ($"上传完成"); |
| | | |
| | | if (index != resultCount) { |
| | | //2020-01-11 备份失败 |
| | | DeleteFolderDataAfterUploadFailed (mBackupClassId); |
| | | Shared.Application.RunOnMainThread (() => { |
| | | new Alert ("", Language.StringByID (R.MyInternationalizationString.FailedToBackupFile), Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | MainPage.Loading.Hide (); |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | Application.RunOnMainThread (() => { |
| | | //MainPage.Loading.Text = "100%"; |
| | | new Alert ("", Language.StringByID (R.MyInternationalizationString.BackupFileIsSuccessful), Language.StringByID (R.MyInternationalizationString.Close)).Show (); |
| | | }); |
| | | |
| | | } |
| | | |
| | | } |
| | | } |