private void update(int reset = 0) { if (reset == 1) { _data.Text = ""; _data.Line = ""; _data.Anchor = 1; _data.Active = 1; _data.LineBegOffset = 1; _data.LineEndOffset = 1; } else { TextSelection selection = App.ActiveDocument.Selection as TextSelection; VirtualPoint active = selection.ActivePoint; VirtualPoint anchor = selection.AnchorPoint; EditPoint point0 = active.CreateEditPoint(); EditPoint point1 = active.CreateEditPoint(); point0.StartOfLine(); point1.EndOfLine(); _data.Line = point0.GetText(point1); _data.LineBegOffset = toPosition(point0.AbsoluteCharOffset); _data.LineEndOffset = toPosition(point1.AbsoluteCharOffset); point0.StartOfDocument(); point1.EndOfDocument(); _data.Text = point0.GetText(point1); _data.Anchor = toPosition(anchor.AbsoluteCharOffset); _data.Active = toPosition(active.AbsoluteCharOffset); } }
/// <summary> /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event args.</param> private void Execute(object sender, EventArgs e) { // Verify the current thread is the UI thread. ThreadHelper.ThrowIfNotOnUIThread(); string name = FormatXmlCommand.environment.ActiveDocument.FullName; string extension = Path.GetExtension(FormatXmlCommand.environment.ActiveDocument.FullName).ToUpperInvariant(); if (FormatXmlCommand.XmlExtensions.Contains(extension)) { // Get the selected text from the environment and make a note of where we are in the document. TextSelection selection = FormatXmlCommand.environment.ActiveDocument.Selection as TextSelection; int line = selection.ActivePoint.Line; int offset = selection.ActivePoint.LineCharOffset; // Get the start end points (round down and up to the start of a line). EditPoint startPoint = selection.AnchorPoint.CreateEditPoint(); startPoint.StartOfDocument(); // The initial endpoint is one line below the start. EditPoint endPoint = selection.ActivePoint.CreateEditPoint(); endPoint.EndOfDocument(); // This will replace the XML in the current module with the scrubbed and beautified XML. startPoint.ReplaceText( endPoint, FormatXmlCommand.ScrubDocument(startPoint.GetText(endPoint), extension), (int)(vsEPReplaceTextOptions.vsEPReplaceTextNormalizeNewlines | vsEPReplaceTextOptions.vsEPReplaceTextTabsSpaces)); // There will likely be some dramatic changes to the format of the document. This will restore the cursor to where it was in the // document before we beautified it. selection.MoveToLineAndOffset(line, offset); } }
public void PasteCode(EditPoint objEditPt, string code, PasteOptions pasteOption) { switch (pasteOption) { case PasteOptions.Overwrite: objEditPt.Delete(code.Length); break; case PasteOptions.Append: objEditPt.EndOfDocument(); break; } objEditPt.Insert(code); }
private void InsertGeneratedMethod(ProjectItem projectItem, string content) { projectItem.Open(Constants.vsViewKindCode); projectItem.Document.ActiveWindow.Activate(); TextDocument textDocument = (TextDocument)projectItem.Document.Object("TextDocument"); EditPoint editPoint = textDocument.EndPoint.CreateEditPoint(); editPoint.EndOfDocument(); editPoint.StartOfLine(); editPoint.LineUp(1); editPoint.Insert(content + "\n"); projectItem.Save(Helper.GetFilePath(projectItem)); }
/// <summary> /// Help method to modify KeyinCommands.cs or KeyinCommands.vb. /// </summary> /// <param name="projectItem"></param> /// <param name="keyinCommandFunctionCS"></param> /// <param name="keyinCommandFunctionvb"></param> private void ModifyKeyinsCommands(ProjectItem projectItem, string keyinCommandFunctionCS, string keyinCommandFunctionvb, string keyinCommandFunctionCPP) { Document activeDoc = projectItem.Document; if (activeDoc == null) { return; } ProjectItem activeDocumentProjectItem = activeDoc.ProjectItem; if (activeDocumentProjectItem == null) { return; } FileCodeModel fileCodeModel = activeDocumentProjectItem.FileCodeModel; if (fileCodeModel == null) { return; } CodeElements codeElements = fileCodeModel.CodeElements; CodeClass codeClass = null; // look for the namespace in the active document CodeNamespace codeNamespace = null; foreach (CodeElement codeElement in codeElements) { if (codeElement.Kind == vsCMElement.vsCMElementNamespace) { codeNamespace = codeElement as CodeNamespace; break; } } if (codeNamespace == null) { if (IsVBProject) { codeElements = fileCodeModel.CodeElements; } else { return; } } else { codeElements = codeNamespace.Members; } if (codeElements == null) { return; } // look for the first class foreach (CodeElement codeElement in codeElements) { if (codeElement.Kind == vsCMElement.vsCMElementClass) { codeClass = codeElement as CodeClass; break; } } if (codeClass == null) { return; } if (IsCSProject) { CodeFunction codeFunction = codeClass.AddFunction(FunctionName + "Keyin", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessPublic); codeFunction.AddParameter("unparsed", vsCMTypeRef.vsCMTypeRefString, -1); TextPoint textPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartBody); EditPoint editPoint = textPoint.CreateEditPoint(); EditPoint objMovePt = textPoint.CreateEditPoint(); EditPoint UtilEditPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint(); UtilEditPoint.ReplaceText(6, "public static", 0); editPoint.Insert ( keyinCommandFunctionCS ); editPoint.StartOfDocument(); objMovePt.EndOfDocument(); editPoint.SmartFormat(objMovePt); } else if (IsVBProject) { CodeFunction codeFunction = codeClass.AddFunction(FunctionName + "Keyin", vsCMFunction.vsCMFunctionSub, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessPublic); codeFunction.AddParameter("unparsed", vsCMTypeRef.vsCMTypeRefString, -1); TextPoint textPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartBody); EditPoint editPoint = textPoint.CreateEditPoint(); EditPoint objMovePt = textPoint.CreateEditPoint(); EditPoint UtilEditPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint(); UtilEditPoint.ReplaceText(6, "Public Shared", 0); editPoint.Insert ( keyinCommandFunctionvb ); editPoint.StartOfDocument(); objMovePt.EndOfDocument(); editPoint.SmartFormat(objMovePt); } else if (IsVCProject) { TextDocument editDoc = (TextDocument)activeDoc.Object("TextDocument"); EditPoint objEditPt = editDoc.CreateEditPoint(); EditPoint objMovePt = editDoc.EndPoint.CreateEditPoint(); objEditPt.StartOfDocument(); activeDoc.ReadOnly = false; if (objEditPt.FindPattern("#include")) { objEditPt.LineDown(1); objEditPt.Insert("#include \"" + FunctionName + ".h\"\n"); } else if ((objEditPt.FindPattern("#using"))) { objEditPt.LineUp(1); objEditPt.Insert("#include \"" + FunctionName + ".h\"\n"); } else { objEditPt.FindPattern("namespace"); objEditPt.LineUp(1); objEditPt.Insert("#include \"" + FunctionName + ".h\"\n"); } CodeFunction codeFunction = codeClass.AddFunction(FunctionName + "Keyin", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessPublic); codeFunction.AddParameter("unparsed", "System::String^", -1); TextPoint textPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartBody); EditPoint editPoint = textPoint.CreateEditPoint(); objMovePt = textPoint.CreateEditPoint(); EditPoint UtilEditPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint(); UtilEditPoint.ReplaceText(4, "public:static", 0); editPoint.Insert(keyinCommandFunctionCPP); if (objEditPt.FindPattern("throw gcnew System::NotImplementedException();")) { editPoint.Delete(52); } editPoint.StartOfDocument(); objMovePt.EndOfDocument(); editPoint.SmartFormat(objMovePt); } }
/// <summary> /// Generates the specified Source File in the received Project with the options /// provided and gets the Namespace ready to add code in it. /// </summary> /// <param name="targetProject">Project where the Source File is going to be placed.</param> /// <param name="targetProjectFolder">Project folder where the source file is going to be placed. /// Null indicates to place the source file as child of targetProject.</param> /// <param name="sourceFileName">Source File name to use.</param> /// <param name="sourceFileHeaderComment">Source File Header Comment (optional).</param> /// <param name="sourceNamespace">Namespace used in the Source File.</param> /// <param name="isServiceReady">Specifies if it is Service-Ready (serialization is going to be used).</param> /// <param name="sourceFileItem">(out parameter) Source File ProjectItem.</param> /// <returns></returns> public static CodeNamespace GenerateSourceAndGetNamespace(Project targetProject, ProjectItem targetProjectFolder, string sourceFileName, string sourceFileHeaderComment, string sourceNamespace, bool isServiceReady, out ProjectItem sourceFileItem) { // Validate source file name if (sourceFileName.EndsWith(Resources.CSharpFileExtension) == false) { sourceFileName += Resources.CSharpFileExtension; } // Validate source file header comment if (string.IsNullOrWhiteSpace(sourceFileHeaderComment) == false) { if (sourceFileHeaderComment.IndexOf("*/") >= 0) { throw new ApplicationException(Resources.Error_HeaderCommentInvalidChars); } } // ProjectItems collection where to place the source file ProjectItems projectItems = targetProject.ProjectItems; if (targetProjectFolder != null) { // Place inside received project folder projectItems = targetProjectFolder.ProjectItems; } // Properties collection of the target EnvDTE.Properties targetProperties = targetProject.Properties; if (targetProjectFolder != null) { targetProperties = targetProjectFolder.Properties; } // Source file sourceFileItem = null; #region If source file exists in the target, clear it and get the reference foreach (ProjectItem projItem in projectItems) { string projItemFileName = projItem.Properties.Item(Resources.ProjectItem_FileName).Value.ToString(); if (sourceFileName.ToLower() == projItemFileName.ToLower()) { // Source file already exists sourceFileItem = projItem; if (sourceFileItem.FileCodeModel.CodeElements != null && sourceFileItem.FileCodeModel.CodeElements.Count > 0) { // Clear source file CodeElement firstElement = sourceFileItem.FileCodeModel.CodeElements.Item(1); CodeElement lastElement = sourceFileItem.FileCodeModel.CodeElements.Item( sourceFileItem.FileCodeModel.CodeElements.Count); EditPoint startPoint = firstElement.StartPoint.CreateEditPoint(); EditPoint endPoint = lastElement.EndPoint.CreateEditPoint(); while (startPoint.AtStartOfDocument != true) { startPoint.LineUp(); } while (endPoint.AtEndOfDocument != true) { endPoint.LineDown(); } startPoint.Delete(endPoint); } break; } } #endregion #region If source file NOT exists in the target, create it and get the reference if (sourceFileItem == null) { // New source file, get target path string targetPath = targetProperties.Item(Resources.Properties_LocalPath).Value.ToString(); // Check if the new source file already exists in the file system (and it is not added to the solution) if (File.Exists(targetPath + sourceFileName)) { // Rename the existent source file string backupSourceFileName = (sourceFileName + Resources.BackupFileExtension); File.Move((targetPath + sourceFileName), (targetPath + backupSourceFileName)); // Add warning VisualStudioHelper.AddToErrorList(TaskErrorCategory.Warning, string.Format(Resources.Warning_SourceFileAlreadyExists, sourceFileName, backupSourceFileName), targetProject, sourceFileName, null, null); } // Add source file to target sourceFileItem = projectItems.AddFromTemplate(TemplateClass.FilePath, sourceFileName); } #endregion #region Generate imports var importList = new List <SourceCodeImport>(); importList.Add(new SourceCodeImport(Resources.NamespaceSystem)); importList.Add(new SourceCodeImport(Resources.NamespaceSystemCollectionsGeneric)); importList.Add(new SourceCodeImport(Resources.NamespaceSystemText)); if (isServiceReady) { importList.Add(new SourceCodeImport(Resources.NamespaceSystemRuntimeSerialization)); } importList = importList.OrderBy(d => d.ImportNamespace).ToList(); #endregion Generate imports // Add imports to the source code VisualStudioHelper.AddImportsToSourceCode(ref sourceFileItem, importList); // Get Source file code start EditPoint objEditPoint = sourceFileItem.FileCodeModel.CodeElements.Item(1).StartPoint.CreateEditPoint(); objEditPoint.StartOfDocument(); // Add header comment if (string.IsNullOrWhiteSpace(sourceFileHeaderComment) == false) { sourceFileHeaderComment = (Environment.NewLine + sourceFileHeaderComment + Environment.NewLine); objEditPoint.Insert( string.Format(Resources.CSharpCommentMultiline, sourceFileHeaderComment) + Environment.NewLine); } // Add EntitiesToDTOs signature string timestamp = DateTime.Now.ToString("yyyy/MM/dd - HH:mm:ss"); objEditPoint.Insert(string.Format(Resources.EntitiesToDTOsSignature, AssemblyHelper.Version, timestamp)); objEditPoint.Insert(Environment.NewLine); // Add blank line before source file namespace objEditPoint.EndOfDocument(); objEditPoint.Insert(Environment.NewLine); // Add namespace CodeNamespace objNamespace = sourceFileItem.FileCodeModel .AddNamespace(sourceNamespace, AppConstants.PLACE_AT_THE_END); return(objNamespace); }
/// <summary> /// Generates Assemblers for DTOs generated using the parameters received. /// </summary> /// <param name="parameters">Parameters for the generation of Assemblers.</param> /// <param name="worker">BackgroundWorker reference.</param> public static void GenerateAssemblers(GenerateAssemblersParams parameters, BackgroundWorker worker) { LogManager.LogMethodStart(); // Variables EditPoint objEditPoint; // EditPoint to reuse CodeNamespace objNamespace = null; // Namespace item to add Classes ProjectItem sourceFileItem = null; // Source File Item to save int assemblersGenerated = 0; // Report Progress worker.ReportProgress(0, new GeneratorOnProgressEventArgs(0, string.Format(Resources.Text_AssemblersGenerated, assemblersGenerated, parameters.EntitiesDTOs.Count))); // Add Reference to System.Core VisualStudioHelper.AddReferenceToProject(parameters.TargetProject, Resources.AssemblySystemCore, Resources.AssemblySystemCore); // Add Reference to System.Data.Entity VisualStudioHelper.AddReferenceToProject(parameters.TargetProject, Resources.AssemblySystemDataEntity, Resources.AssemblySystemDataEntity); if (parameters.IsServiceReady) { // Add Reference to System.Runtime.Serialization VisualStudioHelper.AddReferenceToProject(parameters.TargetProject, Resources.AssemblySystemRuntimeSerialization, Resources.AssemblySystemRuntimeSerialization); } if (parameters.TargetProject.UniqueName != parameters.EDMXProject.UniqueName) { // Add Reference to EDMX Project VisualStudioHelper.AddReferenceToProject(parameters.TargetProject, parameters.EDMXProject); } if (parameters.TargetProject.UniqueName != parameters.DTOsTargetProject.UniqueName) { // Add Reference to DTOs Project VisualStudioHelper.AddReferenceToProject(parameters.TargetProject, parameters.DTOsTargetProject); } // Check Cancellation Pending if (GeneratorManager.CheckCancellationPending()) { return; } // Create Template Class File TemplateClass.CreateFile(); // Imports to add to the Source File var importList = new List <SourceCodeImport>(); importList.Add(new SourceCodeImport(Resources.AssemblySystemLinq)); if (parameters.SourceNamespace != parameters.DTOsNamespace) { // Add import of DTOs namespace importList.Add(new SourceCodeImport(parameters.DTOsNamespace)); } if (parameters.SourceNamespace != parameters.EntitiesNamespace) { // Add import of Entities namespace importList.Add(new SourceCodeImport(parameters.EntitiesNamespace)); } // Generate Source File if type is One Source File if (parameters.SourceFileGenerationType == SourceFileGenerationType.OneSourceFile) { sourceFileItem = null; // Generate Source and Get the Namespace item objNamespace = VisualStudioHelper.GenerateSourceAndGetNamespace( parameters.TargetProject, parameters.TargetProjectFolder, parameters.SourceFileName, parameters.SourceFileHeaderComment, parameters.SourceNamespace, parameters.IsServiceReady, out sourceFileItem); // Add import of System.Data.Objects.DataClasses (necessary for AssemblerBase) importList.Add(new SourceCodeImport(Resources.AssemblySystemDataObjectsDataClasses)); // Add Imports to Source File VisualStudioHelper.AddImportsToSourceCode(ref sourceFileItem, importList); } // Check Cancellation Pending if (GeneratorManager.CheckCancellationPending()) { return; } // Set Assembler for all DTOs foreach (DTOEntity dto in parameters.EntitiesDTOs) { dto.SetAssembler(parameters); } // Check Cancellation Pending if (GeneratorManager.CheckCancellationPending()) { return; } // Loop through Entities DTOs foreach (DTOEntity dto in parameters.EntitiesDTOs) { if (dto.IsAbstract == true && (dto.DTOChilds == null || dto.DTOChilds.Count == 0)) { // DTO is abstract and does not have childs // Get the source file name if it is one source file generation type string sourceFileName = null; if (parameters.SourceFileGenerationType == SourceFileGenerationType.OneSourceFile) { sourceFileName = sourceFileItem.Name; } // Add warning VisualStudioHelper.AddToErrorList(TaskErrorCategory.Warning, string.Format(Resources.Warning_AbstractWithoutChildsAssembler, dto.Name), parameters.TargetProject, sourceFileName, null, null); } else { // Generate Source File if type is Source File per Class if (parameters.SourceFileGenerationType == SourceFileGenerationType.SourceFilePerClass) { sourceFileItem = null; // Generate Source and Get the Namespace item objNamespace = VisualStudioHelper.GenerateSourceAndGetNamespace( parameters.TargetProject, parameters.TargetProjectFolder, dto.Assembler.Name, parameters.SourceFileHeaderComment, parameters.SourceNamespace, parameters.IsServiceReady, out sourceFileItem); // Add Imports to Source File VisualStudioHelper.AddImportsToSourceCode(ref sourceFileItem, importList); } // Instance creation code variables var toDTOInstanceCode = string.Empty; var toEntityInstanceCode = string.Empty; #region Generate instance creation code if (dto.DTOChilds != null && dto.DTOChilds.Count > 0) { bool firstIfStatement = true; // Add null assignment for DTO instance code toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceNull, dto.NameDTO); toDTOInstanceCode += Environment.NewLine + Environment.NewLine; // Add null assignment for Entity instance code toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceNull, dto.Name); toEntityInstanceCode += Environment.NewLine + Environment.NewLine; // Loop DTO Child classes foreach (DTOEntity dtoChild in dto.DTOChilds) { // Set if statement checking if the entity is of this type, if it is then invoke DTO Assembler ToDTO method if (firstIfStatement) { firstIfStatement = false; toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceIfChild, dtoChild.Name); toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceIfChild, dtoChild.NameDTO); } else { toDTOInstanceCode += Environment.NewLine; toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceElseIfChild, dtoChild.Name); toEntityInstanceCode += Environment.NewLine; toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceElseIfChild, dtoChild.NameDTO); } toDTOInstanceCode += Environment.NewLine; toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceChild, dtoChild.Name); toEntityInstanceCode += Environment.NewLine; toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceChild, dtoChild.NameDTO); } if (dto.IsAbstract == false) { toDTOInstanceCode += Environment.NewLine + Resources.AssemblerToDTOInstanceElse + Environment.NewLine; toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceNew, dto.NameDTO); toEntityInstanceCode += Environment.NewLine + Resources.AssemblerToEntityInstanceElse + Environment.NewLine; toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceNew, dto.Name); } } else if (dto.IsAbstract == false) { // No childs, simple DTO instance toDTOInstanceCode = Resources.CSharpCodeVar + Resources.Space; toDTOInstanceCode += string.Format(Resources.AssemblerToDTOInstanceNew, dto.NameDTO); toEntityInstanceCode = Resources.CSharpCodeVar + Resources.Space; toEntityInstanceCode += string.Format(Resources.AssemblerToEntityInstanceNew, dto.Name); } #endregion Generate instance creation code // Property assignments code variables var toDTOAssignmentsCode = string.Empty; var toEntityAssignmentsCode = string.Empty; #region Generate property assignments code if ((dto.DTOChilds != null && dto.DTOChilds.Count > 0) || dto.IsAbstract == false) { foreach (var property in dto.Properties) { bool includeAssignments = true; string toDTOAssignment = null; string toEntityAssignment = null; if (property.IsNavigation == true) { // Do not map navigation properties. // The developer has to map navigations manually on partial methods if needed. includeAssignments = false; } else if (property.IsComplex == true) { toDTOAssignment = string.Format(Resources.AssemblerToDTOAssignmentEntityComplexProp, property.PropertyNameEDMX); toEntityAssignment = string.Format(Resources.AssemblerToEntityAssignmentDTOComplexProp, property.PropertyName); } else { toDTOAssignment = string.Format(Resources.AssemblerToDTOAssignmentEntityProp, property.PropertyNameEDMX); toEntityAssignment = string.Format(Resources.AssemblerToEntityAssignmentDTOProp, property.PropertyName); } if (includeAssignments) { // ToDTO assignment => dto.prop = ... toDTOAssignmentsCode += Environment.NewLine + string.Format(Resources.AssemblerToDTOAssignment, property.PropertyName, toDTOAssignment); // ToEntity assignment => entity.prop = ... toEntityAssignmentsCode += Environment.NewLine + string.Format(Resources.AssemblerToEntityAssignment, property.PropertyNameEDMX, toEntityAssignment); } } } #endregion Generate property assignments code // Get the start point where to insert the Assembler code objEditPoint = objNamespace.EndPoint.CreateEditPoint(); objEditPoint.LineUp(); objEditPoint.EndOfLine(); objEditPoint.Insert(Environment.NewLine); // Get the Assembler's code string assemblerCode = dto.Assembler.GetAssemblerCode(toDTOInstanceCode, toEntityInstanceCode, toDTOAssignmentsCode, toEntityAssignmentsCode); // Insert Assembler's code objEditPoint.Insert(assemblerCode); // Format code objEditPoint.StartOfDocument(); EditPoint endOfDocument = objNamespace.EndPoint.CreateEditPoint(); endOfDocument.EndOfDocument(); objEditPoint.SmartFormat(endOfDocument); // Save changes to Source File Item sourceFileItem.Save(); } // END (else) if dto is abstract and does not have childs // Count Assemblers generated assemblersGenerated++; // Report Progress int progress = ((assemblersGenerated * 100) / parameters.EntitiesDTOs.Count); if (progress < 100) { worker.ReportProgress(progress, new GeneratorOnProgressEventArgs(progress, string.Format(Resources.Text_AssemblersGenerated, assemblersGenerated, parameters.EntitiesDTOs.Count))); } // Check Cancellation Pending if (GeneratorManager.CheckCancellationPending()) { return; } } // END Loop through Entities DTOs // Save Target Project parameters.TargetProject.Save(); // Delete Template Class File TemplateClass.Delete(); // Report Progress worker.ReportProgress(100, new GeneratorOnProgressEventArgs(100, string.Format(Resources.Text_AssemblersGenerated, assemblersGenerated, parameters.EntitiesDTOs.Count))); LogManager.LogMethodEnd(); }