Esempio n. 1
1
        //Appends the any xml file/folder nodes onto the folder
        private void AddXmlNodes(FolderCompareObject folder, int numOfPaths, XmlDocument xmlDoc)
        {
            List<XMLCompareObject> xmlObjList = new List<XMLCompareObject>();
            List<string> xmlFolderList = new List<string>();

            for (int i = 0; i < numOfPaths; i++)
            {
                string path = Path.Combine(folder.GetSmartParentPath(i), folder.Name);

                if (Directory.Exists(path))
                {
                    DirectoryInfo dirInfo = new DirectoryInfo(path);
                    FileInfo[] fileList = dirInfo.GetFiles();
                    DirectoryInfo[] dirInfoList = dirInfo.GetDirectories();
                    string xmlPath = Path.Combine(path, CommonXMLConstants.MetadataPath);

                    if (!File.Exists(xmlPath))
                        continue;

                    CommonMethods.LoadXML(ref xmlDoc, xmlPath);
                    xmlObjList = GetAllFilesInXML(xmlDoc);
                    xmlFolderList = GetAllFoldersInXML(xmlDoc);
                    RemoveSimilarFiles(xmlObjList, fileList);
                    RemoveSimilarFolders(xmlFolderList, dirInfoList);
                }

                AddFileToChild(xmlObjList, folder, i, numOfPaths);
                AddFolderToChild(xmlFolderList, folder, i, numOfPaths);
                xmlObjList.Clear();
                xmlFolderList.Clear();
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Visit <see cref="FolderCompareObject"/> implementation for BuilderVisitor.
        /// </summary>
        /// <param name="folder">The <see cref="FolderCompareObject"/> to build and process.</param>
        /// <param name="numOfPaths">The total number of folders to keep in sync.</param>
        public void Visit(FolderCompareObject folder, int numOfPaths)
        {
            try
            {
                RootCompareObject root = folder as RootCompareObject;

                for (int index = 0; index < numOfPaths; index++)
                {
                    string path = root == null ? Path.Combine(folder.GetSmartParentPath(index), folder.Name) : root.Paths[index];
                    DirectoryInfo f = new DirectoryInfo(path);

                    if (f.Exists)
                    {
                        if (_progress != null)
                        {
                            _progress.Message = f.FullName;
                            _progress.Update();
                        }

                        ProcessFolders(folder, numOfPaths, f, index);
                        ProcessFiles(folder, numOfPaths, f, index);
                    }
                }
            }
            catch (UnauthorizedAccessException e)
            {
                ServiceLocator.GetLogger(ServiceLocator.DEBUG_LOG).Write(e);
                ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.UNKNOWN, "Error retrieving contents of folder due to unauthorized access."));
            }
            catch (PathTooLongException e)
            {
                ServiceLocator.GetLogger(ServiceLocator.DEBUG_LOG).Write(e);
                ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.UNKNOWN, "Error retrieving contents of folder due to path being too long."));
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Visits each folder node and return them and it will update the values either through the meta data
        /// or the last known state document
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="numOfPaths"></param>
        public void Visit(FolderCompareObject folder, int numOfPaths)
        {
            XmlDocument xmlDoc = new XmlDocument();
            PopulateFolderMetaName(folder, numOfPaths);

            for (int i = 0; i < numOfPaths; i++)
            {
                string path = Path.Combine(folder.GetSmartParentPath(i), CommonXMLConstants.MetadataPath);

                if (!File.Exists(path))
                    continue;

                CommonMethods.LoadXML(ref xmlDoc, path);
                folder = PopulateFolderWithMetaData(xmlDoc, folder, i);
            }

            AddXmlNodes(folder, numOfPaths, xmlDoc);
        }
Esempio n. 4
0
        /// <summary>
        /// It calls ProcessFolderFinalState method and if either needLastKnownState or the method is true,
        /// it will call GenerateFolderLastKnownState method to write a last known state document
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="numOfPaths"></param>
        public void Visit(FolderCompareObject folder, int numOfPaths)
        {
            if (folder.Invalid)
                return;

            bool needLastKnownState = false;

            for (int i = 0; i < numOfPaths; i++)
            {
                _progress.Message = Path.Combine(folder.GetSmartParentPath(i), folder.Name);
                needLastKnownState = ProcessFolderFinalState(folder, i) || needLastKnownState;
            }

            if (needLastKnownState)
                GenerateFolderLastKnownState(folder, numOfPaths);

            _progress.Complete();
        }
Esempio n. 5
0
        // Checks the meta data by the folder name , if it exists , set it MetaExists to true.
        // If not , look up the last known state and populate the values from that document
        private FolderCompareObject PopulateFolderWithMetaData(XmlDocument xmlDoc, FolderCompareObject folder, int counter)
        {
            XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");
            if (node != null)
            {
                folder.MetaExists[counter] = true;
            }
            else
            {
                string path = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.LastKnownStatePath);

                if (File.Exists(path))
                {
                    XmlDocument lastKnownXmlDoc = new XmlDocument();
                    CommonMethods.LoadXML(ref lastKnownXmlDoc, path);
                    XmlNode folderNode = lastKnownXmlDoc.SelectSingleNode(CommonXMLConstants.XPathLastKnownState + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");

                    if (folderNode != null)
                    {
                        XmlNodeList nodeList = folderNode.ChildNodes;
                        for (int i = 0; i < nodeList.Count; i++)
                        {
                            XmlNode childNode = nodeList[i];

                            switch (childNode.Name)
                            {
                                case CommonXMLConstants.NodeAction:
                                    string action = childNode.InnerText;
                                    if (action.Equals("deleted"))
                                        folder.LastKnownState[counter] = LastKnownState.Deleted;
                                    else
                                        folder.LastKnownState[counter] = LastKnownState.Renamed;
                                    break;
                                case CommonXMLConstants.NodeLastUpdatedUtc:
                                    folder.MetaUpdated[counter] = long.Parse(childNode.InnerText);
                                    break;
                            }
                        }
                    }
                }
            }

            return folder;
        }
Esempio n. 6
0
        // Populate the folder's meta name if it exists
        private void PopulateFolderMetaName(FolderCompareObject folder, int numOfPaths)
        {
            for (int i = 0; i < numOfPaths; i++)
            {
                string currMetaData = Path.Combine(Path.Combine(folder.GetSmartParentPath(i), folder.Name), CommonXMLConstants.MetadataPath);

                if (File.Exists(currMetaData))
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    CommonMethods.LoadXML(ref xmlDoc, currMetaData);
                    XmlNode xmlNode = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + "/name");
                    if (xmlNode != null)
                        folder.MetaName = xmlNode.InnerText;
                    
                }
            }
        }
Esempio n. 7
0
        private void MoveFolder(FolderCompareObject folder, int numOfPaths, int srcFolderPos)
        {
            bool changed = false;

            for (int i = 0; i < numOfPaths; i++)
            {
                if (i != srcFolderPos && folder.Priority[i] != folder.Priority[srcFolderPos])
                {
                    string oldFolderName = Path.Combine(folder.GetSmartParentPath(i), folder.Name);
                    string newFolderName = Path.Combine(folder.GetSmartParentPath(i), folder.NewName);
                    string srcFolderName = Path.Combine(folder.GetSmartParentPath(srcFolderPos), folder.NewName);

                    try
                    {
                        if (Directory.Exists(oldFolderName))
                        {
                            CommonMethods.MoveFolder(oldFolderName, newFolderName); // Rename the old folder to the new name if it exists.
                            folder.FinalState[i] = FinalState.Renamed;
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_RENAMED, "Folder renamed from " + oldFolderName + " to " + newFolderName));
                        }
                        else
                        {
                            CommonMethods.CopyDirectory(srcFolderName, newFolderName); // Copy the directory from the source folder if the old folder does not exist.
                            folder.Exists[i] = true;
                            folder.CreationTimeUtc[i] = Directory.GetCreationTimeUtc(newFolderName).Ticks;
                            folder.FinalState[i] = FinalState.CreatedRenamed;
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_CREATED, "Folder copied from " + srcFolderName + " to " + newFolderName));
                        }
                        changed = true;
                    }
                    catch (MoveFolderException)
                    {
                        folder.FinalState[i] = FinalState.Error;
                        ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error renaming folder from " + oldFolderName + " to " + newFolderName));
                    }
                    catch (CopyFolderException)
                    {
                        folder.FinalState[i] = FinalState.Error;
                        ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error copying folder: " + srcFolderName + " to " + newFolderName));
                    }
                }
                else
                {
                    folder.FinalState[i] = FinalState.Renamed;
                    changed = true;
                }
            }
            folder.FinalState[srcFolderPos] = changed ? FinalState.Renamed : FinalState.Unchanged; // If changed is true, that means at least one file has been affected, set the FinalState to renamed. Otherwise, leave it as Unchanged.
        }
Esempio n. 8
0
        private void DeleteFolder(FolderCompareObject folder, int numOfPaths, int srcFolderPos)
        {
            bool changed = false;

            for (int i = 0; i < numOfPaths; i++)
            {
                if (i != srcFolderPos && folder.Priority[i] != folder.Priority[srcFolderPos])
                {
                    string destFolder = Path.Combine(folder.GetSmartParentPath(i), folder.Name);

                    try
                    {
                        if (_syncConfig.ArchiveLimit > 0)
                        {
                            CommonMethods.ArchiveFolder(destFolder, _syncConfig.ArchiveName, _syncConfig.ArchiveLimit);
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ARCHIVED, "Folder archived " + destFolder));
                        }
                    }
                    catch (ArchiveFolderException)
                    {
                        folder.FinalState[i] = FinalState.Error;
                        ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error archiving folder " + destFolder));
                    }

                    try
                    {
                        if (_syncConfig.Recycle)
                            CommonMethods.DeleteFolderToRecycleBin(destFolder);
                        else
                            CommonMethods.DeleteFolder(destFolder);

                        folder.Exists[i] = false;
                        folder.FinalState[i] = FinalState.Deleted;
                        changed = true; // Set it to true since the change has propaged to at least one folder

                        if (_syncConfig.Recycle)
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_DELETED, "Folder deleted to recycle bin " + destFolder));
                        else
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_DELETED, "Folder deleted " + destFolder));
                    }
                    catch (DeleteFolderException)
                    {
                        folder.FinalState[i] = FinalState.Error;
                        ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error deleting folder " + destFolder));
                    }
                }
                else
                {
                    folder.FinalState[i] = folder.MetaExists[i] ? FinalState.Deleted : FinalState.Unchanged;
                    changed = true; // Set it to true since the change has propaged to at least one folder
                }

            }
            folder.FinalState[srcFolderPos] = changed ? FinalState.Deleted : FinalState.Unchanged; // Set the FinalState to deleted if changed is true, otherwise set it as Unchanged.
        }
Esempio n. 9
0
        private void CreateFolder(FolderCompareObject folder, int numOfPaths, int srcFolderPos)
        {
            for (int i = 0; i < numOfPaths; i++)
            {
                if (i != srcFolderPos && folder.Priority[i] != folder.Priority[srcFolderPos])
                {
                    string folderToCreate = Path.Combine(folder.GetSmartParentPath(i), folder.Name);

                    if (!Directory.Exists(folderToCreate))
                    {
                        try
                        {
                            CommonMethods.CreateFolder(folderToCreate); // Create the folder since it does not exist
                            folder.Exists[i] = true; // Update the state of the folder
                            folder.CreationTimeUtc[i] = Directory.GetCreationTimeUtc(folderToCreate).Ticks;
                            folder.FinalState[i] = FinalState.Created;
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_CREATED, "Folder created " + folderToCreate));
                        }
                        catch (CreateFolderException)
                        {
                            folder.FinalState[i] = FinalState.Error;
                            ServiceLocator.GetLogger(ServiceLocator.USER_LOG).Write(new LogData(LogEventType.FSCHANGE_ERROR, "Error creating folder " + folderToCreate));
                        }
                    }
                }
                else
                {
                    folder.FinalState[i] = folder.MetaExists[i] ? FinalState.Unchanged : FinalState.Created; // Set the FinalState to unchanged if the metadata already exists, else set it to created.
                }
            }
            folder.FinalState[srcFolderPos] = folder.MetaExists[srcFolderPos] ? FinalState.Unchanged : FinalState.Created; // Set the FinalState to unchanged if the metadata exists, else set it to created.
        }
Esempio n. 10
0
 // Checks if the last known state document exists and calls the AppendActionFolderLastKnownState method 
 private void GenerateFolderLastKnownState(FolderCompareObject folder, int numOfPaths)
 {
     for (int i = 0; i < numOfPaths; i++)
     {
         string parentPath = folder.GetSmartParentPath(i);
         XmlDocument xmlTodoDoc = new XmlDocument();
         string todoPath = Path.Combine(parentPath, CommonXMLConstants.LastKnownStatePath);
         CommonMethods.CreateLastKnownStateFile(parentPath);
         CommonMethods.LoadXML(ref xmlTodoDoc, todoPath);
         CommonMethods.DoFolderLastKnownCleanUp(xmlTodoDoc, folder.Name);
         AppendActionFolderLastKnownState(xmlTodoDoc, folder, CommonXMLConstants.ActionDeleted);
         CommonMethods.SaveXML(ref xmlTodoDoc, todoPath);
     }
 }
Esempio n. 11
0
        // Deletes a folder node in the last known state file by searching for the name 
        private void DeleteFolderLastKnownState(FolderCompareObject folder, int counter)
        {
            string todoXMLPath = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.LastKnownStatePath);

            if (!File.Exists(todoXMLPath))
                return;

            XmlDocument todoXMLDoc = new XmlDocument();
            CommonMethods.LoadXML(ref todoXMLDoc, todoXMLPath);
            XmlNode folderNode = todoXMLDoc.SelectSingleNode(CommonXMLConstants.XPathLastKnownState + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");

            if (folderNode != null)
                folderNode.ParentNode.RemoveChild(folderNode);

            CommonMethods.SaveXML(ref todoXMLDoc, todoXMLPath);
        }
Esempio n. 12
0
        // Based on the folder and counter , it will load the xml document and delete the folder node
        private void DeleteFolderObject(FolderCompareObject folder, int counter)
        {
            XmlDocument xmlDoc = new XmlDocument();
            string xmlPath = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath);

            if (File.Exists(xmlPath))
            {
                CommonMethods.LoadXML(ref xmlDoc, xmlPath);
                XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");

                if (node == null)
                    return;

                node.ParentNode.RemoveChild(node);
                CommonMethods.SaveXML(ref xmlDoc, xmlPath);
            }
        }
Esempio n. 13
0
        // Based on the folder and counter , it will load the xml document and rename it
        private void RenameFolderObject(FolderCompareObject folder, int counter)
        {
            if (Directory.Exists(Path.Combine(folder.GetSmartParentPath(counter), folder.Name)))
            {
                XmlDocument xmlDoc = new XmlDocument();
                string xmlPath = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath);
                CommonMethods.LoadXML(ref xmlDoc, xmlPath);
                XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");

                if (node == null)
                {
                    CreateFolderObject(folder, counter, true);
                    return;
                }

                node.FirstChild.InnerText = folder.NewName;
                node.LastChild.InnerText = _dateTime.ToString();
                CommonMethods.SaveXML(ref xmlDoc, xmlPath);
            }
            else
            {
                XmlDocument newXmlDoc = new XmlDocument();
                string editOldXML = Path.Combine(Path.Combine(folder.GetSmartParentPath(counter), folder.NewName), CommonXMLConstants.MetadataPath);
                CommonMethods.CreateFileIfNotExist(Path.Combine(folder.GetSmartParentPath(counter), folder.NewName));
                CommonMethods.LoadXML(ref newXmlDoc, editOldXML);
                XmlNode xmlNameNode = newXmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathName);

                if (xmlNameNode != null)
                    xmlNameNode.InnerText = folder.NewName;

                CommonMethods.SaveXML(ref newXmlDoc, editOldXML);
                string parentXML = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath);
                XmlDocument parentXmlDoc = new XmlDocument();
                CommonMethods.CreateFileIfNotExist(folder.GetSmartParentPath(counter));
                CommonMethods.LoadXML(ref parentXmlDoc, parentXML);
                XmlNode parentXmlFolderNode = parentXmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr + CommonXMLConstants.XPathFolder + "[name=" + CommonMethods.ParseXPathString(folder.Name) + "]");

                if (parentXmlFolderNode != null)
                {
                    parentXmlFolderNode.FirstChild.InnerText = folder.NewName;
                    parentXmlFolderNode.LastChild.InnerText = _dateTime.ToString();
                }

                CommonMethods.SaveXML(ref parentXmlDoc, Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath));
            }
        }
Esempio n. 14
0
        // Based on the folder and counter , it will create a folder node in the meta data
        private void CreateFolderObject(FolderCompareObject folder, int counter, bool useNewName)
        {
            string name = useNewName ? folder.NewName : folder.Name;
            XmlDocument xmlDoc = new XmlDocument();
            string xmlPath = Path.Combine(folder.GetSmartParentPath(counter), CommonXMLConstants.MetadataPath);
            CommonMethods.CreateFileIfNotExist(folder.GetSmartParentPath(counter));
            CommonMethods.LoadXML(ref xmlDoc, xmlPath);
            CommonMethods.DoFolderCleanUp(xmlDoc, name);
            XmlText nameText = xmlDoc.CreateTextNode(name);
            XmlText lastUpdatedUtcText = xmlDoc.CreateTextNode(_dateTime.ToString());
            XmlElement nameElement = xmlDoc.CreateElement(CommonXMLConstants.NodeName);
            XmlElement lastUpdatedElement = xmlDoc.CreateElement(CommonXMLConstants.NodeLastUpdatedUtc);
            XmlElement folderElement = xmlDoc.CreateElement(CommonXMLConstants.NodeFolder);

            nameElement.AppendChild(nameText);
            lastUpdatedElement.AppendChild(lastUpdatedUtcText);
            folderElement.AppendChild(nameElement);
            folderElement.AppendChild(lastUpdatedElement);

            XmlNode node = xmlDoc.SelectSingleNode(CommonXMLConstants.XPathExpr);

            if (node == null)
                return;
            node.AppendChild(folderElement);

            string subFolderXML = Path.Combine(folder.GetSmartParentPath(counter), name);
            CommonMethods.CreateFileIfNotExist(subFolderXML);
            CommonMethods.SaveXML(ref xmlDoc, xmlPath);
            ModifyFolderName(folder, folder.GetSmartParentPath(counter));
            DeleteFolderLastKnownState(folder, counter);
        }
Esempio n. 15
0
        /// <summary>
        /// For every FolderCompareObject, check which path has the max priority and then decide
        /// what sync operation should be approriate for each FolderCompareObject. Once the path of the max
        /// priority has been set, for every other path which is not the max priority, populate the datagrid
        /// with by adding a DataRow for each operation that needs to be done.
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="numOfPaths"></param>
        public void Visit(FolderCompareObject folder, int numOfPaths)
        {
            if (folder.Invalid)
                return;

            int maxPriorityPos = folder.SourcePosition;

            if (folder.Priority[maxPriorityPos] > 0)
            {
                for (int i = 0; i < numOfPaths; i++)
                {
                    if (i != maxPriorityPos && folder.Priority[i] != folder.Priority[maxPriorityPos])
                    {
                        string operation = string.Empty, source = string.Empty, dest = string.Empty, tooltip = string.Empty;

                        DataRow row = SyncData.NewRow();

                        switch (folder.ChangeType[maxPriorityPos])
                        {
                            // if the MetaChangeType is of New, Update, Change
                            case MetaChangeType.New:
                            case MetaChangeType.NoChange:
                            case MetaChangeType.Update:
                                source = Path.Combine(folder.GetSmartParentPath(maxPriorityPos), folder.Name);
                                dest = Path.Combine(folder.GetSmartParentPath(i), folder.Name);
                                operation = CopyArrow;
                                tooltip = folder.Exists[i] ? FolderUpdateToolTip : FolderCopyToolTip;
                                break;
                            // if the MetaChangeType is of Delete type
                            case MetaChangeType.Delete:
                                source = Path.Combine(folder.GetSmartParentPath(maxPriorityPos), folder.Name);
                                dest = Path.Combine(folder.GetSmartParentPath(i), folder.Name);
                                operation = DeleteArrow;
                                tooltip = FolderDeleteToolTip;
                                break;
                            // if the MetaChangeType is of Rename type
                            case MetaChangeType.Rename:
                                string oldFolderName = Path.Combine(folder.GetSmartParentPath(i), folder.Name);
                                source = File.Exists(oldFolderName) ? oldFolderName : Path.Combine(folder.GetSmartParentPath(maxPriorityPos), folder.NewName);
                                dest = Path.Combine(folder.GetSmartParentPath(i), folder.NewName);
                                operation = Directory.Exists(oldFolderName) ? RenameArrow : CopyArrow;
                                tooltip = Directory.Exists(oldFolderName) ? FolderRenameToolTip : FolderCopyToolTip;
                                break;
                        }

                        // set all properties of the DataRow which has been decided by the switch case above
                        row[Source] = source;
                        row[Dest] = dest;
                        row[Operation] = operation;
                        row[Tooltip] = tooltip;
                        row[SourceIcon] = FolderIcon;
                        row[DestIcon] = FolderIcon;

                        // Set size and date/time for Folder to none
                        row[SourceLastModifiedDate] = "-";
                        row[SourceSize] = "-";
                        row[DestLastModifiedDate] = "-";
                        row[DestSize] = "-";
                        _syncData.Rows.Add(row);
                    }
                }

            }
        }