commit 5aecb67f5a46f32e0f66248f2cd964da2088a807 Author: zhuxiaojiong <645680426@qq.com> Date: Sat Jun 28 11:05:56 2025 +0800 init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c9a181 --- /dev/null +++ b/.gitignore @@ -0,0 +1,242 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/DBCHM_Sln.sln b/DBCHM_Sln.sln new file mode 100644 index 0000000..7b25e20 --- /dev/null +++ b/DBCHM_Sln.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29324.140 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBCHM", "DBChm\DBCHM.csproj", "{55B00DB1-BA55-47B7-BD44-35C0FCD05FD5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MJTop.Data", "MJTop.Data\MJTop.Data.csproj", "{3D36CDC9-E989-465B-A9F1-AD85DC42F242}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocTools", "DocTools\DocTools.csproj", "{130A8861-0C39-4933-9DE8-AA9525488211}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {55B00DB1-BA55-47B7-BD44-35C0FCD05FD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55B00DB1-BA55-47B7-BD44-35C0FCD05FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55B00DB1-BA55-47B7-BD44-35C0FCD05FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55B00DB1-BA55-47B7-BD44-35C0FCD05FD5}.Release|Any CPU.Build.0 = Release|Any CPU + {3D36CDC9-E989-465B-A9F1-AD85DC42F242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D36CDC9-E989-465B-A9F1-AD85DC42F242}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D36CDC9-E989-465B-A9F1-AD85DC42F242}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D36CDC9-E989-465B-A9F1-AD85DC42F242}.Release|Any CPU.Build.0 = Release|Any CPU + {130A8861-0C39-4933-9DE8-AA9525488211}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {130A8861-0C39-4933-9DE8-AA9525488211}.Debug|Any CPU.Build.0 = Debug|Any CPU + {130A8861-0C39-4933-9DE8-AA9525488211}.Release|Any CPU.ActiveCfg = Release|Any CPU + {130A8861-0C39-4933-9DE8-AA9525488211}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6EF98F13-0310-44EE-AF0C-6A033D4E8EB1} + EndGlobalSection +EndGlobal diff --git a/DBChm/AboutBox.Designer.cs b/DBChm/AboutBox.Designer.cs new file mode 100644 index 0000000..6b54c86 --- /dev/null +++ b/DBChm/AboutBox.Designer.cs @@ -0,0 +1,196 @@ +namespace DBCHM +{ + partial class AboutBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutBox)); + this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.logoPictureBox = new System.Windows.Forms.PictureBox(); + this.labelProductName = new System.Windows.Forms.Label(); + this.labelVersion = new System.Windows.Forms.Label(); + this.labelCopyright = new System.Windows.Forms.Label(); + this.labelCompanyName = new System.Windows.Forms.Label(); + this.textBoxDescription = new System.Windows.Forms.TextBox(); + this.okButton = new System.Windows.Forms.Button(); + this.tableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel + // + this.tableLayoutPanel.ColumnCount = 2; + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F)); + this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0); + this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 0); + this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1); + this.tableLayoutPanel.Controls.Add(this.labelCopyright, 1, 2); + this.tableLayoutPanel.Controls.Add(this.labelCompanyName, 1, 3); + this.tableLayoutPanel.Controls.Add(this.textBoxDescription, 1, 4); + this.tableLayoutPanel.Controls.Add(this.okButton, 1, 5); + this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel.Location = new System.Drawing.Point(9, 8); + this.tableLayoutPanel.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.tableLayoutPanel.Name = "tableLayoutPanel"; + this.tableLayoutPanel.RowCount = 6; + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + this.tableLayoutPanel.Size = new System.Drawing.Size(417, 246); + this.tableLayoutPanel.TabIndex = 0; + // + // logoPictureBox + // + this.logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.logoPictureBox.Image = ((System.Drawing.Image)(resources.GetObject("logoPictureBox.Image"))); + this.logoPictureBox.Location = new System.Drawing.Point(3, 2); + this.logoPictureBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.logoPictureBox.Name = "logoPictureBox"; + this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 6); + this.logoPictureBox.Size = new System.Drawing.Size(131, 242); + this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.logoPictureBox.TabIndex = 12; + this.logoPictureBox.TabStop = false; + // + // labelProductName + // + this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelProductName.Location = new System.Drawing.Point(143, 0); + this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelProductName.MaximumSize = new System.Drawing.Size(0, 16); + this.labelProductName.Name = "labelProductName"; + this.labelProductName.Size = new System.Drawing.Size(271, 16); + this.labelProductName.TabIndex = 19; + this.labelProductName.Text = "DBCHM"; + this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelVersion + // + this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelVersion.Location = new System.Drawing.Point(143, 24); + this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelVersion.MaximumSize = new System.Drawing.Size(0, 16); + this.labelVersion.Name = "labelVersion"; + this.labelVersion.Size = new System.Drawing.Size(271, 16); + this.labelVersion.TabIndex = 0; + this.labelVersion.Text = "Version 1.0.0.0"; + this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelCopyright + // + this.labelCopyright.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelCopyright.Location = new System.Drawing.Point(143, 48); + this.labelCopyright.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 16); + this.labelCopyright.Name = "labelCopyright"; + this.labelCopyright.Size = new System.Drawing.Size(271, 16); + this.labelCopyright.TabIndex = 21; + this.labelCopyright.Text = "Copyright 2018 , lztkdr"; + this.labelCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelCompanyName + // + this.labelCompanyName.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelCompanyName.Location = new System.Drawing.Point(143, 72); + this.labelCompanyName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 16); + this.labelCompanyName.Name = "labelCompanyName"; + this.labelCompanyName.Size = new System.Drawing.Size(271, 16); + this.labelCompanyName.TabIndex = 22; + this.labelCompanyName.Text = "QQȺ132941648"; + this.labelCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // textBoxDescription + // + this.textBoxDescription.Cursor = System.Windows.Forms.Cursors.Hand; + this.textBoxDescription.Dock = System.Windows.Forms.DockStyle.Fill; + this.textBoxDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.textBoxDescription.ForeColor = System.Drawing.SystemColors.ActiveCaption; + this.textBoxDescription.Location = new System.Drawing.Point(143, 98); + this.textBoxDescription.Margin = new System.Windows.Forms.Padding(6, 2, 3, 2); + this.textBoxDescription.Multiline = true; + this.textBoxDescription.Name = "textBoxDescription"; + this.textBoxDescription.ReadOnly = true; + this.textBoxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.textBoxDescription.Size = new System.Drawing.Size(271, 119); + this.textBoxDescription.TabIndex = 23; + this.textBoxDescription.TabStop = false; + this.textBoxDescription.Text = "⣬Ҫ"; + this.textBoxDescription.Click += new System.EventHandler(this.textBoxDescription_Click); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.okButton.Location = new System.Drawing.Point(339, 222); + this.okButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 22); + this.okButton.TabIndex = 24; + this.okButton.Text = "&OK"; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // AboutBox + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.okButton; + this.ClientSize = new System.Drawing.Size(435, 262); + this.Controls.Add(this.tableLayoutPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutBox"; + this.Padding = new System.Windows.Forms.Padding(9, 8, 9, 8); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "AboutBox"; + this.tableLayoutPanel.ResumeLayout(false); + this.tableLayoutPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.PictureBox logoPictureBox; + private System.Windows.Forms.Label labelProductName; + private System.Windows.Forms.Label labelVersion; + private System.Windows.Forms.Label labelCopyright; + private System.Windows.Forms.Label labelCompanyName; + private System.Windows.Forms.TextBox textBoxDescription; + private System.Windows.Forms.Button okButton; + } +} diff --git a/DBChm/AboutBox.cs b/DBChm/AboutBox.cs new file mode 100644 index 0000000..8b02af6 --- /dev/null +++ b/DBChm/AboutBox.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Reflection; +using ComponentFactory.Krypton.Toolkit; + +namespace DBCHM +{ + partial class AboutBox : KryptonForm + { + public AboutBox() + { + InitializeComponent(); + + this.Text = String.Format("About {0}", AssemblyTitle); + this.labelProductName.Text = AssemblyProduct; + this.labelVersion.Text = String.Format("Version {0}", AssemblyVersion); + this.labelCopyright.Text = AssemblyCopyright; + this.labelCompanyName.Text = AssemblyCompany; + } + + #region Assembly Attribute Accessors + + public string AssemblyTitle + { + get + { + // Get all Title attributes on this assembly + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + // If there is at least one Title attribute + if (attributes.Length > 0) + { + // Select the first one + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + // If it is not an empty string, return it + if (titleAttribute.Title != "") + return titleAttribute.Title; + } + // If there was no Title attribute, or if the Title attribute was the empty string, return the .exe name + return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); + } + } + + public string AssemblyVersion + { + get + { + return Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + } + + public string AssemblyDescription + { + get + { + // Get all Description attributes on this assembly + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + // If there aren't any Description attributes, return an empty string + if (attributes.Length == 0) + return ""; + // If there is a Description attribute, return its value + return ((AssemblyDescriptionAttribute)attributes[0]).Description; + } + } + + public string AssemblyProduct + { + get + { + // Get all Product attributes on this assembly + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); + // If there aren't any Product attributes, return an empty string + if (attributes.Length == 0) + return ""; + // If there is a Product attribute, return its value + return ((AssemblyProductAttribute)attributes[0]).Product; + } + } + + public string AssemblyCopyright + { + get + { + // Get all Copyright attributes on this assembly + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + // If there aren't any Copyright attributes, return an empty string + if (attributes.Length == 0) + return ""; + // If there is a Copyright attribute, return its value + return ((AssemblyCopyrightAttribute)attributes[0]).Copyright; + } + } + + public string AssemblyCompany + { + get + { + // Get all Company attributes on this assembly + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + // If there aren't any Company attributes, return an empty string + if (attributes.Length == 0) + return ""; + // If there is a Company attribute, return its value + return ((AssemblyCompanyAttribute)attributes[0]).Company; + } + } + #endregion + + private void okButton_Click(object sender, EventArgs e) + { + this.Dispose(); + } + + private void textBoxDescription_Click(object sender, EventArgs e) + { + System.Diagnostics.Process.Start("http://shang.qq.com/wpa/qunwpa?idkey=43619cbe3b2a10ded01b5354ac6928b30cc91bda45176f89a191796b7a7c0e26"); + } + } +} diff --git a/DBChm/AboutBox.resx b/DBChm/AboutBox.resx new file mode 100644 index 0000000..2153d61 --- /dev/null +++ b/DBChm/AboutBox.resx @@ -0,0 +1,603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + + + \ No newline at end of file diff --git a/DBChm/Common/CommonExtension.cs b/DBChm/Common/CommonExtension.cs new file mode 100644 index 0000000..6373b91 --- /dev/null +++ b/DBChm/Common/CommonExtension.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading.Tasks; + +namespace DBCHM +{ + public static class CommonExtension + { + public static string FormatString(this string s, params object[] args) + { + return string.Format(s, args); + } + + public static void SetHidden(this FileInfo file) + { + if (file != null && file.Exists) + { + file.Attributes = FileAttributes.Hidden; + } + } + + /// + /// 深度复制 + /// + /// + /// + /// + public static T DeepCopy(this T source) + { + if (source == null) + { + return default(T); + } + + using (Stream objectStream = new MemoryStream()) + { + //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制 + IFormatter formatter = new BinaryFormatter(); + formatter.Serialize(objectStream, source); + objectStream.Seek(0, SeekOrigin.Begin); + return (T)formatter.Deserialize(objectStream); + } + } + } +} diff --git a/DBChm/Common/ConfigUtils.cs b/DBChm/Common/ConfigUtils.cs new file mode 100644 index 0000000..8bcdade --- /dev/null +++ b/DBChm/Common/ConfigUtils.cs @@ -0,0 +1,304 @@ +using MJTop.Data; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +namespace DBCHM +{ + /// + /// 管理 配置的连接字符串 + /// + public static class ConfigUtils + { + /// + /// 当前应用程序的名称 + /// + private static string ConfigFileName = Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName).Replace(".vshost", ""); + /// + /// 定义配置存放的路径 + /// + public static string AppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), ConfigFileName); + + /// + /// sqlite db文件的存放路径 + /// + private static string ConfigFilePath = string.Empty; + + /// + /// 针对配置的 数据库操作对象 + /// + private static DB db = null; + + /// + /// 初始化静态数据 + /// 将sqlite数据库写入 C:\Users\用户名\AppData\Local\DBChm 目录中 + /// + static ConfigUtils() + { + try + { + if (!ZetaLongPaths.ZlpIOHelper.DirectoryExists(AppPath)) + { + ZetaLongPaths.ZlpIOHelper.CreateDirectory(AppPath); + } + AddSecurityControll2Folder(AppPath); + ConfigFilePath = Path.Combine(AppPath, ConfigFileName + ".db"); + Init(); + } + catch (Exception ex) + { + LogUtils.LogError("ConfigUtils初始化", Developer.SysDefault, ex); + } + } + + /// + /// 初始化创建配置数据库 + /// + private static void Init() + { + db = DBMgr.UseDB(DBType.SQLite, ConfigFilePath); + + string strSql = @"create table DBCHMConfig + ( + Id integer PRIMARY KEY autoincrement, + Name nvarchar(200) unique, + DBType varchar(30), + Server varchar(100), + Port integer, + DBName varchar(100), + Uid varchar(50), + Pwd varchar(100), + ConnTimeOut integer, + ConnString text, + Modified text + )"; + + //表不存在则创建 连接字符串 配置表 + if (db.Info.TableNames == null || !db.Info.TableNames.Contains(nameof(DBCHMConfig), StringComparer.OrdinalIgnoreCase)) + { + db.ExecSql(strSql); + //执行后,刷新实例 表结构信息 + db.Info.Refresh(); + } + else + { + // v1.7.3.7 版本 增加 连接超时 与 最后连接时间 + var info = db.Info; + if(!info.IsExistColumn(nameof(DBCHMConfig), nameof(DBCHMConfig.Modified))) + { + var configs = db.GetListDictionary("select * from " + nameof(DBCHMConfig)); + + db.Info.DropTable(nameof(DBCHMConfig)); + + db.ExecSql(strSql); + + //执行后,刷新实例 表结构信息 + db.Info.Refresh(); + + if (configs != null && configs.Count > 0) + { + foreach (var config in configs) + { + try + { + db.Insert(config, nameof(DBCHMConfig)); + } + catch (Exception ex) + { + LogUtils.LogError("Init", Developer.SysDefault, ex, config); + } + } + + db.ExecSql("update " + nameof(DBCHMConfig) + " set ConnTimeOut = 120 "); + } + } + } + } + + + + /// + /// 判断磁盘路径下是否安装存在某个文件,最后返回存在某个文件的路径 + /// + /// + /// + /// + public static bool IsInstall(string[] installPaths, out string installPath) + { + installPath = string.Empty; + var driInfos = DriveInfo.GetDrives(); + foreach (DriveInfo dInfo in driInfos) + { + if (dInfo.DriveType == DriveType.Fixed) + { + foreach (string ipath in installPaths) + { + string path = Path.Combine(dInfo.Name, ipath); + if (File.Exists(path)) + { + installPath = path; + return true; + } + } + } + } + return false; + } + + /// + /// 检测是否安装某个软件,并返回软件的卸载安装路径 + /// + /// + /// + /// + public static bool CheckInstall(string softName, string str_exe, out string installPath) + { + //即时刷新注册表 + SHChangeNotify(0x8000000, 0, IntPtr.Zero, IntPtr.Zero); + + installPath = string.Empty; + + bool isFind = false; + var uninstallNode = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall", false); + if (uninstallNode != null) + { + //LocalMachine_64 + using (uninstallNode) + { + foreach (var subKeyName in uninstallNode.GetSubKeyNames()) + { + var subKey = uninstallNode.OpenSubKey(subKeyName); + string displayName = (subKey.GetValue("DisplayName") ?? string.Empty).ToString(); + string path = (subKey.GetValue("UninstallString") ?? string.Empty).ToString(); + Console.WriteLine(displayName); + if (displayName.Contains(softName) && !string.IsNullOrWhiteSpace(path)) + { + installPath = Path.Combine(Path.GetDirectoryName(path), str_exe); + if (File.Exists(installPath)) + { + isFind = true; + break; + } + } + } + } + } + + + if (!isFind) + { + //LocalMachine_32 + uninstallNode = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false); + using (uninstallNode) + { + foreach (var subKeyName in uninstallNode.GetSubKeyNames()) + { + var subKey = uninstallNode.OpenSubKey(subKeyName); + string displayName = (subKey.GetValue("DisplayName") ?? string.Empty).ToString(); + string path = (subKey.GetValue("UninstallString") ?? string.Empty).ToString(); + Console.WriteLine(displayName); + if (displayName.Contains(softName) && !string.IsNullOrWhiteSpace(path)) + { + installPath = Path.Combine(Path.GetDirectoryName(path), str_exe); + if (File.Exists(installPath)) + { + isFind = true; + break; + } + } + } + } + } + return isFind; + } + + [DllImport("shell32.dll")] + + public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2); + + + + /// + ///为文件夹添加users,everyone用户组的完全控制权限 + /// + /// + public static void AddSecurityControll2Folder(string dirPath) + { + //获取文件夹信息 + DirectoryInfo dir = new DirectoryInfo(dirPath); + if (dir.Exists) + { + //获得该文件夹的所有访问权限 + System.Security.AccessControl.DirectorySecurity dirSecurity = dir.GetAccessControl(AccessControlSections.All); + //设定文件ACL继承 + InheritanceFlags inherits = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit; + //添加ereryone用户组的访问权限规则 完全控制权限 + FileSystemAccessRule everyoneFileSystemAccessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow); + //添加Users用户组的访问权限规则 完全控制权限 + FileSystemAccessRule usersFileSystemAccessRule = new FileSystemAccessRule("Users", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow); + bool isModified = false; + dirSecurity.ModifyAccessRule(AccessControlModification.Add, everyoneFileSystemAccessRule, out isModified); + dirSecurity.ModifyAccessRule(AccessControlModification.Add, usersFileSystemAccessRule, out isModified); + //设置访问权限 + dir.SetAccessControl(dirSecurity); + } + } + + /// + /// 添加或修改配置连接 + /// + /// + public static void Save(NameValueCollection dbCHMConfig) + { + db.Save(dbCHMConfig, "DBCHMConfig"); + } + + public static void UpLastModified(int id) + { + db.ExecSql("update DBCHMConfig set Modified='" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "' where id=" + id); + } + + /// + /// 删除连接 + /// + /// + public static void Delete(int id) + { + db.Delete("DBCHMConfig", "Id", id); + } + + /// + /// 查询出所有配置的连接 + /// + /// + public static List SelectAll() + { + return db.GetDataTable("select * from DBCHMConfig order by Modified desc ").ConvertToListObject(); + } + + /// + /// 得到其中1个连接 + /// + /// + /// + public static DBCHMConfig Get(int id) + { + return db.GetDataTable("select * from DBCHMConfig where id = " + id).ConvertToListObject().FirstOrDefault(); + } + + /// + /// 判断配置表是否存在连接字符串 + /// + /// + public static bool HasValue() + { + string strSql = "select count(1) from DBCHMConfig"; + return db.Single(strSql, 0) > 0; + } + + } +} diff --git a/DBChm/Common/DBCHMConfig.cs b/DBChm/Common/DBCHMConfig.cs new file mode 100644 index 0000000..30171cc --- /dev/null +++ b/DBChm/Common/DBCHMConfig.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DBCHM +{ + public class DBCHMConfig + { + [DisplayName("ID")] + public int Id { get; set; } + + [DisplayName("连接名")] + public string Name { get; set; } + + [DisplayName("数据库类型")] + public string DBType { get; set; } + + [DisplayName("主机")] + public string Server { get; set; } + + [DisplayName("端口")] + public int? Port { get; set; } + [DisplayName("数据库")] + public string DBName { get; set; } + + [DisplayName("用户名")] + public string Uid { get; set; } + + [DisplayName("密码")] + public string Pwd { get; set; } + + [DisplayName("连接超时")] + public int? ConnTimeOut { get; set; } + + [DisplayName("连接字符串")] + public string ConnString { get; set; } + + [DisplayName("最后使用时间")] + public DateTime Modified { get; set; } + } +} diff --git a/DBChm/Common/DBUtils.cs b/DBChm/Common/DBUtils.cs new file mode 100644 index 0000000..f89dc5e --- /dev/null +++ b/DBChm/Common/DBUtils.cs @@ -0,0 +1,15 @@ +using MJTop.Data; + +namespace DBCHM +{ + public static class DBUtils + { + + /// + /// 数据库对象 + /// MJTop.Data 已开源。 + /// + public static DB Instance + { get; set; } + } +} diff --git a/DBChm/Common/EnumExtension.cs b/DBChm/Common/EnumExtension.cs new file mode 100644 index 0000000..81c431c --- /dev/null +++ b/DBChm/Common/EnumExtension.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + + +public static class EnumExt + where TEnum : struct +{ + private static readonly Dictionary read_dict_Enums; + private static readonly Dictionary read_dict_Enums_desc; + static EnumExt() + { + read_dict_Enums = new Dictionary(); + read_dict_Enums_desc = new Dictionary(); + Type enumType = typeof(TEnum); + foreach (var val in Enum.GetValues(enumType)) + { + string valstr = val.ToString(); + read_dict_Enums.Add(valstr, (TEnum)val); + + var descAttr = enumType.GetField(valstr).GetCustomAttributes(typeof(DescriptionAttribute), false); + string desc = valstr; + if (descAttr.Length > 0) + { + desc = (descAttr.FirstOrDefault() as DescriptionAttribute).Description; + } + read_dict_Enums_desc.Add(val.ToString(), desc); + } + } + public static Dictionary All() + { + return new Dictionary(read_dict_Enums); + } + + public static string ToDescriptionString(TEnum @this) + { + return read_dict_Enums_desc[@this.ToString()]; + } + public static int ToInt(TEnum @this) + { + return Convert.ToInt32(@this); + } + +} diff --git a/DBChm/Common/Enums.cs b/DBChm/Common/Enums.cs new file mode 100644 index 0000000..74dae62 --- /dev/null +++ b/DBChm/Common/Enums.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DBCHM +{ + /// + /// 操作类型 + /// + public enum OPType + { + 新建, + 编辑, + 删除, + 克隆 + } +} diff --git a/DBChm/Common/FormUtils.cs b/DBChm/Common/FormUtils.cs new file mode 100644 index 0000000..61d76a4 --- /dev/null +++ b/DBChm/Common/FormUtils.cs @@ -0,0 +1,82 @@ +using MJTop.Data; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Windows.Forms; + +namespace DBCHM +{ + public static class FormUtils + { + /// + /// 静态构造函数执行1次,记载 可以处理DBCHM的数据库类型 + /// + static FormUtils() + { + DictDBType = EnumExt.All(); + + DictDBType.Remove(DBType.Oracle.ToString()); + } + /// + /// 所有数据库类型 对应的端口 + /// + public static readonly Dictionary DictPort = new Dictionary() + { + { DBType.SQLite.ToString(),string.Empty}, + { DBType.SqlServer.ToString(),"1433"}, + { DBType.MySql.ToString(),"3306"}, + { DBType.OracleDDTek.ToString(),"1521"}, + //{ DBType.Oracle.ToString(),"1521"}, + { DBType.PostgreSql.ToString(),"5432"}, + { DBType.DB2.ToString(),"50000"}, + }; + + /// + /// 所有数据库类型 + /// + public static Dictionary DictDBType + { + get; private set; + } + + + /// + /// 是否正常的Close + /// + public static bool IsOK_Close { get; set; } = false; + + + public static ProgressArg ProgArg { get; set; } + + + + /// + /// Loading加载 + /// + /// loading提示消息 + /// 当前窗体 this + /// 异步执行方法 + /// 异步执行方法的参数 + public static void ShowProcessing(string msg, Form owner, Action work, object workArg = null) + { + try + { + FrmProcessing processingForm = new FrmProcessing(msg); + dynamic expObj = new ExpandoObject(); + expObj.Form = processingForm; + expObj.WorkArg = workArg; + processingForm.SetWorkAction(work, expObj); + processingForm.ShowDialog(owner); + if (processingForm.WorkException != null) + { + throw processingForm.WorkException; + } + } + catch (System.Exception ex) + { + LogUtils.LogError("FrmProcessing", Developer.SysDefault, ex, msg); + } + } + + } +} diff --git a/DBChm/Common/ProgressArg.cs b/DBChm/Common/ProgressArg.cs new file mode 100644 index 0000000..d25d89c --- /dev/null +++ b/DBChm/Common/ProgressArg.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DBCHM +{ + /// + /// 进度条参数类 + /// + public class ProgressArg + { + public ProgressArg(Action execAct, int maxNum) + { + this.ExecAct = execAct; + this.MaxNum = maxNum; + } + public Action ExecAct { get; set; } + + public int MaxNum { get; set; } + + } +} diff --git a/DBChm/Common/XmlAnalyze.cs b/DBChm/Common/XmlAnalyze.cs new file mode 100644 index 0000000..36f6035 --- /dev/null +++ b/DBChm/Common/XmlAnalyze.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Xml; + +namespace DBCHM.Common +{ + /// + /// 解析VS生成的 XML文档文件 + /// + public class XmlAnalyze + { + public Dictionary, List>> Data { get; set; } = new Dictionary, List>>(); + + public List EntityNames { get; set; } = new List(); + + public Dictionary EntityComments { get; set; } = new Dictionary(); + + private Dictionary EntityXPaths { get; set; } = new Dictionary(); + + public XmlAnalyze(string path) + { + var content = File.ReadAllText(path, Encoding.UTF8); + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(content); + var rootNode = doc.DocumentElement; + + var nodeList = rootNode.SelectNodes("//members/member[starts-with(@name,'T:')]"); + + foreach (XmlNode node in nodeList) + { + var value = node.Attributes["name"]?.Value; + var entityName = value?.Split('.')?.LastOrDefault(); + + //实体名 必须由 字母数字或下划线组成 + if (Regex.IsMatch(entityName, @"^[a-z\d_]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase)) + { + var comment = node?.InnerText?.Trim(); + EntityNames.Add(entityName); + EntityComments.Add(entityName, comment); + + var xpath = value?.Replace("T:", "P:") + "."; + EntityXPaths.Add(entityName, xpath); + } + } + + foreach (var item in EntityXPaths) + { + var nodes = rootNode.SelectNodes($"//members/member[starts-with(@name,'{item.Value}')]"); + + var ecKey = new KeyValuePair(item.Key, EntityComments[item.Key]); + + var lstKV = new List>(); + + foreach (XmlNode node in nodes) + { + var value = node.Attributes["name"]?.Value; + var propName = value?.Split('.')?.LastOrDefault(); + var comment = node.InnerText?.Trim(); + + lstKV.Add(new KeyValuePair(propName, comment)); + } + Data.Add(ecKey, lstKV); + } + } + } +} diff --git a/DBChm/DBCHM.csproj b/DBChm/DBCHM.csproj new file mode 100644 index 0000000..88e5dcb --- /dev/null +++ b/DBChm/DBCHM.csproj @@ -0,0 +1,217 @@ + + + + + Debug + AnyCPU + {55B00DB1-BA55-47B7-BD44-35C0FCD05FD5} + WinExe + Properties + DBCHM + DBCHM + v4.7.2 + 512 + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + CS7035 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + ico\dbchm.ico + + + + False + lib\ComponentFactory.Krypton.Toolkit.dll + + + ..\packages\ICSharpCode.TextEditor.Extended.4.2.4\lib\net45\ICSharpCode.TextEditor.dll + + + + + + + + + + + + + + + ..\packages\ZetaLongPaths.1.0.0.38\lib\net452-full\ZetaLongPaths.dll + + + + + Form + + + AboutBox.cs + + + + + + + + + + + + Form + + + FrmProcessing.cs + + + Form + + + GridFormMgr.cs + + + Form + + + DBForm.cs + + + Form + + + ImportForm.cs + + + + + + + + + + + Form + + + MainForm.cs + + + Component + + + AboutBox.cs + + + FrmProcessing.cs + + + GridFormMgr.cs + + + DBForm.cs + + + ImportForm.cs + + + ResXFileCodeGenerator + Designer + Resources.Designer.cs + + + MainForm.cs + + + Designer + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + {130a8861-0c39-4933-9de8-aa9525488211} + DocTools + + + {3d36cdc9-e989-465b-a9f1-ad85dc42f242} + MJTop.Data + + + + + + + + + + \ No newline at end of file diff --git a/DBChm/DBForm.Designer.cs b/DBChm/DBForm.Designer.cs new file mode 100644 index 0000000..0094c58 --- /dev/null +++ b/DBChm/DBForm.Designer.cs @@ -0,0 +1,387 @@ +namespace DBCHM +{ + partial class DBForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DBForm)); + this.labelControl1 = new System.Windows.Forms.Label(); + this.TxtConnectName = new System.Windows.Forms.TextBox(); + this.labelControl2 = new System.Windows.Forms.Label(); + this.TxtHost = new System.Windows.Forms.TextBox(); + this.labelControl3 = new System.Windows.Forms.Label(); + this.TxtPort = new System.Windows.Forms.TextBox(); + this.labelControl4 = new System.Windows.Forms.Label(); + this.TxtUName = new System.Windows.Forms.TextBox(); + this.labelControl5 = new System.Windows.Forms.Label(); + this.TxtPwd = new System.Windows.Forms.TextBox(); + this.labelControl6 = new System.Windows.Forms.Label(); + this.BtnOk = new System.Windows.Forms.Button(); + this.BtnCancel = new System.Windows.Forms.Button(); + this.cboDBType = new System.Windows.Forms.ComboBox(); + this.labDBName = new System.Windows.Forms.Label(); + this.BtnTestConnect = new System.Windows.Forms.Button(); + this.cboDBName = new System.Windows.Forms.ComboBox(); + this.lblMsg = new System.Windows.Forms.Label(); + this.btnSelectFile = new System.Windows.Forms.Button(); + this.txtConnTimeOut = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.noneSSLCB = new System.Windows.Forms.CheckBox(); + this.requiredSSLCB = new System.Windows.Forms.CheckBox(); + this.sslLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // labelControl1 + // + this.labelControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl1.Location = new System.Drawing.Point(85, 59); + this.labelControl1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl1.Name = "labelControl1"; + this.labelControl1.Size = new System.Drawing.Size(122, 18); + this.labelControl1.TabIndex = 2; + this.labelControl1.Text = "连接名"; + // + // TxtConnectName + // + this.TxtConnectName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.TxtConnectName.Location = new System.Drawing.Point(177, 57); + this.TxtConnectName.Margin = new System.Windows.Forms.Padding(2); + this.TxtConnectName.Name = "TxtConnectName"; + this.TxtConnectName.Size = new System.Drawing.Size(213, 21); + this.TxtConnectName.TabIndex = 0; + // + // labelControl2 + // + this.labelControl2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl2.Location = new System.Drawing.Point(84, 136); + this.labelControl2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl2.Name = "labelControl2"; + this.labelControl2.Size = new System.Drawing.Size(144, 14); + this.labelControl2.TabIndex = 2; + this.labelControl2.Text = "主机名或IP地址"; + // + // TxtHost + // + this.TxtHost.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.TxtHost.Location = new System.Drawing.Point(176, 133); + this.TxtHost.Margin = new System.Windows.Forms.Padding(2); + this.TxtHost.Name = "TxtHost"; + this.TxtHost.Size = new System.Drawing.Size(213, 21); + this.TxtHost.TabIndex = 2; + // + // labelControl3 + // + this.labelControl3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl3.Location = new System.Drawing.Point(84, 173); + this.labelControl3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl3.Name = "labelControl3"; + this.labelControl3.Size = new System.Drawing.Size(100, 18); + this.labelControl3.TabIndex = 2; + this.labelControl3.Text = "端口"; + // + // TxtPort + // + this.TxtPort.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.TxtPort.Location = new System.Drawing.Point(176, 170); + this.TxtPort.Margin = new System.Windows.Forms.Padding(2); + this.TxtPort.Name = "TxtPort"; + this.TxtPort.Size = new System.Drawing.Size(213, 21); + this.TxtPort.TabIndex = 3; + this.TxtPort.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.TxtPort_KeyPress); + // + // labelControl4 + // + this.labelControl4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl4.Location = new System.Drawing.Point(85, 239); + this.labelControl4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl4.Name = "labelControl4"; + this.labelControl4.Size = new System.Drawing.Size(100, 14); + this.labelControl4.TabIndex = 2; + this.labelControl4.Text = "用户名"; + // + // TxtUName + // + this.TxtUName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.TxtUName.Location = new System.Drawing.Point(177, 237); + this.TxtUName.Margin = new System.Windows.Forms.Padding(2); + this.TxtUName.Name = "TxtUName"; + this.TxtUName.Size = new System.Drawing.Size(213, 21); + this.TxtUName.TabIndex = 4; + // + // labelControl5 + // + this.labelControl5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl5.Location = new System.Drawing.Point(85, 270); + this.labelControl5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl5.Name = "labelControl5"; + this.labelControl5.Size = new System.Drawing.Size(107, 18); + this.labelControl5.TabIndex = 2; + this.labelControl5.Text = "密码"; + // + // TxtPwd + // + this.TxtPwd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.TxtPwd.Location = new System.Drawing.Point(177, 268); + this.TxtPwd.Margin = new System.Windows.Forms.Padding(2); + this.TxtPwd.Name = "TxtPwd"; + this.TxtPwd.PasswordChar = '*'; + this.TxtPwd.Size = new System.Drawing.Size(213, 21); + this.TxtPwd.TabIndex = 5; + // + // labelControl6 + // + this.labelControl6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelControl6.Location = new System.Drawing.Point(83, 98); + this.labelControl6.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelControl6.Name = "labelControl6"; + this.labelControl6.Size = new System.Drawing.Size(134, 15); + this.labelControl6.TabIndex = 2; + this.labelControl6.Text = "数据库类型"; + // + // BtnOk + // + this.BtnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BtnOk.Location = new System.Drawing.Point(346, 380); + this.BtnOk.Margin = new System.Windows.Forms.Padding(2); + this.BtnOk.Name = "BtnOk"; + this.BtnOk.Size = new System.Drawing.Size(61, 28); + this.BtnOk.TabIndex = 8; + this.BtnOk.Text = "确定"; + this.BtnOk.Click += new System.EventHandler(this.BtnOk_Click); + // + // BtnCancel + // + this.BtnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(423, 380); + this.BtnCancel.Margin = new System.Windows.Forms.Padding(2); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(57, 28); + this.BtnCancel.TabIndex = 9; + this.BtnCancel.Text = "取消"; + this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click); + // + // cboDBType + // + this.cboDBType.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.cboDBType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboDBType.Location = new System.Drawing.Point(176, 93); + this.cboDBType.Margin = new System.Windows.Forms.Padding(2); + this.cboDBType.Name = "cboDBType"; + this.cboDBType.Size = new System.Drawing.Size(214, 20); + this.cboDBType.TabIndex = 1; + this.cboDBType.SelectedIndexChanged += new System.EventHandler(this.cboDBType_SelectedIndexChanged); + // + // labDBName + // + this.labDBName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labDBName.Location = new System.Drawing.Point(84, 204); + this.labDBName.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labDBName.Name = "labDBName"; + this.labDBName.Size = new System.Drawing.Size(108, 20); + this.labDBName.TabIndex = 2; + this.labDBName.Text = "数据库"; + // + // BtnTestConnect + // + this.BtnTestConnect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.BtnTestConnect.Location = new System.Drawing.Point(32, 380); + this.BtnTestConnect.Margin = new System.Windows.Forms.Padding(2); + this.BtnTestConnect.Name = "BtnTestConnect"; + this.BtnTestConnect.Size = new System.Drawing.Size(70, 28); + this.BtnTestConnect.TabIndex = 7; + this.BtnTestConnect.Text = "连接/测试"; + this.BtnTestConnect.Click += new System.EventHandler(this.BtnTestConnect_Click); + // + // cboDBName + // + this.cboDBName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.cboDBName.Location = new System.Drawing.Point(177, 204); + this.cboDBName.Margin = new System.Windows.Forms.Padding(2); + this.cboDBName.Name = "cboDBName"; + this.cboDBName.Size = new System.Drawing.Size(214, 20); + this.cboDBName.TabIndex = 6; + this.cboDBName.SelectedIndexChanged += new System.EventHandler(this.cboDBName_SelectedIndexChanged); + // + // lblMsg + // + this.lblMsg.AutoSize = true; + this.lblMsg.Location = new System.Drawing.Point(116, 386); + this.lblMsg.Name = "lblMsg"; + this.lblMsg.Size = new System.Drawing.Size(41, 12); + this.lblMsg.TabIndex = 10; + this.lblMsg.Text = "lblMsg"; + // + // btnSelectFile + // + this.btnSelectFile.Location = new System.Drawing.Point(399, 164); + this.btnSelectFile.Name = "btnSelectFile"; + this.btnSelectFile.Size = new System.Drawing.Size(81, 29); + this.btnSelectFile.TabIndex = 11; + this.btnSelectFile.Text = "选择文件"; + this.btnSelectFile.UseVisualStyleBackColor = true; + this.btnSelectFile.Click += new System.EventHandler(this.BtnSelectFile_Click); + // + // txtConnTimeOut + // + this.txtConnTimeOut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.txtConnTimeOut.Location = new System.Drawing.Point(178, 303); + this.txtConnTimeOut.Margin = new System.Windows.Forms.Padding(2); + this.txtConnTimeOut.Name = "txtConnTimeOut"; + this.txtConnTimeOut.Size = new System.Drawing.Size(66, 21); + this.txtConnTimeOut.TabIndex = 13; + this.txtConnTimeOut.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.txtConnTimeOut.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtConnectionOut_KeyPress); + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.Location = new System.Drawing.Point(86, 305); + this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(88, 18); + this.label1.TabIndex = 12; + this.label1.Text = "连接超时"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(249, 306); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(17, 12); + this.label2.TabIndex = 14; + this.label2.Text = "秒"; + // + // noneSSLCB + // + this.noneSSLCB.AutoSize = true; + this.noneSSLCB.Checked = true; + this.noneSSLCB.CheckState = System.Windows.Forms.CheckState.Checked; + this.noneSSLCB.Location = new System.Drawing.Point(176, 341); + this.noneSSLCB.Name = "noneSSLCB"; + this.noneSSLCB.Size = new System.Drawing.Size(48, 16); + this.noneSSLCB.TabIndex = 15; + this.noneSSLCB.Text = "None"; + this.noneSSLCB.UseVisualStyleBackColor = true; + this.noneSSLCB.Click += new System.EventHandler(this.noneSSLCB_Click); + // + // requiredSSLCB + // + this.requiredSSLCB.AutoSize = true; + this.requiredSSLCB.Location = new System.Drawing.Point(230, 341); + this.requiredSSLCB.Name = "requiredSSLCB"; + this.requiredSSLCB.Size = new System.Drawing.Size(72, 16); + this.requiredSSLCB.TabIndex = 16; + this.requiredSSLCB.Text = "Required"; + this.requiredSSLCB.UseVisualStyleBackColor = true; + this.requiredSSLCB.Click += new System.EventHandler(this.requiredSSLCB_Click); + // + // sslLabel + // + this.sslLabel.AutoSize = true; + this.sslLabel.Location = new System.Drawing.Point(86, 341); + this.sslLabel.Name = "sslLabel"; + this.sslLabel.Size = new System.Drawing.Size(47, 12); + this.sslLabel.TabIndex = 17; + this.sslLabel.Text = "SSL连接"; + // + // DBForm + // + this.AcceptButton = this.BtnOk; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.BtnCancel; + this.ClientSize = new System.Drawing.Size(516, 443); + this.Controls.Add(this.sslLabel); + this.Controls.Add(this.requiredSSLCB); + this.Controls.Add(this.noneSSLCB); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConnTimeOut); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnSelectFile); + this.Controls.Add(this.lblMsg); + this.Controls.Add(this.cboDBName); + this.Controls.Add(this.cboDBType); + this.Controls.Add(this.BtnCancel); + this.Controls.Add(this.BtnTestConnect); + this.Controls.Add(this.BtnOk); + this.Controls.Add(this.labDBName); + this.Controls.Add(this.TxtPwd); + this.Controls.Add(this.labelControl5); + this.Controls.Add(this.TxtUName); + this.Controls.Add(this.labelControl4); + this.Controls.Add(this.TxtPort); + this.Controls.Add(this.labelControl3); + this.Controls.Add(this.TxtHost); + this.Controls.Add(this.labelControl2); + this.Controls.Add(this.TxtConnectName); + this.Controls.Add(this.labelControl6); + this.Controls.Add(this.labelControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DBForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "连接数据库"; + this.Load += new System.EventHandler(this.DBFrom_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label labelControl1; + private System.Windows.Forms.TextBox TxtConnectName; + private System.Windows.Forms.Label labelControl2; + private System.Windows.Forms.TextBox TxtHost; + private System.Windows.Forms.Label labelControl3; + private System.Windows.Forms.TextBox TxtPort; + private System.Windows.Forms.Label labelControl4; + private System.Windows.Forms.TextBox TxtUName; + private System.Windows.Forms.Label labelControl5; + private System.Windows.Forms.TextBox TxtPwd; + private System.Windows.Forms.Label labelControl6; + private System.Windows.Forms.Button BtnOk; + private System.Windows.Forms.Button BtnCancel; + private System.Windows.Forms.ComboBox cboDBType; + private System.Windows.Forms.Label labDBName; + private System.Windows.Forms.Button BtnTestConnect; + private System.Windows.Forms.ComboBox cboDBName; + private System.Windows.Forms.Label lblMsg; + private System.Windows.Forms.Button btnSelectFile; + private System.Windows.Forms.TextBox txtConnTimeOut; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.CheckBox noneSSLCB; + private System.Windows.Forms.CheckBox requiredSSLCB; + private System.Windows.Forms.Label sslLabel; + } +} \ No newline at end of file diff --git a/DBChm/DBForm.cs b/DBChm/DBForm.cs new file mode 100644 index 0000000..6ee204c --- /dev/null +++ b/DBChm/DBForm.cs @@ -0,0 +1,481 @@ +using ComponentFactory.Krypton.Toolkit; +using MJTop.Data; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows.Forms; + +namespace DBCHM +{ + public partial class DBForm : KryptonForm + { + private DBForm() + : this(OPType.新建) + { + + } + + /// + /// 当前操作类型 + /// + public OPType OpType { get; private set; } + + public int Id { get; private set; } + + public DBForm(OPType opType, int? id = null) + { + InitializeComponent(); + + this.OpType = opType; + + if ((this.OpType == OPType.编辑 || this.OpType == OPType.克隆) && !id.HasValue) + { + throw new ArgumentNullException(this.OpType + "操作必须传递要操作的Id!"); + } + else + { + if (id.HasValue) + { + foreach (var item in FormUtils.DictDBType) + { + cboDBType.Items.Add(item.Value.ToString()); + } + + this.Id = id.Value; + DBCHMConfig config = ConfigUtils.Get(id.Value); + TxtConnectName.Text = config.Name; + cboDBType.Text = config.DBType; + TxtHost.Text = config.Server; + TxtPort.Text = config.Port?.ToString(); + TxtUName.Text = config.Uid; + TxtPwd.Text = config.Pwd; + cboDBName.Text = config.DBName; + txtConnTimeOut.Text = config.ConnTimeOut?.ToString(); + + if (this.OpType == OPType.克隆) + { + TxtConnectName.Text += "_Clone"; + } + + if (config.DBType == DBType.SQLite.ToString()) + { + btnSelectFile.Visible = true; + + TxtHost.Enabled = false; + TxtPort.Enabled = false; + TxtUName.Enabled = false; + + //暂不支持 加密的 Sqlite数据库 + TxtPwd.Enabled = false; + } + + //编辑时,确定后刷新连接列表 + BtnOk.DialogResult = DialogResult.OK; + } + else + { + btnSelectFile.Visible = false; + } + + if (string.IsNullOrWhiteSpace(txtConnTimeOut.Text)) + { + txtConnTimeOut.Text = "30"; + } + } + + //为KeyDown能应用到所有控件上 注册 KeyDown 事件 + foreach (Control control in this.Controls) + { + control.KeyDown += control_KeyDown; + } + lblMsg.Text = string.Empty; + } + + void control_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + this.Close(); + } + } + + private void DBFrom_Load(object sender, EventArgs e) + { + if (OpType == OPType.新建) + { + foreach (var item in FormUtils.DictDBType) + { + cboDBType.Items.Add(item.Value.ToString()); + } + cboDBType.SelectedIndex = 0; + string port; + if (FormUtils.DictPort.TryGetValue(cboDBType.Text, out port)) + { + TxtPort.Text = port; + } + TxtHost.Text = "127.0.0.1"; + // TODO 设置默认用户名等 + SetUserNameByDbType(); + } + } + private void BtnSelectFile_Click(object sender, EventArgs e) + { + OpenFileDialog fileDia = new OpenFileDialog(); + var dia = fileDia.ShowDialog(); + if (dia == DialogResult.OK) + { + cboDBName.Text = fileDia.FileName; + } + } + + public void SetMsg(string msg, bool isSuccess = false) + { + lblMsg.Text = msg; + lblMsg.ForeColor = isSuccess ? System.Drawing.Color.Green : System.Drawing.Color.Red; + } + + private void BtnTestConnect_Click(object sender, EventArgs e) + { + DBType type = (DBType)Enum.Parse(typeof(DBType), cboDBType.Text); + try + { + if (type == DBType.Oracle && string.IsNullOrWhiteSpace(cboDBName.Text)) + { + throw new Exception("Oracle没有提供数据库名称查询支持,请输入服务名!"); + } + + var connString = InitConnectionStr(type); + + List dbNames = null; + + DBMgr.TryConnect(type, connString, out dbNames); + + if (dbNames != null && dbNames.Count > 0) + { + cboDBName.Items.Clear(); + foreach (var dbName in dbNames) + { + cboDBName.Items.Add(dbName); + } + } + + this.Text = "连接服务器成功!"; + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + } + + private void BtnCancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void BtnOk_Click(object sender, EventArgs e) + { + try + { + if (string.IsNullOrWhiteSpace(TxtConnectName.Text)) + { + MessageBox.Show("请输入连接名!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + if (string.IsNullOrWhiteSpace(cboDBName.Text)) + { + MessageBox.Show("请输入数据库名称!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + DBType type = (DBType)Enum.Parse(typeof(DBType), cboDBType.Text); + + + string connString = InitConnectionStr(type); + NameValueCollection nvc = new NameValueCollection(); + if (OpType == OPType.新建 || OpType == OPType.克隆) + { + nvc.Add("Name", TxtConnectName.Text.Trim()); + nvc.Add("DBType", cboDBType.Text.Trim()); + + nvc.Add("Server", TxtHost.Enabled ? TxtHost.Text.Trim() : string.Empty); + nvc.Add("Port", TxtPort.Enabled ? TxtPort.Text : string.Empty); + nvc.Add("DBName", cboDBName.Text.Trim()); + nvc.Add("Uid", TxtUName.Enabled ? TxtUName.Text.Trim() : string.Empty); + nvc.Add("Pwd", TxtPwd.Enabled ? TxtPwd.Text : string.Empty); + nvc.Add("ConnTimeOut", txtConnTimeOut.Enabled ? txtConnTimeOut.Text : "30"); + nvc.Add("ConnString", connString); + nvc.Add("Modified", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + + ConfigUtils.Save(nvc); + + } + else if (OpType == OPType.编辑) + { + nvc.Add("Id", Id.ToString()); + nvc.Add("Name", TxtConnectName.Text.Trim()); + nvc.Add("DBType", cboDBType.Text.Trim()); + + nvc.Add("Server", TxtHost.Enabled ? TxtHost.Text.Trim() : string.Empty); + nvc.Add("Port", TxtPort.Enabled ? TxtPort.Text : string.Empty); + nvc.Add("DBName", cboDBName.Text.Trim()); + nvc.Add("Uid", TxtUName.Enabled ? TxtUName.Text.Trim() : string.Empty); + nvc.Add("Pwd", TxtPwd.Enabled ? TxtPwd.Text : string.Empty); + nvc.Add("ConnTimeOut", txtConnTimeOut.Enabled ? txtConnTimeOut.Text : "30"); + nvc.Add("ConnString", connString); + nvc.Add("Modified", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + ConfigUtils.Save(nvc); + } + + + FormUtils.ShowProcessing("正在查询表结构信息,请稍等......", this, arg => + { + try + { + InitDb(type); + } + catch (Exception ex) + { + LogUtils.LogError("BtnOk_Click", Developer.SysDefault, ex, connString); + } + + }, null); + + this.DialogResult = DialogResult.OK; + this.Close(); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + } + + private void cboDBType_SelectedIndexChanged(object sender, EventArgs e) + { + this.Text = "连接数据库"; + string port; + if (FormUtils.DictPort.TryGetValue(cboDBType.Text, out port)) + { + TxtPort.Text = port; + } + // TODO 设置默认用户名等 + SetUserNameByDbType(); + } + + /// + /// 根据数据库类型设置默认用户名等 扩展修改 2019-01-24 21:23 + /// + private void SetUserNameByDbType() + { + btnSelectFile.Visible = false; + + TxtHost.Enabled = true; + TxtPort.Enabled = true; + TxtUName.Enabled = true; + + sslLabel.Visible = false; + noneSSLCB.Visible = false; + requiredSSLCB.Visible = false; + + labDBName.Text = "数据库"; + DBType dbtype = (DBType)Enum.Parse(typeof(DBType), cboDBType.Text.ToString()); + if (dbtype == DBType.SqlServer) + { + TxtUName.Text = "sa"; + } + else if (dbtype == DBType.MySql) + { + TxtUName.Text = "root"; + + sslLabel.Visible = true; + noneSSLCB.Visible = true; + requiredSSLCB.Visible = true; + + } + else if (dbtype == DBType.Oracle || dbtype == DBType.OracleDDTek) + { + TxtUName.Text = "scott"; + labDBName.Text = "服务名"; + } + else if (dbtype == DBType.PostgreSql) + { + TxtUName.Text = "postgres"; + } + else if (dbtype == DBType.DB2) + { + TxtUName.Text = "db2admin"; + } + else if(dbtype == DBType.SQLite) + { + btnSelectFile.Visible = true; + + TxtHost.Enabled = false; + TxtPort.Enabled = false; + TxtUName.Enabled = false; + + //暂不支持 加密的 Sqlite数据库 + TxtPwd.Enabled = false; + } + else + { + TxtUName.Text = ""; + } + } + + /// + /// 端口验证 只能输入数字 + /// + /// + /// + private void TxtPort_KeyPress(object sender, KeyPressEventArgs e) + { + e.Handled = true; + if (e.KeyChar == 8 || e.KeyChar == 127)//退格删除,delete删除 + { + e.Handled = false; + } + else + { + if (e.KeyChar >= '0' && e.KeyChar <= '9')//只能输入数字 + { + int maxPort = 0; + int.TryParse(TxtPort.Text + e.KeyChar.ToString(), out maxPort); + if (maxPort > 0 && maxPort <= 65535) + { + e.Handled = false; + } + } + } + + } + + private void txtConnectionOut_KeyPress(object sender, KeyPressEventArgs e) + { + e.Handled = true; + if (e.KeyChar == 8 || e.KeyChar == 127)//退格删除,delete删除 + { + e.Handled = false; + } + else + { + if (e.KeyChar >= '0' && e.KeyChar <= '9')//只能输入数字 + { + int timtOut = 0; + int.TryParse(TxtPort.Text + e.KeyChar.ToString(), out timtOut); + if (timtOut > -1) + { + e.Handled = false; + } + } + } + + } + + private void cboDBName_SelectedIndexChanged(object sender, EventArgs e) + { + this.Text = "连接数据库"; + } + + private void noneSSLCB_Click(object sender, EventArgs e) + { + if ((sender as CheckBox).Checked == true) + { + requiredSSLCB.Checked = false; + } + else + { + requiredSSLCB.Checked = true; + } + } + + private void requiredSSLCB_Click(object sender, EventArgs e) + { + if ((sender as CheckBox).Checked == true) + { + noneSSLCB.Checked = false; + } + else + { + noneSSLCB.Checked = true; + } + } + + private string extraParam = ""; // 额外参数 + + /// + /// 临时处理 + /// TODO 创建额外参数 + /// + private void CreateExtraParam() + { + // TODO 额外参数 + if (noneSSLCB.Checked == true) + { + this.extraParam = "SslMode=None;"; + } + else if (requiredSSLCB.Checked == true) + { + this.extraParam = "SslMode=Required;"; + } + else + { + this.extraParam = ""; + } + } + + /// + /// 临时处理 + /// TODO 初始化DB连接 + /// + /// + /// + private void InitDb(DBType type) + { + if (type == DBType.MySql) + { + CreateExtraParam(); + DBUtils.Instance = DBMgr.UseDB(type, TxtHost.Text, + (string.IsNullOrWhiteSpace(TxtPort.Text) ? null : new Nullable(Convert.ToInt32(TxtPort.Text))), + cboDBName.Text.Trim(), TxtUName.Text, TxtPwd.Text, + (string.IsNullOrWhiteSpace(txtConnTimeOut.Text) ? 30 : Convert.ToInt32(txtConnTimeOut.Text)) + , 300, this.extraParam); + } + else + { + DBUtils.Instance = DBMgr.UseDB(type, TxtHost.Text, + (string.IsNullOrWhiteSpace(TxtPort.Text) ? null : new Nullable(Convert.ToInt32(TxtPort.Text))), + cboDBName.Text.Trim(), TxtUName.Text, TxtPwd.Text, + (string.IsNullOrWhiteSpace(txtConnTimeOut.Text) ? 30 : Convert.ToInt32(txtConnTimeOut.Text)) + , 300); + } + } + + /// + /// 临时处理 + /// TODO 初始化连接串 + /// + private string InitConnectionStr(DBType type) { + CreateExtraParam(); + string connString = ""; + if (type == DBType.MySql) + { + connString = DBMgr.GetConnectionString(type, TxtHost.Text, + (string.IsNullOrWhiteSpace(TxtPort.Text) ? null : new Nullable(Convert.ToInt32(TxtPort.Text))), + cboDBName.Text, TxtUName.Text, TxtPwd.Text, + (string.IsNullOrWhiteSpace(txtConnTimeOut.Text) ? 30 : Convert.ToInt32(txtConnTimeOut.Text)), + this.extraParam); + } + else + { + connString = DBMgr.GetConnectionString(type, TxtHost.Text, + (string.IsNullOrWhiteSpace(TxtPort.Text) ? null : new Nullable(Convert.ToInt32(TxtPort.Text))), + cboDBName.Text, TxtUName.Text, TxtPwd.Text, + (string.IsNullOrWhiteSpace(txtConnTimeOut.Text) ? 30 : Convert.ToInt32(txtConnTimeOut.Text)) + ); + } + return connString; + } + } +} diff --git a/DBChm/DBForm.resx b/DBChm/DBForm.resx new file mode 100644 index 0000000..9f93bf7 --- /dev/null +++ b/DBChm/DBForm.resx @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEASEgAAAEAIACIVAAAFgAAACgAAABIAAAAkAAAAAEAIAAAAAAAAFEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8vLwA+LS4ASkVBAEtL + RABLS0YAS0tEAElHQgBAQD8APz8/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE1KQwBMSUMAU1BLAFtYUgBdWlQAXVxVAGBeWABramMADggHAyotJQYpKykFKiwlBiEf + HQSAfXIAYF5ZAF9cVgBcWlQAWVdRAFZUTgBNTEYAQjc6AEc/PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0xJAAAAAABQSUEAQ0E+AFVS + TACEhYEAREE9CFVRTBVaV1A1WllSVl5cVm9iX1mCZmNdlGhmX5tpZ2CbaGZfm2VjXJhhX1mFX1xXdVxa + VF1ZVlFAVFFLHkpKQwwABAABW1pRAEtJQgAbJBgARUU5AElJPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBPzwAODY1AFFPSQCFgnYAREM/DVZTTjlcWlRwYV9YqGdl + X9RvbGXodXJr9Hl2b/59enL/hIB4/4eEfP+JhX3/iIR9/4WBev9/fHT/e3dw/3d0bfdyb2jsamdh3WRh + W7heXFWDWFVPSktKQxUAAAABWVZTAEhFQwBPS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT01HAExKRABYVk8AAAAAAk9NSCZbWVNwYmBavmxpYul0cWr/fnpy/4eDe/+Pi4L/lZCI/5qV + jP+dmZD/oZyT/6Self+ln5f/paCX/6Oflf+gm5L/nZiP/5qVjP+UkIj/jomB/4WBev96d3D/cG1m8mVj + XcxbWlOJU1BLOz07OwhcWlQATk1GAGdmWQAvLy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSAAAAAAAU1FMAP// + /wBQTkgsWFZQimJgWdlua2T+e3dw/4eDe/+RjIT/mJOL/56ZkP+inZT/pJ+X/6ehmP+po5r/qqSb/6ul + nP+rpZz/rKad/6ymnf+rpZz/qqSb/6mkm/+oopn/pZ+W/6Cbkv+alYz/kIyE/4SAef92c2z/aGVf6l1b + VaVTUUtBQ0M7BldVTgBIRj8AT0xHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkI/ADwxLQBTUEwAT05IE1lXUW9fXVfaamdh/3l1 + bv+Ggnv/kY2F/5mUi/+dmI//oJuS/6KdlP+kn5b/pqGY/6eimf+po5r/qqSb/6ulnP+rpZz/q6Wc/6ym + nf+rpZz/q6Wc/6ulnP+qpJv/qaOa/6eimf+loJf/op2T/5yXj/+Tj4b/hoJ6/3Vya/9kYlzsV1VPk09O + SCZbXFsAS0dCAExHQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABFRT8ARUI9AG1xaQBRTUkvWVdSrWNgWvpxbmf/gHx1/4yIf/+Uj4f/mZOL/5yW + jv+emZD/oJuS/6KdlP+kn5X/paCX/6ahmP+moJf/pqCX/6Wflv+inZT/op2U/6Wflv+nopj/qKKZ/6mj + mv+qpJv/qKOa/6eimf+loJf/pJ+V/6Kdk/+empH/mZSL/46Kgv9/e3T/a2hi/1pYUs5NTEZTQjs3BEhH + QQBHR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVF + PwBHRkAAoJKOAFJQSUdYVlDPZGFb/3Zya/+EgHj/jYmA/5KOhf+VkYn/mZOL/5uWjf+dmI//npmQ/5yX + jv+Yk4r/ko2E/4qFff+BfHT/fHhw/3Vxaf9taWL/a2hg/3JuZv94dGz/e3hv/4aBef+Qi4P/mZSM/5+a + kf+inZT/o56U/6Gdk/+fm5H/nZiP/5qUjP+SjYX/hYF5/3BtZv9bWVPtTkxIcjw9OQZCQj0AOjo1AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk1IAEdHQQCdjYwATUxGS1RS + TN5iX1n/dnJr/4R/eP+Lh37/j4qC/5GNhP+UkIj/l5GJ/5aQiP+RjIP/iYR8/4B7c/92cWn/bmli/2hk + Xf9iXlf/XVlT/11ZU/9dWVP/WlZR/1VRTP9UUUv/V1RN/1tXUf9hXVb/bGdg/3dya/+GgXj/k46F/5qV + jP+dmI//nJeO/5qVjf+Xkor/kY2F/4aCev9zcGj/W1lU80lIQ3MtLigGOzo2AD48OgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Pz8ATktFAE1KRwBMSkU8T01I3F5bVv9yb2j/gHx1/4eC + e/+Lhn7/jomB/5CLg/+Qi4P/jId+/4WAd/9/eXH/fHdv/355cf+Dfnb/iIN7/4+Kgv+RjYX/ko2G/5iT + i/+dl4//m5aO/5GMhf+Mh4D/i4Z//4SAeP99eXH/dnJr/3BsZf9vamP/dXBp/4B7c/+MiH//lpCI/5iS + iv+WkYn/k4+H/4+Kgv+EgHj/cW1n/1dVT/NEQj5wMjAvBD8+OwBGRkQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADGu4cATkxFAEpIQxtLSkW/V1VP/21pY/97d3D/gn52/4WBev+JhHz/i4V9/4uF + fP+Jg3r/iYN6/4+JgP+ZlIz/paCX/6+qov+1saj/uLOr/7u2rv+8t67/vLiv/724r/+9uK//vLev/7u2 + rv+6taz/ubOr/7axqP+yraX/raif/6Sfl/+YlIz/kIuD/4yHf/+Lh3//jYiA/5CLgv+SjYX/kY2E/4+L + gv+Lhn7/f3t0/2lmYP9OTEfqQkE9TT87NgBKS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABISEQAT1JRAkZEQH1OTEb9ZGFb/3Vxa/98eXH/gHx1/4N/d/+Ef3f/hoB4/4+JgP+dmI//r6qj/765 + sf/Gwrr/ycS9/8jDvP/Fwbn/wr61/8K9tP/CvbT/wb20/8G9tP/BvLP/wLyz/8C7sv+/u7L/vrqx/765 + sP++ubD/vbiv/7y3rv+6taz/tbCo/7Cro/+sp5//pKCa/6Kdl/+Yk4v/kIuD/42IgP+Lhn7/hYF5/3dz + bP9dW1X/R0ZBx0ZFQx9FREEAWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBGRT8ARUQ+KEdF + QNpYVVD/bWlj/3dzbP97d3D/fXly/396cv+KhX3/pKCY/7+7tP/OysX/0s7I/8/Lw//Lx8D/ycS9/8fD + u//Fwbn/xcC4/8S/t//AvLP/u7au/7q2rv+6tq7/ubWt/7m1rP+7tq7/wLyz/8C8s//AvLP/wLyz/8C7 + sv+/urH/vrmw/724sP+9uK//l5ON/7Cspv++u7b/r6ul/5eSi/+JhHz/hYF5/356c/9raGH/UE5J/ENC + P3UAAAAAXl5eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBAQDoAREI9cUtIQ/1hXlj/cGxm/3Vx + a/94dG3/gX11/52Ykf/Bvbf/19PO/9fUzv/Szsf/zcjB/8rGvv/FwLn/ubWu/6+ro/+empP/kY2H/4aD + fP+Cf3j/gn54/4SAef+EgXr/hIB5/4J/eP+AfXb/gn95/4uIgf+Xk4z/pqKa/7Gtpf+6tq3/wbyz/8C8 + s//AvLL/rquk/52Ykv/Fwrr/z83I/8bDv/+gnZf/hoF6/397c/9zb2j/WldS/0NBPchAQD8bQkJBAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBPTkkMQ0E9rFBOSf9mY13/cG1n/3ZybP+MiIL/uLaw/9rX + 0//d29b/1NHK/9DKw//Iw7z/ubSt/52ak/+Jhn//endx/3Rxav94dW7/fXpz/4F9dv+Ggnr/jIiA/5CL + g/+RjYT/kYyE/4+Lg/+Khn7/hIB4/4B8df97d3D/dHFr/3Zzbf+Bfnf/kY6H/6umnv+7tq7/yMS9/5qV + kf+8tq7/xsK7/9nX0v/X1dL/p6Oe/4F9dv92cmv/Yl9Z/0ZEQO87OjhFNDIvAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEZEQABGREAdRUM/2FVSTv9oZV//c3Bq/5aTjv/Pzcr/5+Xi/93a1P/Tz8j/ycW9/62o + ov+Lh4H/dXJs/3Vxa/97eHH/hoN7/4+Lg/+Wkon/m5aO/5+bkf+inpT/paCX/6eimf+oo5r/qaOa/6ij + mv+moZj/o56V/6Cbkv+bl47/lZGI/46Jgf+Cf3f/eHVu/3Rxav9+e3T/qqaf/62ppP+qpZ3/v7qx/8nF + vv/i4Nz/4N/d/6eln/96dm//ZmNd/0lHQ/06OTdsNDMrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpI + RABJSEQkSkhE7FlWUf9raGP/mJaR/9va1//q5+T/3NnT/87Kw/+tqqP/hoN9/3FuZ/90cGr/gX52/42I + gP+WkYj/nJeO/6Cbkv+jnpX/paCX/6eimf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6qk + nP+po5r/pqGY/6Oelf+emZD/lpGJ/4uHf/98eHH/fntz/66rpf9tamT/Y2Ba/7ezq//NycP/6Ofk/+Xk + 4/+fnJf/aWZg/0xJRf85NzWDXVhLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5NSQBOTUklTkxI7F1b + Vv+Ni4b/2tjV/+3r6f/d2dP/vrqz/4eEfv9qZ2H/cW1m/397dP+MiH//lZCH/5qVjf+emZD/oJuS/6Kd + lP+kn5b/pqGX/6eimf+po5v/qqSb/6ulnP+rpZz/rKWc/6ymnf+spp3/q6Wc/6ulnP+qpJv/qaOa/6ei + mf+loJf/o56U/5+ZkP+Xkon/i4d//25rY/8wLyj/Dg0L/3Rxbf/Ev7j/1dLM//X08//X1dP/fHl0/0xK + Rf82NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBPTkolUE5L7HNwbf/KyMb/7+7r/9zZ + 0/+tqqT/cG1n/2RhW/91cmv/hYF5/4+Lg/+VkYj/mZSM/5yWjv+emZD/oJuS/6Kdk/+jnpX/pJ+V/6Oe + lf+hnJT/oZuS/52Xjv+blY3/mpWM/5yXjv+hnJP/o56U/6agmP+oopn/qKKZ/6eimP+loJf/pJ+V/6Kd + k/+fm5H/nJeO/3BtZf83Ni3/FRQQ/xsaGP+Cf3n/wLy1/+Xi3v/29vX/qaek/09NSf8yMS6aAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJRTABPTkolWFZS7KKgnv/q6Ob/4d3Z/6Sgmv9kYlv/ZmNd/3l1 + bv+Hgnv/joqB/5KOhf+VkIj/mZOL/5uVjf+cl47/m5aN/5eSif+QjIP/iIN7/396cv92cWr/cGxl/2hj + Xf9jX1j/YV1X/2NfWf9rZl//cGxl/3p2b/+FgXn/kIuD/5mUi/+fmpD/oZyT/6Gck/+fmpH/npmQ/4iD + fP9IRz3/JSQd/wcGBf8zMi//fnt1/8XBuv/08/H/1NPR/19dWf8wLiyZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFZVUQBQTkolbGpn7MvJxv/o5eL/op+a/1xaVP9kYVv/eHRt/4WBef+Lh3//joqC/5GN + hP+Uj4f/lZCI/5ONhf+Mh37/g311/3p1bf9xbWX/bWhh/2llXv9lYVr/ZGFa/2djXP9lYVr/Y19Z/2Fd + WP9bV1L/WlZR/11YUv9fW1X/ZmFb/29rY/98d2//ioV9/5aRiP+bl43/nJaO/5eRif9VU0r/NDMq/w4O + Cv8VFBP/UlBL/3t4cv/Z1tL/6+vp/3p5df8wLy2ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg + XABXVVEliIaD7d/d2v+0saz/W1lT/2BeWP90cWr/gX12/4eCe/+Khn7/jYmA/4+Kgv+OiYH/iYR7/4N9 + dP+AenL/gXxz/4aBef+NiYH/lZCJ/5yXj/+emZH/op2V/6eimv+nopr/p6Ka/6WgmP+dmJD/mZSM/5OP + h/+Khn7/gn12/3l1bf91cGj/dnJq/355cv+Ig3v/kYyD/5aRiP9qaGD/PTwy/x8eGP8FBAP/QDw3/1xa + VP+KiIT/5+bk/5eVkv81NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG9uagBiYV0nn56b7cnH + xP9mZF//WldR/3BtZv98eHH/gX52/4WBev+Ig3z/iYN7/4qEe/+KhHv/joiA/5iTi/+moZn/s66m/7u2 + rv++urL/vrqx/7+7sv/Au7L/wLuy/7+7sv+/urH/v7qx/765sP+9ubD/vbiv/7u2rv+5tKv/tbCo/6+q + of+nopn/nZiR/5aSi/+Tj4j/kIyE/5GMhP98eHD/RUQ6/y8uJv8KCgb/HRkW/2FdV/9ZV1L/qKek/6Wj + oP89PDmaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHVzcABraWYooZ+c7YiGgv9RTkn/aGRe/3Zy + a/98eHH/gHx0/4J+dv+CfnX/iIN6/5iTiv+sp6D/vbix/8fDvP/Lxr//y8a//8nEvP/Fwbn/w762/8K+ + tf/CvrX/wr21/8K+tf/CvbT/wb20/8C8s//Au7L/v7uy/7+6sf++urH/vrmw/724sP+8t6//urSs/7aw + qP+yraX/sKyl/66qpP+dmJL/VlVM/zo5L/8YFxL/CAUD/0xIQv9iYFn/Y2Fd/4qJhf9DQj6YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIF+ewCCgH0pioiF7VdVUP9aV1L/bmpk/3dzbP96d2//fHhx/4F8 + dP+Sjob/sKyl/8nFv//U0cv/0s7I/83Jwv/Kxr7/yMS8/8bCuv/Dv7f/wby0/765sf+1san/sq2l/6mm + nv+opJz/p6Sc/6+ro/+yrqb/uLSs/7y4r/++ubD/v7uy/8G8s//Au7L/v7qx/765sP+8uK//vLev/766 + tP/Fwr3/goB6/0JBN/8oKCD/BgUD/yYhG/9lYVv/VVNN/1dWUv9DQj6H////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHh2cgCDgH0hX11Z3k1KRv9iX1r/cG1m/3Vxav95dG3/h4N8/6yoov/Pzcj/2tfS/9bS + zP/Qy8T/zMfA/8jDvP+8uLH/rKmi/5uYkf+NioL/gH13/3h1b/94dW7/fHhx/3p3cP96d3D/endv/3x5 + cv96d3D/eHVu/3t4cv+HhH3/lJCJ/6OfmP+wrKT/vrqx/8G8s//AvLP/v7qx/766sf/Fwbr/sa6p/09O + Rf82Niz/EREN/wwIBP9QS0P/XVpV/0A+Ov0zMzBoPzw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJP + SwBWU08YRkRBy1FPSv9nZF7/cGxm/3h0bv+Xk47/x8XA/+De2v/d2tX/08/I/87Jwv/Au7T/qaSe/4uH + gf97eHL/dXJs/3Vya/98eXH/g394/4mFff+OioH/k46G/5eSiv+ZlIv/mZSL/5eSiv+Tjob/jYmB/4iE + ff+Cfnf/endv/3VybP94dW//goB5/5qXj/+wraT/v7qy/8K9tP/BvbT/vrmy/2tpYf8+PTP/ISEa/wUD + Af8tJx//XltV/0RCPvw3NjNbKigjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZEPwBGQz8fRkQ/3lZT + T/9oZV//dnNt/6OhnP/a2NX/5+Tg/9rWz//QzMT/vbmx/5eUjf96d3D/cW1n/3h0bf+Cfnb/jIiA/5SQ + iP+alo3/n5qR/6OelP+loJf/qKKZ/6mkm/+qpJv/q6Wc/6qlnP+po5r/p6GY/6Wflv+hnJP/nJeO/5WQ + iP+Lh3//f3x0/3Rxav9zcGn/h4R9/6qmnv+/u7L/xcC4/4yJgf9FRDr/MTAo/wsLCP8TDQf/TEdA/0tJ + RP47OTZxNzUvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBLSUUlS0lF7FpXUv9va2b/paOf/+Tj + 4P/n5eH/2NTO/8XAuf+Zlo//dXJs/25rZf95dW7/h4N7/5GMhP+ZlIv/npmQ/6Gck/+kn5b/paCY/6ei + mf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6ulnP+qpJv/qKKZ/6Wgl/+hnJP/m5aN/5KN + hP+EgHj/dXFq/3BtZ/+Fgnv/r6uj/6+ro/9SUUf/PDwy/xoaFP8GAwD/MSoi/0lHQv85NzWHcGlYAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSQBPTUklT01J7GBeWf+cmpb/4+Lg/+ro5P/X083/r6ul/3p3 + cf9pZmD/dnJr/4SAef+Pi4P/l5GJ/5uWjv+emZD/oJuS/6KdlP+kn5X/pqGX/6eimf+po5r/qqSb/6qk + m/+rpZz/q6Wc/6ulnP+rpZz/q6Wc/6ulnP+qpJv/qKOa/6eimf+loJf/pJ+V/6Cckv+alo3/kIyE/4J+ + dv9va2X/bWpk/5GNhv9raWH/QkE2/ysqIv8IBwX/GBEK/z06Nf83NjOZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFBPSwBQTkslUU9L7H58eP/Y1tT/7uvo/9TQyv+ZlpD/aWZh/2tnYf98eHH/iYV9/5GN + hP+WkYn/mZSM/5uWjv+emZD/oJuS/6Gck/+inZP/oZuS/52Yj/+alYz/lpGI/5CLg/+OioH/joqB/4+K + gv+VkIf/mpWM/5+Zkf+jnZX/paCX/6WhmP+loJb/o56V/6Kdk/+gm5H/nJeP/5aRiP+JhX3/d3Ns/2dl + Xv9aWFH/REM5/zg4Lv8TEw//CgYB/yokHP8yMS+aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJR + TQBPTkolW1lW7K6sqf/t6+j/1tLN/4uIgv9fXVf/bGli/356c/+JhX3/j4uC/5KOhf+VkIj/mJOL/5qV + jf+alYz/l5KK/5GMhP+Ig3v/fnlx/3NvZ/9qZl//ZGBZ/11ZU/9aVlD/V1RO/1hUT/9dWVP/Yl9Y/2xn + Yf93cmr/g352/5GMg/+alYz/n5qR/6Cbkv+fmpH/nZiP/5uVjf+WkYn/jIiA/3x4cf9hX1n/QUA3/0FA + Nf8kIxz/BgUD/xoSC/8oJSKamv//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlXUwBRT0sldHJv7NLQ + zv/h3tn/jImD/1tYUv9qZ2H/fHhx/4aCev+Lh37/joqB/5GNhP+Tj4b/k46F/4+Kgf+Hgnr/fnlx/3dy + av9ybWb/cGxk/29rZP9taWL/cW1m/3JuZ/9wbGX/bmtk/25qZP9oZV//Y19Z/2RfWf9iXVf/ZGBa/2tm + X/91cGj/gn51/4+Kgv+Yk4r/m5WN/5qUjP+Xkor/k46G/4uHfv97eHD/UVBI/0JBN/8zMin/Dg0K/w4I + Av8eFw+nDTBaATUaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjYABZV1QmkI6L7eDe2/+fnJj/V1RP/2Zj + Xf94dW3/gn53/4aCe/+KhX7/jYiA/46JgP+Nh37/h4J4/4N+dP+Efnb/ioR8/5KOhf+dmJH/p6Ob/7Cs + pP+zr6f/urav/7y4sf+7t7D/urau/7i0rP+xrKX/qaWd/6Sfl/+alY3/kYyE/4aCev99eHD/enVu/355 + cf+FgHj/jYiA/5SPhv+UkIj/ko6G/4+Lgv+JhH3/amdg/0RDOv8+PTP/HBwW/wcFAv8XDgTGCQUBEgIA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFwbABkY18noqCd7b+9uv9dW1f/XVtV/3NvaP99eXH/gX12/4WA + ef+Hgnv/iIJ6/4mDev+Nh37/l5KJ/6ikm/+7t7D/y8jC/9fUz//d2tb/3tzY/+Dd2f/e3Nf/3dvV/9vY + 0//Y1dD/1tPN/9PQyv/QzMX/zcnC/8nFvv/FwLn/wLuz/7m0rP+wq6L/pqCZ/56Zkf+ZlI3/lpGL/5OO + hv+QjIP/kIuD/46Kgf+Lh3//gHx1/09NRP9CQTf/LCwk/wkJBv8QCQL0Fg0DTyMVBQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHl4dABycG0ooJ6a7Xd1cf9RT0r/amZg/3dzbP98eHH/f3t0/4F8dP+CfXX/jId//6Oe + lv++urT/2NXQ/+fl4v/u7On/7u3q/+3r6P/q6OX/5+Xi/+Ti3//i4Nz/393Y/93a1f/a19L/19TP/9TR + zP/Sz8j/0MzF/83Jwv/Kxr//x8O8/8TAuP/BvbT/vbiw/7mzq/+0sKj/s6+o/7Ovqf+qp6D/mZSM/42I + gP+KhH3/hoJ7/2ViWv9DQjf/Ojkw/xUVEP8JBQH/GA0EoQIAAAcKBQIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKA + fACHhYIogX977FFPS/9cWVT/b2tl/3ZybP96dm7/fHhw/4eCev+inpb/yMS//+Xj4P/z8vD/9vX0//Tz + 8f/x8O7/7+3q/+zq5//p5+T/5+Xi/+Xj3//j4N3/4N7Z/97b1v/b2NP/2NXQ/9bTzv/T0Mr/0c7H/87L + xP/Mx8H/ycS9/8bCuv/Dv7f/wLyz/7+6sf++ubD/vLev/725sv/Fwrz/w8C7/6mlnv+OiYL/hYB5/3Zy + a/9JRz7/QkE2/yYlHv8GBQP/EwsC5Q4GADMJAwAABgUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBuagB4dnIfV1VR2E1L + R/9kYVv/cG1n/3Vxav97d3D/lZGK/8TBvP/o5+T/+Pf2//n49//29vT/9PPx//Lx7//w7+3/7u3q/+zq + 5//p5+T/5+Xi/+Xj3//j4d3/4d/a/9/c1//d2dT/2tbR/9fUz//U0cz/0s/I/9DMxf/NycL/y8a//8fD + vP/Fwbn/wr61/8C8s//AvLP/wLuy/7+6sf/BvbT/zcrE/9bU0f+7uLP/j4uF/397c/9WVEz/QkE3/zQz + Kv8VFRL/Ih0X/y8nH3wAAAABAQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRABMSUUXREI+yVJQS/9nZF//cG1m/315 + c/+npJ7/3NrX//b29P/5+fj/+Pf2//b19P/19PL/8/Lw//Lw7v/w7uz/7uzp/+zq5//p5+T/6OXi/+bj + 4P/j4d7/4d/b/9/d1//d2tX/29fS/9jVz//V0s3/09DJ/9HNxv/PysT/zMfA/8nFvv/Gwrv/xL+3/8K+ + tf/CvbX/wb20/8G8tP/AvLP/wr62/9TQy//l4+H/yMbC/4+LhP9pZV7/SEY9/19eVv90cm3/rKaf/2Ve + VsoAAAAZGxYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVEPwBFQz4hRkRA41dUT/9oZV//fXp0/7a0sP/p6Ob/+fj3//j3 + 9v/29fT/9fTz//X08v/z8vD/8vHv//Hv7f/v7uv/7uvo/+vp5v/p5+T/5+Xi/+bj4P/k4d3/4t/b/9/d + 2P/e2tX/29jT/9jV0P/W083/1NDL/9LOx//PzMT/zcjC/8vGv//Iw7z/xcG6/8TAuP/Ev7f/w7+2/8O/ + tv/DvrX/wr61/8bBuf/a2NP/7ezq/8PBvf9+enT/ZWJc/7q4s/9ubWj/T0xH/yIbFPYSCQJXGA4FABcK + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAExLRwBMS0clTEpH7VpXUv9zcGv/tbOv//Hx7//5+Pf/9vXz//Xz8v/08/H/9PPx//Py + 8P/y8e7/8e/t//Du6//u7On/7ern/+ro5f/p5uP/5+Xi/+bj4P/k4d3/4t/b/+Dd2P/e2tX/3NjT/9nW + 0f/X087/1dHL/9LPyP/QzcX/zsrC/8zHwP/JxL3/x8K7/8bCuv/Gwbr/xcG5/8XBuf/FwLj/xMC3/8S/ + t//Iw7z/4d7b//Lx8P+2tLD/Y2Bb/2NhWP9DQTj/FRUQ/wYDAP8YDgSmAAAACQYCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBO + SgBPTkolT01J7GRiXf+qqKT/7u3r//j39v/08/H/8/Lw//Py8P/y8e//8vHv//Hw7v/x7+3/8O7r/+/t + 6f/t6+j/7Onm/+ro5P/o5uP/5+Xh/+Xj3//k4d3/4t/b/+Dd1//e29b/3NjT/9rW0f/X1M7/1dLM/9PQ + yf/Rzcb/z8vD/83Iwf/Lxr//ycS9/8nEvf/Iw7z/yMO8/8fDu//Hwrv/xsK6/8bBuv/Fwbn/zcnC/+7t + 6f/r6+n/jouH/0A+Nv9DQjf/Kikh/wgHBP8SCgLnFg0DMx4RBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBQT0slUlBM7IeF + gf/g393/9/b1//Px7//y8O7/8vDu//Lw7v/x8O3/8e/t//Du7P/v7ur/7+3p/+7r6P/s6ub/6+jl/+nn + 5P/o5eL/5uTg/+Xj3v/j4dz/4t/a/+Dd1//f29b/3dnT/9vW0f/Y1M//1tLM/9TQyv/Szsf/0MzE/87J + wv/Mx8D/zMa//8vGvv/Lxb7/ysW+/8rEvf/JxL3/ycS8/8jDvP/Hw7v/yMK7/9vY0v/5+Pf/wcC+/09N + SP9ZV07/XlxU/xUUEP8LBgH+FAsCeyUWBgD/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFNSTgBPTkolX15a7Le1s//z8vD/8/Hu//Dv + 7P/x7+z/8O/s//Dv7P/w7uv/8O7q/+/t6f/u7Oj/7evn/+3q5v/r6eX/6ufk/+jm4//n5eH/5uTg/+Xi + 3v/j4Nz/4t/Z/+Dd1//f3Nb/3dnT/9vX0f/Y1c//1tPN/9XRy//Tz8f/0c3F/8/Lw//OycL/zcnB/83I + wf/Nx8D/zMfA/8zGv//Mxr//y8a//8vFvv/Kxb7/ycS9/8/Kw//t6+j/4uHg/2VkYP9YVk/9hoN9/z07 + Nf8FAwH/FAsDwgsEABMHAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtZVgBRUEwlfHp37NrZ1//z8u//8O7q//Dt6v/w7er/7+3q/+/t + 6f/v7en/7+zo/+7s6P/t6+f/7enm/+zo5f/q6OT/6efj/+jl4v/n5OD/5uPf/+Xi3f/j4Nz/4t/Z/+Dd + 1//f3Nb/3dnU/9vX0f/Z1c//19PN/9XRy//T0Mj/0s7G/9HMxP/QzMT/0MzE/8/Lw//Py8P/z8rC/87J + wv/OyMH/zcjB/83HwP/Mx8D/zMa//8zHv//e29b/7+7t/4uKh/45ODLrh4V//V9dVv8REQ3/CgUA8xQK + AUccDwIABQMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGlnZABbWlcmlZSR7ejm5P/x7+v/7+3p/+/t6P/v7en/7+3p/+7s6P/u7Oj/7uvn/+3q + 5//t6eb/7Onl/+ro5P/p5+P/6ebi/+jl4f/n5OD/5eLe/+Th3f/j4Nv/4t7Z/+Dd1//f3NX/3dnU/9zX + 0f/a1dD/2NTO/9bSy//U0Mn/08/H/9POx//Szsb/0s7G/9HNxf/RzcX/0czE/9DMxP/Qy8P/z8vD/8/L + w//PysL/zsnC/83Iwf/W0sz/7uzq/6Oinv4zMi7CbWtk3ISBe/8sKyX/BgQB/xEJApQDAQACAQEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJx + bQBlZGAnoqGe7ezp5v/w7en/7uzn/+7s6P/u7Oj/7uzo/+7r5//u6uf/7erm/+3p5v/s6OX/6+jk/+rn + 4//p5uL/6OXh/+fk4P/m49//5eLe/+Th3f/j4Nv/4d7Y/+Dd1v/f3NX/3tnU/9zX0v/a1tD/2NTO/9fT + zP/V0cv/1dHK/9XQyf/U0Mn/1M/I/9PPx//Tz8f/0s7G/9LOxv/SzcX/0c3F/9HNxf/QzMT/0MzE/8/L + w//Tz8j/6Obi/6mnpP88OzecTkxFkoiGgP9WVE7/CQgG/w4HAdULBQEgBAEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHt5dgBxcGwpq6mm7e3r + 6P/v7ej/7uzn/+7s5//u6+f/7urn/+7q5//t6ub/7enm/+zp5f/s6OT/6ufj/+nn4//p5uL/6OXh/+fk + 4P/m497/5eLd/+Th3P/j4Nr/4t7Y/+Hd1//g3NX/3tnU/93Y0v/b1tD/2dXO/9fTzf/X083/19LM/9bS + zP/W0sv/1dHL/9XRyv/V0Mr/1NDJ/9TPyP/Tz8j/08/H/9POx//Szsb/0s3G/9HNxf/Uz8j/5ePe/6im + ov9CQT2PJyYfOYOBe+h7eXP/IiEc/wUCAPgRCQFbIxICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKBfQB7enYnr66q6u3r5//v7Of/7uvn/+7q + 5v/u6ub/7urm/+3q5v/t6eX/7enl/+zo5P/r6OP/6ufj/+nn4v/p5uH/6OXg/+fk3//m497/5eLd/+Th + 3P/j4Nr/4t7Y/+Hd1//g3Nb/39rU/93Y0v/c19H/29bQ/9vWz//a1c//2dXO/9nUzv/Y1M3/2NPN/9fT + zP/X0sz/1tLM/9bSy//W0cv/1dHK/9XQyv/U0Mn/1NDI/9PPx//V0cn/4+Db/6Shnv9DQj56AAAABmJg + Wp+Uko3/SEZA/wYFAv8KBACmDAIACAsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHp3cwBsamYcpaKf0+fk4f/v6+f/7uvm/+7q5v/u6ub/7urm/+3p + 5f/t6eX/7Ojk/+zo5P/r6OP/6ufi/+nm4v/p5uH/6OXg/+fk3//m497/5uLd/+Xh3P/k4Nr/49/Y/+He + 1//g3Nb/39vU/97Z0//e2NL/3djS/93Y0v/c19H/3NfR/9vW0P/b1tD/29bP/9rVz//Z1c//2dTO/9jU + zv/Y083/19PN/9fTzP/W0sz/1tLL/9bRy//X08z/39zX/5uYlPtGREBUW1lTADk3MkSPjYjsdXJs/xgX + FP8FAQDeCgMAJgkDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHFuagBQTUoKnZuWp93b1//w7ej/7+vm/+7q5v/u6ub/7urm/+7q5f/t6eX/7enk/+zo + 5P/s6OP/6ufi/+nm4v/p5uH/6OXg/+jk3//n497/5uLd/+Xh3P/k4Nv/49/Z/+Le1//h3db/4d3W/+Dc + 1f/g29T/39rU/9/a1P/e2dP/3tnT/97Y0v/d2NL/3dfR/9zX0f/c19H/29bQ/9rW0P/a1c//2dXP/9nV + zv/Z1M7/2NTN/9jTzf/a1s//2dXP/5CNiOZGREEuRUM/AAAAAAt6eXOolJKO/z89N/8CAQD5BAAAZAUA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1q + aACqp6IAkY6Kbs/Lx/zv7Of/7+zm/+/r5v/v6ub/7+rm/+7q5f/u6eX/7enk/+3o5P/s6OP/6+fi/+rn + 4v/p5uH/6eXg/+jk3//n497/5uPd/+bi3P/l4dv/5ODa/+Pf2f/j39j/4t7Y/+Le1//h3df/4d3W/+Hd + 1v/g3NX/4NzV/9/b1P/f29T/39rT/97Z0//e2NL/3djS/93Y0v/d19H/3NfR/9zW0P/b1tD/29bP/9rV + z//c2NH/x8S+/316dbY8ODQOT0tHAGVjWQBQT0hQmJeT8nRxa/8SEQ7/AAAApAAAAAkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL+4tACQjIkAhIF9Kri1 + sdvp5eD/8O3n//Ds5//v6+b/7+rm/+/q5f/u6eX/7unl/+3o5P/t6OP/7Oji/+zn4v/q5uH/6eXh/+nl + 3//o5N//5+Pe/+fj3f/m4t3/5uLc/+Xh2//l4dv/5ODa/+Tg2f/j39n/49/Y/+Pf2P/i3tf/4t7X/+Le + 1//h3db/4d3W/+Dc1f/g3NX/39vU/9/b1P/f2tT/3tnT/97Z0//d2NL/3djS/93X0f/b1tD/sK2n+nVy + bmiloZkAQ0ZLACIhHAAAAAANkI+KspeUkP9APjn/AAAA3wAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFg38AVlZSBJ6cl4bU0Mz+8Ozn//Ht + 5//x7ef/8Ozm/+/r5v/v6uX/7unl/+7p5P/u6eT/7ejj/+zo4v/s5+L/6ubh/+rm4f/p5eD/6eXg/+nl + 3//o5N//6OTe/+fj3v/n493/5uLd/+bi3P/m4tz/5eHb/+Xh2//k4Nr/5ODZ/+Tg2f/j39j/49/Y/+Le + 1//i3tf/4d3W/+Hd1v/h3db/4NzV/+Dc1f/f29T/39rU/9/a1P/OycP/mpeRxHZybRp+e3UAU1NTAAAA + AADGwr0AeHZxT6imo/N5dnD/ERAO+QAAAGMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPytAAhoN9AIiGgCCyr6vI49/a//Dt5//x7ef/8e3o//Ds + 5//w6+b/8Ovm/+/r5f/v6uX/7+rl/+7p5P/u6eT/7ejj/+3o4//s6OL/6+fi/+vn4f/q5uH/6ubg/+nl + 4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3f/m4t3/5uLc/+bi3P/l4dv/5eHa/+Tg2v/k4Nn/49/Z/+Pf + 2P/j39j/4t7X/+Le1//h3db/4d3W/9vX0f+yr6nth4N+VJ6UlQB7e3IAAAAAAAUFAAAmJSIAPjw4DKup + pq+hnpr/RUM+/wAAAKAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDwsIAGz0YALu4tQCWk5BKwb645erm4P/x7Of/8u7p//Lv6v/w7ef/8Ozm//Ds + 5v/w7Ob/8Ozm/+/r5f/v6+X/7+vl/+/q5f/u6uT/7unk/+7o4//t6OP/7Oji/+zn4v/r5+L/6+bh/+rm + 4f/q5eD/6eXg/+nl3//o5N//6OTe/+fj3v/n493/5+Pd/+bi3f/m4tz/5eHc/+Xh2//k4Nr/5ODa/+Tg + 2f/j39n/4NzW/8C8t/uTkIqFdnRxCI2LhQDPz8EAAAAAAAAAAAAuLisA1dTRANXTzkmvrarzkIyG/xwb + GdEAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAeHhvAHZ2cgANFBQCnpyWZ8vHwu7q5eD/8e3o//Pw7P/z8Ov/8e3n//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm/+/r5f/v6+X/7+rl/+7p5P/u6eT/7ejj/+3o4//s5+L/7Ofi/+vn + 4f/r5uH/6ubh/+rl4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3v/n493/5uLd/+bi3P/j39n/yMW//5mW + kaNoZWERfn95AP///wAAAAAAAAAAAAAAAAAAAAAA0c3FAOPe1Q/KycazqaWh/3FtaPQODQxCLCooAK+n + nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH19 + eACRkIsAnJmVBaGfmmfBvrnl5uHc//Lu6f/18u7/9PHt//Hu6f/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/7+vl/+/q5f/u6uT/7unk/+7p5P/t6OP/7ejj/+zn + 4v/s5+L/6+fh/+rm4f/q5uD/6eXg/+nl4P/p5d//6eXf/+Pf2v/EwLv6m5eSm3p4cxuMioMAoKmXAH5z + cwAAAAAAAAAAAAAAAAAAAAAA3NjSAN7c2QDh3ttQtbOx8bKtp/+inZaLZmFaAq6mngAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVkYoAlZONAFJY + VQKZlZBOuLWw0dvX0v/v6+b/9fPv//b08P/z8Oz/8e3n//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm//Dr5v/v6+X/7+rl/+/q5f/u6eT/7unk/+3o + 4//t6OP/7Ofi/+zn4v/q5eD/3dnU/7u4s+uVkYx/dHNtD5SSjQAAAAAALCsqAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAODc1gDk3tYK2NbTn6qmo//HwLnTwbuyHbmyqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj42IAIaCfgD///8AiYeFK6ej + n5/Hw77x5eHc//Tx7f/49vP/9/Tx//Pw7P/x7ej/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w6+b/8Ovm//Dr5v/v6uX/7unk/+bh + 3P/KxsH9qKahxIuJhU1UUE4Fe3lzAI6LhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wDl4t4A7OnlLrCuqsOnpJ6e2NDJD763sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH11cgCPgoEAkI6LAHdycAuXlJBUsq+ruMvH + w/Xl4t7/9fPx//r49v/59/T/9fLu//Lv6v/x7ef/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8e3n/+/r5f/l4Nv/zsrF/bKuqdiTkIx7endzHK2m + qwCHhH8Ah4N/ALa2tgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM/JxADLyMEA4+DYAb26 + sxOmpJ4NuLGrAL63sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AY2VzAIyIhQCxq6cAiIaDE5qYlFauq6exxsTA6dza + 1//v7ev/+Pf1//r59//49vP/9vPv//Tw7P/y7+r/8u7p//Hu6P/x7ef/8e3n//Ht5//x7ef/8e3n//Hu + 6P/y7uj/8O3n/+3p4//m4tz/2tbR/8bCvfauq6bGmJWQeICAeigAAAABiYeCAHh4cADJyLwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADX1M8A6ebhALm2sACmpJ4AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSUjgCanJYAkI2KALy4swBqamkLjoyIN6OhnXSysKy3xsTA39bU + 0ffi4d7/7Orn//Du7P/z8O3/8/Dt//Lw7P/x7ur/8Ozo/+/r5//u6uX/6OXg/+Lf2v/Z1dD/zMjD/b67 + t+etqqbJn5yXko6MiExwb2sY////AI2MhQB+fHoAlpOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAADAwMAP///wCEgoEAlJGPAP///wBcW1kJe3p2LJGPi1KjoZ13rKmmmrKv + rLq5trPRvLq33L27t+C6uLTlt7Wx4ra0sNuyr6vYqKaiwaGemqeal5OEjouHYHx6djhraGcSAAAAAZqY + kwB+fHgAXmBeAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8Axse9ADUzMQBqa2oAfHt4AJCOigD///8AOjs5BmJgXg1+fHoThYSAFYB/ + eiF6eXUvenl2JoSDgBV8enYUZmVhDklHRQkAAAAClZKOAHx5dQBwbWwAeHd1AHJvbAD07OQABQUFAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUwB4eHUAenl3AIOBfwCUko8AmZeUAI+OigCGhYEAiIaDAJeV + kgCQjooAgn98AHRybwBwb2kAc3NsAP///wAgICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8AAAD///////////8A + AAD/////AH////8AAAD////AAAD///8AAAD///wAAAAf//8AAAD///gAAAAP//8AAAD//+AAAAAB//8A + AAD//4AAAAAA//8AAAD//wAAAAAAf/8AAAD//gAAAAAAP/8AAAD//AAAAAAAH/8AAAD/+AAAAAAAD/8A + AAD/8AAAAAAAB/8AAAD/8AAAAAAAB/8AAAD/8AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAP8A + AAD/4AAAAAAAAP8AAAD/4AAAAAAAAP8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8A + AAD/4AAAAAAAAD8AAAD/4AAAAAAAAD8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAA8A + AAD/4AAAAAAAAA8AAAD/4AAAAAAAAA8AAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcA + AAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAEA + AAD/8AAAAAAABAEAAAD/8AAAAAAABgEAAAD/+AAAAAAABwAAAAD/+AAAAAAADwAAAAD//gAAAAAAH4AA + AAD//wAAAAAAP4AAAAD//wAAAAAAf4AAAAD//4AAAAAA/8EAAAD///AAAAAD//8AAAD///wAAAAP//8A + AAD///4AAAA///8AAAD////gAAP///8AAAD///////////8AAAD///////////8AAAD///////////8A + AAA= + + + \ No newline at end of file diff --git a/DBChm/FrmProcessing.Designer.cs b/DBChm/FrmProcessing.Designer.cs new file mode 100644 index 0000000..86efa4a --- /dev/null +++ b/DBChm/FrmProcessing.Designer.cs @@ -0,0 +1,108 @@ +namespace DBCHM +{ + partial class FrmProcessing + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.lbMessage = new System.Windows.Forms.Label(); + this.panImage = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.lbMessage, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.panImage, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(776, 398); + this.tableLayoutPanel1.TabIndex = 1; + // + // lbMessage + // + this.lbMessage.BackColor = System.Drawing.Color.Transparent; + this.lbMessage.Dock = System.Windows.Forms.DockStyle.Fill; + this.lbMessage.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lbMessage.Location = new System.Drawing.Point(4, 0); + this.lbMessage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lbMessage.Name = "lbMessage"; + this.lbMessage.Padding = new System.Windows.Forms.Padding(0, 0, 0, 38); + this.lbMessage.Size = new System.Drawing.Size(768, 199); + this.lbMessage.TabIndex = 1; + this.lbMessage.Text = "lbMessage\r\nadsfadsf"; + this.lbMessage.TextAlign = System.Drawing.ContentAlignment.BottomCenter; + // + // panImage + // + this.panImage.Dock = System.Windows.Forms.DockStyle.Fill; + this.panImage.Location = new System.Drawing.Point(4, 203); + this.panImage.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.panImage.Name = "panImage"; + this.panImage.Size = new System.Drawing.Size(768, 191); + this.panImage.TabIndex = 2; + // + // FrmProcessing + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Control; + this.ClientSize = new System.Drawing.Size(776, 398); + this.Controls.Add(this.tableLayoutPanel1); + this.Cursor = System.Windows.Forms.Cursors.WaitCursor; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.Name = "FrmProcessing"; + this.Opacity = 0.85D; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "FrmProcessing"; + this.Load += new System.EventHandler(this.FrmProcessing_Load); + this.Shown += new System.EventHandler(this.FrmProcessing_Shown); + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label lbMessage; + private System.Windows.Forms.Panel panImage; + + + } + + +} diff --git a/DBChm/FrmProcessing.cs b/DBChm/FrmProcessing.cs new file mode 100644 index 0000000..204841f --- /dev/null +++ b/DBChm/FrmProcessing.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace DBCHM +{ + public partial class FrmProcessing : Form + { + private static Image m_Image = null; + + private EventHandler evtHandler = null; + + private Action workAction = null; + private object workActionArg = null; + + private Thread workThread = null; + + public string Message + { + get + { + return lbMessage.Text; + } + set + { + lbMessage.Text = value; + } + } + + public bool WorkCompleted = false; + + public Exception WorkException + { get; private set; } + + public void SetWorkAction(Action workAction, object arg) + { + this.workAction = workAction; + this.workActionArg = arg; + } + + public FrmProcessing(string msg) + { + InitializeComponent(); + this.Message = msg; + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + if (m_Image != null) + { + //获得当前gif动画下一步要渲染的帧。 + UpdateImage(); + + //将获得的当前gif动画需要渲染的帧显示在界面上的某个位置。 + int x = (int)(panImage.ClientRectangle.Width - m_Image.Width) / 2; + int y = 0; + //e.Graphics.DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height)); + panImage.CreateGraphics().DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height)); + } + if (this.WorkCompleted) + { + this.Close(); + } + } + + + private void FrmProcessing_Load(object sender, EventArgs e) + { + if (this.Owner != null) + { + this.StartPosition = FormStartPosition.Manual; + this.Location = new Point(this.Owner.Left, this.Owner.Top); + //MessageBox.Show(string.Format("X={0},Y={1}", this.Owner.Left, this.Owner.Top)); + this.Width = this.Owner.Width; + this.Height = this.Owner.Height; + } + else + { + Rectangle screenRect = Screen.PrimaryScreen.WorkingArea; + this.Location = new Point((screenRect.Width - this.Width) / 2, (screenRect.Height - this.Height) / 2); + } + + //为委托关联一个处理方法 + evtHandler = new EventHandler(OnImageAnimate); + + if (m_Image == null) + { + Assembly assy = Assembly.GetExecutingAssembly(); + //获取要加载的gif动画文件 + m_Image = Image.FromStream(assy.GetManifestResourceStream(assy.GetName().Name + ".Resources.loading.gif")); + } + //调用开始动画方法 + BeginAnimate(); + } + + + //开始动画方法 + + private void BeginAnimate() + { + if (m_Image != null) + { + //当gif动画每隔一定时间后,都会变换一帧,那么就会触发一事件,该方法就是将当前image每变换一帧时,都会调用当前这个委托所关联的方法。 + ImageAnimator.Animate(m_Image, evtHandler); + } + } + + //委托所关联的方法 + + private void OnImageAnimate(Object sender, EventArgs e) + { + //该方法中,只是使得当前这个winform重绘,然后去调用该winform的OnPaint()方法进行重绘) + this.Invalidate(); + } + + //获得当前gif动画的下一步需要渲染的帧,当下一步任何对当前gif动画的操作都是对该帧进行操作) + + private void UpdateImage() + { + ImageAnimator.UpdateFrames(m_Image); + } + + //关闭显示动画,该方法可以在winform关闭时,或者某个按钮的触发事件中进行调用,以停止渲染当前gif动画。 + + private void StopAnimate() + { + m_Image = null; + ImageAnimator.StopAnimate(m_Image, evtHandler); + } + + private void FrmProcessing_Shown(object sender, EventArgs e) + { + if (this.workAction != null) + { + workThread = new Thread(ExecWorkAction); + workThread.IsBackground = true; + workThread.Start(); + } + } + + private void ExecWorkAction() + { + try + { + var workTask = new Task((arg) => + { + this.workAction(arg); + }, + this.workActionArg); + + workTask.Start(); + Task.WaitAll(workTask); + } + catch (Exception ex) + { + this.WorkException = ex; + } + finally + { + this.WorkCompleted = true; + } + + } + } +} \ No newline at end of file diff --git a/DBChm/FrmProcessing.resx b/DBChm/FrmProcessing.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/DBChm/FrmProcessing.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DBChm/GridFormMgr.Designer.cs b/DBChm/GridFormMgr.Designer.cs new file mode 100644 index 0000000..c4cacc7 --- /dev/null +++ b/DBChm/GridFormMgr.Designer.cs @@ -0,0 +1,185 @@ +namespace DBCHM +{ + partial class GridFormMgr + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GridFormMgr)); + this.linkAdd = new System.Windows.Forms.LinkLabel(); + this.linkEdit = new System.Windows.Forms.LinkLabel(); + this.linkRemove = new System.Windows.Forms.LinkLabel(); + this.linkClone = new System.Windows.Forms.LinkLabel(); + this.GV_DBConfigs = new System.Windows.Forms.DataGridView(); + this.BtnConnect = new ComponentFactory.Krypton.Toolkit.KryptonButton(); + this.BtnCancel = new ComponentFactory.Krypton.Toolkit.KryptonButton(); + ((System.ComponentModel.ISupportInitialize)(this.GV_DBConfigs)).BeginInit(); + this.SuspendLayout(); + // + // linkAdd + // + this.linkAdd.AutoSize = true; + this.linkAdd.Location = new System.Drawing.Point(22, 10); + this.linkAdd.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.linkAdd.Name = "linkAdd"; + this.linkAdd.Size = new System.Drawing.Size(29, 12); + this.linkAdd.TabIndex = 0; + this.linkAdd.TabStop = true; + this.linkAdd.Text = "新建"; + this.linkAdd.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkAdd_LinkClicked); + // + // linkEdit + // + this.linkEdit.AutoSize = true; + this.linkEdit.Location = new System.Drawing.Point(63, 10); + this.linkEdit.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.linkEdit.Name = "linkEdit"; + this.linkEdit.Size = new System.Drawing.Size(29, 12); + this.linkEdit.TabIndex = 0; + this.linkEdit.TabStop = true; + this.linkEdit.Text = "编辑"; + this.linkEdit.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkEdit_LinkClicked); + // + // linkRemove + // + this.linkRemove.AutoSize = true; + this.linkRemove.Location = new System.Drawing.Point(103, 10); + this.linkRemove.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.linkRemove.Name = "linkRemove"; + this.linkRemove.Size = new System.Drawing.Size(29, 12); + this.linkRemove.TabIndex = 0; + this.linkRemove.TabStop = true; + this.linkRemove.Text = "删除"; + this.linkRemove.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkRemove_LinkClicked); + // + // linkClone + // + this.linkClone.AutoSize = true; + this.linkClone.Location = new System.Drawing.Point(142, 10); + this.linkClone.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.linkClone.Name = "linkClone"; + this.linkClone.Size = new System.Drawing.Size(29, 12); + this.linkClone.TabIndex = 0; + this.linkClone.TabStop = true; + this.linkClone.Text = "克隆"; + this.linkClone.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkClone_LinkClicked); + // + // GV_DBConfigs + // + this.GV_DBConfigs.AllowUserToAddRows = false; + this.GV_DBConfigs.AllowUserToDeleteRows = false; + this.GV_DBConfigs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GV_DBConfigs.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.Raised; + this.GV_DBConfigs.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Sunken; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle1.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.GV_DBConfigs.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; + this.GV_DBConfigs.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.GV_DBConfigs.Location = new System.Drawing.Point(9, 34); + this.GV_DBConfigs.Margin = new System.Windows.Forms.Padding(2); + this.GV_DBConfigs.MultiSelect = false; + this.GV_DBConfigs.Name = "GV_DBConfigs"; + this.GV_DBConfigs.ReadOnly = true; + this.GV_DBConfigs.RowHeadersVisible = false; + this.GV_DBConfigs.RowTemplate.Height = 27; + this.GV_DBConfigs.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.GV_DBConfigs.Size = new System.Drawing.Size(762, 255); + this.GV_DBConfigs.TabIndex = 5; + this.GV_DBConfigs.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.GV_DBConfigs_CellDoubleClick); + this.GV_DBConfigs.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.GV_DBConfigs_CellFormatting); + // + // BtnConnect + // + this.BtnConnect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BtnConnect.AutoSize = true; + this.BtnConnect.DialogResult = System.Windows.Forms.DialogResult.OK; + this.BtnConnect.Location = new System.Drawing.Point(525, 302); + this.BtnConnect.Margin = new System.Windows.Forms.Padding(2); + this.BtnConnect.Name = "BtnConnect"; + this.BtnConnect.Size = new System.Drawing.Size(90, 26); + this.BtnConnect.TabIndex = 6; + this.BtnConnect.Values.Text = "连接"; + this.BtnConnect.Click += new System.EventHandler(this.btnConnect_Click); + // + // BtnCancel + // + this.BtnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BtnCancel.AutoSize = true; + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(652, 302); + this.BtnCancel.Margin = new System.Windows.Forms.Padding(2); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(90, 26); + this.BtnCancel.TabIndex = 7; + this.BtnCancel.Values.Text = "取消"; + this.BtnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // GridFormMgr + // + this.AcceptButton = this.BtnConnect; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(780, 337); + this.Controls.Add(this.GV_DBConfigs); + this.Controls.Add(this.BtnCancel); + this.Controls.Add(this.BtnConnect); + this.Controls.Add(this.linkClone); + this.Controls.Add(this.linkRemove); + this.Controls.Add(this.linkEdit); + this.Controls.Add(this.linkAdd); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "GridFormMgr"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "数据库连接管理"; + this.Load += new System.EventHandler(this.GridFormMgr_Load); + ((System.ComponentModel.ISupportInitialize)(this.GV_DBConfigs)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.LinkLabel linkAdd; + private System.Windows.Forms.LinkLabel linkEdit; + private System.Windows.Forms.LinkLabel linkRemove; + private System.Windows.Forms.LinkLabel linkClone; + private System.Windows.Forms.DataGridView GV_DBConfigs; + private ComponentFactory.Krypton.Toolkit.KryptonButton BtnConnect; + private ComponentFactory.Krypton.Toolkit.KryptonButton BtnCancel; + } +} \ No newline at end of file diff --git a/DBChm/GridFormMgr.cs b/DBChm/GridFormMgr.cs new file mode 100644 index 0000000..3aeccf9 --- /dev/null +++ b/DBChm/GridFormMgr.cs @@ -0,0 +1,185 @@ +using ComponentFactory.Krypton.Toolkit; +using MJTop.Data; +using System; +using System.Windows.Forms; + +namespace DBCHM +{ + public partial class GridFormMgr : KryptonForm + { + public GridFormMgr() + { + InitializeComponent(); + + //为KeyDown能应用到所有控件上 注册 KeyDown 事件 + foreach (Control control in this.Controls) + { + control.KeyDown += control_KeyDown; + } + } + + + + void control_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + this.Close(); + } + } + + DBForm dbForm = null; + private void linkAdd_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + dbForm = new DBForm(OPType.新建); + var dia = dbForm.ShowDialog(); + if (dia == DialogResult.OK) + { + RefreshListView(); + } + } + + private void linkEdit_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (GV_DBConfigs.SelectedRows.Count <= 0) + { + MessageBox.Show("请选择连接!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + int Id = Convert.ToInt32(GV_DBConfigs.SelectedRows[0].Cells[0].Value); + DBForm dbForm = new DBForm(OPType.编辑, Id); + var diaResult = dbForm.ShowDialog(); + if (diaResult == DialogResult.OK) + { + RefreshListView(); + } + } + + private void linkRemove_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (GV_DBConfigs.SelectedRows.Count <= 0) + { + MessageBox.Show("请选择连接!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + if (MessageBox.Show("确定要删除该连接吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK) + { + int Id = Convert.ToInt32(GV_DBConfigs.SelectedRows[0].Cells[0].Value); + ConfigUtils.Delete(Id); + + RefreshListView(); + } + } + + private void linkClone_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (GV_DBConfigs.SelectedRows.Count <= 0) + { + MessageBox.Show("请选择连接!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + int Id = Convert.ToInt32(GV_DBConfigs.SelectedRows[0].Cells[0].Value); + DBForm dbForm = new DBForm(OPType.克隆, Id); + var diaResult = dbForm.ShowDialog(this); + if (diaResult == DialogResult.OK) + { + RefreshListView(); + } + } + + private void btnConnect_Click(object sender, EventArgs e) + { + if (GV_DBConfigs.SelectedRows.Count <= 0) + { + MessageBox.Show("请选择连接!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + int Id = Convert.ToInt32(GV_DBConfigs.SelectedRows[0].Cells[0].Value); + DBCHMConfig config = ConfigUtils.Get(Id); + + + if ((DBType)Enum.Parse(typeof(DBType), config.DBType) == DBType.SqlServer + && !GV_DBConfigs.SelectedRows[0].Cells[6].Value.ToString().Equals("sa", StringComparison.OrdinalIgnoreCase)) + { + var dia = MessageBox.Show("非超级管理员的账号,可能因权限不足,查询不出表结构信息,确定要继续吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (dia == DialogResult.Cancel) + { + return; + } + } + + FormUtils.ShowProcessing("正在查询表结构信息,请稍等......", this, arg => + { + try + { + DBUtils.Instance = DBMgr.UseDB((DBType)Enum.Parse(typeof(DBType), config.DBType), config.ConnString, 300); + ConfigUtils.UpLastModified(Id); + } + catch (Exception ex) + { + LogUtils.LogError("连接数据库失败", Developer.SysDefault, ex, config); + MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + }, null); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void GridFormMgr_Load(object sender, EventArgs e) + { + RefreshListView(); + } + + /// + /// 刷新列表 + /// + private void RefreshListView() + { + var data = ConfigUtils.SelectAll(); + if (data != null) + { + GV_DBConfigs.DataSource = data; + + if (data.Count > 0) + { + GV_DBConfigs.Columns[0].Visible = false; + GV_DBConfigs.Columns[1].Width = 150; + } + } + + } + private void GV_DBConfigs_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + btnConnect_Click(sender, e); + //代表已经正常选中 + FormUtils.IsOK_Close = true; + this.Close(); + } + + private void GV_DBConfigs_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + if (e.ColumnIndex == 7) + { + if (GV_DBConfigs.Columns[e.ColumnIndex].DataPropertyName == "Pwd") + { + string str = e.Value.ToString(); + string strEncrypt = string.Empty; + for (int j = 0; j < str.Length; j++) + { + strEncrypt += "*"; + } + e.Value = strEncrypt; + } + } + + } + } +} diff --git a/DBChm/GridFormMgr.resx b/DBChm/GridFormMgr.resx new file mode 100644 index 0000000..9f93bf7 --- /dev/null +++ b/DBChm/GridFormMgr.resx @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEASEgAAAEAIACIVAAAFgAAACgAAABIAAAAkAAAAAEAIAAAAAAAAFEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8vLwA+LS4ASkVBAEtL + RABLS0YAS0tEAElHQgBAQD8APz8/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE1KQwBMSUMAU1BLAFtYUgBdWlQAXVxVAGBeWABramMADggHAyotJQYpKykFKiwlBiEf + HQSAfXIAYF5ZAF9cVgBcWlQAWVdRAFZUTgBNTEYAQjc6AEc/PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0xJAAAAAABQSUEAQ0E+AFVS + TACEhYEAREE9CFVRTBVaV1A1WllSVl5cVm9iX1mCZmNdlGhmX5tpZ2CbaGZfm2VjXJhhX1mFX1xXdVxa + VF1ZVlFAVFFLHkpKQwwABAABW1pRAEtJQgAbJBgARUU5AElJPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBPzwAODY1AFFPSQCFgnYAREM/DVZTTjlcWlRwYV9YqGdl + X9RvbGXodXJr9Hl2b/59enL/hIB4/4eEfP+JhX3/iIR9/4WBev9/fHT/e3dw/3d0bfdyb2jsamdh3WRh + W7heXFWDWFVPSktKQxUAAAABWVZTAEhFQwBPS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT01HAExKRABYVk8AAAAAAk9NSCZbWVNwYmBavmxpYul0cWr/fnpy/4eDe/+Pi4L/lZCI/5qV + jP+dmZD/oZyT/6Self+ln5f/paCX/6Oflf+gm5L/nZiP/5qVjP+UkIj/jomB/4WBev96d3D/cG1m8mVj + XcxbWlOJU1BLOz07OwhcWlQATk1GAGdmWQAvLy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSAAAAAAAU1FMAP// + /wBQTkgsWFZQimJgWdlua2T+e3dw/4eDe/+RjIT/mJOL/56ZkP+inZT/pJ+X/6ehmP+po5r/qqSb/6ul + nP+rpZz/rKad/6ymnf+rpZz/qqSb/6mkm/+oopn/pZ+W/6Cbkv+alYz/kIyE/4SAef92c2z/aGVf6l1b + VaVTUUtBQ0M7BldVTgBIRj8AT0xHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkI/ADwxLQBTUEwAT05IE1lXUW9fXVfaamdh/3l1 + bv+Ggnv/kY2F/5mUi/+dmI//oJuS/6KdlP+kn5b/pqGY/6eimf+po5r/qqSb/6ulnP+rpZz/q6Wc/6ym + nf+rpZz/q6Wc/6ulnP+qpJv/qaOa/6eimf+loJf/op2T/5yXj/+Tj4b/hoJ6/3Vya/9kYlzsV1VPk09O + SCZbXFsAS0dCAExHQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABFRT8ARUI9AG1xaQBRTUkvWVdSrWNgWvpxbmf/gHx1/4yIf/+Uj4f/mZOL/5yW + jv+emZD/oJuS/6KdlP+kn5X/paCX/6ahmP+moJf/pqCX/6Wflv+inZT/op2U/6Wflv+nopj/qKKZ/6mj + mv+qpJv/qKOa/6eimf+loJf/pJ+V/6Kdk/+empH/mZSL/46Kgv9/e3T/a2hi/1pYUs5NTEZTQjs3BEhH + QQBHR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVF + PwBHRkAAoJKOAFJQSUdYVlDPZGFb/3Zya/+EgHj/jYmA/5KOhf+VkYn/mZOL/5uWjf+dmI//npmQ/5yX + jv+Yk4r/ko2E/4qFff+BfHT/fHhw/3Vxaf9taWL/a2hg/3JuZv94dGz/e3hv/4aBef+Qi4P/mZSM/5+a + kf+inZT/o56U/6Gdk/+fm5H/nZiP/5qUjP+SjYX/hYF5/3BtZv9bWVPtTkxIcjw9OQZCQj0AOjo1AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk1IAEdHQQCdjYwATUxGS1RS + TN5iX1n/dnJr/4R/eP+Lh37/j4qC/5GNhP+UkIj/l5GJ/5aQiP+RjIP/iYR8/4B7c/92cWn/bmli/2hk + Xf9iXlf/XVlT/11ZU/9dWVP/WlZR/1VRTP9UUUv/V1RN/1tXUf9hXVb/bGdg/3dya/+GgXj/k46F/5qV + jP+dmI//nJeO/5qVjf+Xkor/kY2F/4aCev9zcGj/W1lU80lIQ3MtLigGOzo2AD48OgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Pz8ATktFAE1KRwBMSkU8T01I3F5bVv9yb2j/gHx1/4eC + e/+Lhn7/jomB/5CLg/+Qi4P/jId+/4WAd/9/eXH/fHdv/355cf+Dfnb/iIN7/4+Kgv+RjYX/ko2G/5iT + i/+dl4//m5aO/5GMhf+Mh4D/i4Z//4SAeP99eXH/dnJr/3BsZf9vamP/dXBp/4B7c/+MiH//lpCI/5iS + iv+WkYn/k4+H/4+Kgv+EgHj/cW1n/1dVT/NEQj5wMjAvBD8+OwBGRkQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADGu4cATkxFAEpIQxtLSkW/V1VP/21pY/97d3D/gn52/4WBev+JhHz/i4V9/4uF + fP+Jg3r/iYN6/4+JgP+ZlIz/paCX/6+qov+1saj/uLOr/7u2rv+8t67/vLiv/724r/+9uK//vLev/7u2 + rv+6taz/ubOr/7axqP+yraX/raif/6Sfl/+YlIz/kIuD/4yHf/+Lh3//jYiA/5CLgv+SjYX/kY2E/4+L + gv+Lhn7/f3t0/2lmYP9OTEfqQkE9TT87NgBKS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABISEQAT1JRAkZEQH1OTEb9ZGFb/3Vxa/98eXH/gHx1/4N/d/+Ef3f/hoB4/4+JgP+dmI//r6qj/765 + sf/Gwrr/ycS9/8jDvP/Fwbn/wr61/8K9tP/CvbT/wb20/8G9tP/BvLP/wLyz/8C7sv+/u7L/vrqx/765 + sP++ubD/vbiv/7y3rv+6taz/tbCo/7Cro/+sp5//pKCa/6Kdl/+Yk4v/kIuD/42IgP+Lhn7/hYF5/3dz + bP9dW1X/R0ZBx0ZFQx9FREEAWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBGRT8ARUQ+KEdF + QNpYVVD/bWlj/3dzbP97d3D/fXly/396cv+KhX3/pKCY/7+7tP/OysX/0s7I/8/Lw//Lx8D/ycS9/8fD + u//Fwbn/xcC4/8S/t//AvLP/u7au/7q2rv+6tq7/ubWt/7m1rP+7tq7/wLyz/8C8s//AvLP/wLyz/8C7 + sv+/urH/vrmw/724sP+9uK//l5ON/7Cspv++u7b/r6ul/5eSi/+JhHz/hYF5/356c/9raGH/UE5J/ENC + P3UAAAAAXl5eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBAQDoAREI9cUtIQ/1hXlj/cGxm/3Vx + a/94dG3/gX11/52Ykf/Bvbf/19PO/9fUzv/Szsf/zcjB/8rGvv/FwLn/ubWu/6+ro/+empP/kY2H/4aD + fP+Cf3j/gn54/4SAef+EgXr/hIB5/4J/eP+AfXb/gn95/4uIgf+Xk4z/pqKa/7Gtpf+6tq3/wbyz/8C8 + s//AvLL/rquk/52Ykv/Fwrr/z83I/8bDv/+gnZf/hoF6/397c/9zb2j/WldS/0NBPchAQD8bQkJBAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBPTkkMQ0E9rFBOSf9mY13/cG1n/3ZybP+MiIL/uLaw/9rX + 0//d29b/1NHK/9DKw//Iw7z/ubSt/52ak/+Jhn//endx/3Rxav94dW7/fXpz/4F9dv+Ggnr/jIiA/5CL + g/+RjYT/kYyE/4+Lg/+Khn7/hIB4/4B8df97d3D/dHFr/3Zzbf+Bfnf/kY6H/6umnv+7tq7/yMS9/5qV + kf+8tq7/xsK7/9nX0v/X1dL/p6Oe/4F9dv92cmv/Yl9Z/0ZEQO87OjhFNDIvAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEZEQABGREAdRUM/2FVSTv9oZV//c3Bq/5aTjv/Pzcr/5+Xi/93a1P/Tz8j/ycW9/62o + ov+Lh4H/dXJs/3Vxa/97eHH/hoN7/4+Lg/+Wkon/m5aO/5+bkf+inpT/paCX/6eimf+oo5r/qaOa/6ij + mv+moZj/o56V/6Cbkv+bl47/lZGI/46Jgf+Cf3f/eHVu/3Rxav9+e3T/qqaf/62ppP+qpZ3/v7qx/8nF + vv/i4Nz/4N/d/6eln/96dm//ZmNd/0lHQ/06OTdsNDMrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpI + RABJSEQkSkhE7FlWUf9raGP/mJaR/9va1//q5+T/3NnT/87Kw/+tqqP/hoN9/3FuZ/90cGr/gX52/42I + gP+WkYj/nJeO/6Cbkv+jnpX/paCX/6eimf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6qk + nP+po5r/pqGY/6Oelf+emZD/lpGJ/4uHf/98eHH/fntz/66rpf9tamT/Y2Ba/7ezq//NycP/6Ofk/+Xk + 4/+fnJf/aWZg/0xJRf85NzWDXVhLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5NSQBOTUklTkxI7F1b + Vv+Ni4b/2tjV/+3r6f/d2dP/vrqz/4eEfv9qZ2H/cW1m/397dP+MiH//lZCH/5qVjf+emZD/oJuS/6Kd + lP+kn5b/pqGX/6eimf+po5v/qqSb/6ulnP+rpZz/rKWc/6ymnf+spp3/q6Wc/6ulnP+qpJv/qaOa/6ei + mf+loJf/o56U/5+ZkP+Xkon/i4d//25rY/8wLyj/Dg0L/3Rxbf/Ev7j/1dLM//X08//X1dP/fHl0/0xK + Rf82NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBPTkolUE5L7HNwbf/KyMb/7+7r/9zZ + 0/+tqqT/cG1n/2RhW/91cmv/hYF5/4+Lg/+VkYj/mZSM/5yWjv+emZD/oJuS/6Kdk/+jnpX/pJ+V/6Oe + lf+hnJT/oZuS/52Xjv+blY3/mpWM/5yXjv+hnJP/o56U/6agmP+oopn/qKKZ/6eimP+loJf/pJ+V/6Kd + k/+fm5H/nJeO/3BtZf83Ni3/FRQQ/xsaGP+Cf3n/wLy1/+Xi3v/29vX/qaek/09NSf8yMS6aAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJRTABPTkolWFZS7KKgnv/q6Ob/4d3Z/6Sgmv9kYlv/ZmNd/3l1 + bv+Hgnv/joqB/5KOhf+VkIj/mZOL/5uVjf+cl47/m5aN/5eSif+QjIP/iIN7/396cv92cWr/cGxl/2hj + Xf9jX1j/YV1X/2NfWf9rZl//cGxl/3p2b/+FgXn/kIuD/5mUi/+fmpD/oZyT/6Gck/+fmpH/npmQ/4iD + fP9IRz3/JSQd/wcGBf8zMi//fnt1/8XBuv/08/H/1NPR/19dWf8wLiyZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFZVUQBQTkolbGpn7MvJxv/o5eL/op+a/1xaVP9kYVv/eHRt/4WBef+Lh3//joqC/5GN + hP+Uj4f/lZCI/5ONhf+Mh37/g311/3p1bf9xbWX/bWhh/2llXv9lYVr/ZGFa/2djXP9lYVr/Y19Z/2Fd + WP9bV1L/WlZR/11YUv9fW1X/ZmFb/29rY/98d2//ioV9/5aRiP+bl43/nJaO/5eRif9VU0r/NDMq/w4O + Cv8VFBP/UlBL/3t4cv/Z1tL/6+vp/3p5df8wLy2ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg + XABXVVEliIaD7d/d2v+0saz/W1lT/2BeWP90cWr/gX12/4eCe/+Khn7/jYmA/4+Kgv+OiYH/iYR7/4N9 + dP+AenL/gXxz/4aBef+NiYH/lZCJ/5yXj/+emZH/op2V/6eimv+nopr/p6Ka/6WgmP+dmJD/mZSM/5OP + h/+Khn7/gn12/3l1bf91cGj/dnJq/355cv+Ig3v/kYyD/5aRiP9qaGD/PTwy/x8eGP8FBAP/QDw3/1xa + VP+KiIT/5+bk/5eVkv81NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG9uagBiYV0nn56b7cnH + xP9mZF//WldR/3BtZv98eHH/gX52/4WBev+Ig3z/iYN7/4qEe/+KhHv/joiA/5iTi/+moZn/s66m/7u2 + rv++urL/vrqx/7+7sv/Au7L/wLuy/7+7sv+/urH/v7qx/765sP+9ubD/vbiv/7u2rv+5tKv/tbCo/6+q + of+nopn/nZiR/5aSi/+Tj4j/kIyE/5GMhP98eHD/RUQ6/y8uJv8KCgb/HRkW/2FdV/9ZV1L/qKek/6Wj + oP89PDmaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHVzcABraWYooZ+c7YiGgv9RTkn/aGRe/3Zy + a/98eHH/gHx0/4J+dv+CfnX/iIN6/5iTiv+sp6D/vbix/8fDvP/Lxr//y8a//8nEvP/Fwbn/w762/8K+ + tf/CvrX/wr21/8K+tf/CvbT/wb20/8C8s//Au7L/v7uy/7+6sf++urH/vrmw/724sP+8t6//urSs/7aw + qP+yraX/sKyl/66qpP+dmJL/VlVM/zo5L/8YFxL/CAUD/0xIQv9iYFn/Y2Fd/4qJhf9DQj6YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIF+ewCCgH0pioiF7VdVUP9aV1L/bmpk/3dzbP96d2//fHhx/4F8 + dP+Sjob/sKyl/8nFv//U0cv/0s7I/83Jwv/Kxr7/yMS8/8bCuv/Dv7f/wby0/765sf+1san/sq2l/6mm + nv+opJz/p6Sc/6+ro/+yrqb/uLSs/7y4r/++ubD/v7uy/8G8s//Au7L/v7qx/765sP+8uK//vLev/766 + tP/Fwr3/goB6/0JBN/8oKCD/BgUD/yYhG/9lYVv/VVNN/1dWUv9DQj6H////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHh2cgCDgH0hX11Z3k1KRv9iX1r/cG1m/3Vxav95dG3/h4N8/6yoov/Pzcj/2tfS/9bS + zP/Qy8T/zMfA/8jDvP+8uLH/rKmi/5uYkf+NioL/gH13/3h1b/94dW7/fHhx/3p3cP96d3D/endv/3x5 + cv96d3D/eHVu/3t4cv+HhH3/lJCJ/6OfmP+wrKT/vrqx/8G8s//AvLP/v7qx/766sf/Fwbr/sa6p/09O + Rf82Niz/EREN/wwIBP9QS0P/XVpV/0A+Ov0zMzBoPzw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJP + SwBWU08YRkRBy1FPSv9nZF7/cGxm/3h0bv+Xk47/x8XA/+De2v/d2tX/08/I/87Jwv/Au7T/qaSe/4uH + gf97eHL/dXJs/3Vya/98eXH/g394/4mFff+OioH/k46G/5eSiv+ZlIv/mZSL/5eSiv+Tjob/jYmB/4iE + ff+Cfnf/endv/3VybP94dW//goB5/5qXj/+wraT/v7qy/8K9tP/BvbT/vrmy/2tpYf8+PTP/ISEa/wUD + Af8tJx//XltV/0RCPvw3NjNbKigjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZEPwBGQz8fRkQ/3lZT + T/9oZV//dnNt/6OhnP/a2NX/5+Tg/9rWz//QzMT/vbmx/5eUjf96d3D/cW1n/3h0bf+Cfnb/jIiA/5SQ + iP+alo3/n5qR/6OelP+loJf/qKKZ/6mkm/+qpJv/q6Wc/6qlnP+po5r/p6GY/6Wflv+hnJP/nJeO/5WQ + iP+Lh3//f3x0/3Rxav9zcGn/h4R9/6qmnv+/u7L/xcC4/4yJgf9FRDr/MTAo/wsLCP8TDQf/TEdA/0tJ + RP47OTZxNzUvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBLSUUlS0lF7FpXUv9va2b/paOf/+Tj + 4P/n5eH/2NTO/8XAuf+Zlo//dXJs/25rZf95dW7/h4N7/5GMhP+ZlIv/npmQ/6Gck/+kn5b/paCY/6ei + mf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6ulnP+qpJv/qKKZ/6Wgl/+hnJP/m5aN/5KN + hP+EgHj/dXFq/3BtZ/+Fgnv/r6uj/6+ro/9SUUf/PDwy/xoaFP8GAwD/MSoi/0lHQv85NzWHcGlYAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSQBPTUklT01J7GBeWf+cmpb/4+Lg/+ro5P/X083/r6ul/3p3 + cf9pZmD/dnJr/4SAef+Pi4P/l5GJ/5uWjv+emZD/oJuS/6KdlP+kn5X/pqGX/6eimf+po5r/qqSb/6qk + m/+rpZz/q6Wc/6ulnP+rpZz/q6Wc/6ulnP+qpJv/qKOa/6eimf+loJf/pJ+V/6Cckv+alo3/kIyE/4J+ + dv9va2X/bWpk/5GNhv9raWH/QkE2/ysqIv8IBwX/GBEK/z06Nf83NjOZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFBPSwBQTkslUU9L7H58eP/Y1tT/7uvo/9TQyv+ZlpD/aWZh/2tnYf98eHH/iYV9/5GN + hP+WkYn/mZSM/5uWjv+emZD/oJuS/6Gck/+inZP/oZuS/52Yj/+alYz/lpGI/5CLg/+OioH/joqB/4+K + gv+VkIf/mpWM/5+Zkf+jnZX/paCX/6WhmP+loJb/o56V/6Kdk/+gm5H/nJeP/5aRiP+JhX3/d3Ns/2dl + Xv9aWFH/REM5/zg4Lv8TEw//CgYB/yokHP8yMS+aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJR + TQBPTkolW1lW7K6sqf/t6+j/1tLN/4uIgv9fXVf/bGli/356c/+JhX3/j4uC/5KOhf+VkIj/mJOL/5qV + jf+alYz/l5KK/5GMhP+Ig3v/fnlx/3NvZ/9qZl//ZGBZ/11ZU/9aVlD/V1RO/1hUT/9dWVP/Yl9Y/2xn + Yf93cmr/g352/5GMg/+alYz/n5qR/6Cbkv+fmpH/nZiP/5uVjf+WkYn/jIiA/3x4cf9hX1n/QUA3/0FA + Nf8kIxz/BgUD/xoSC/8oJSKamv//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlXUwBRT0sldHJv7NLQ + zv/h3tn/jImD/1tYUv9qZ2H/fHhx/4aCev+Lh37/joqB/5GNhP+Tj4b/k46F/4+Kgf+Hgnr/fnlx/3dy + av9ybWb/cGxk/29rZP9taWL/cW1m/3JuZ/9wbGX/bmtk/25qZP9oZV//Y19Z/2RfWf9iXVf/ZGBa/2tm + X/91cGj/gn51/4+Kgv+Yk4r/m5WN/5qUjP+Xkor/k46G/4uHfv97eHD/UVBI/0JBN/8zMin/Dg0K/w4I + Av8eFw+nDTBaATUaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjYABZV1QmkI6L7eDe2/+fnJj/V1RP/2Zj + Xf94dW3/gn53/4aCe/+KhX7/jYiA/46JgP+Nh37/h4J4/4N+dP+Efnb/ioR8/5KOhf+dmJH/p6Ob/7Cs + pP+zr6f/urav/7y4sf+7t7D/urau/7i0rP+xrKX/qaWd/6Sfl/+alY3/kYyE/4aCev99eHD/enVu/355 + cf+FgHj/jYiA/5SPhv+UkIj/ko6G/4+Lgv+JhH3/amdg/0RDOv8+PTP/HBwW/wcFAv8XDgTGCQUBEgIA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFwbABkY18noqCd7b+9uv9dW1f/XVtV/3NvaP99eXH/gX12/4WA + ef+Hgnv/iIJ6/4mDev+Nh37/l5KJ/6ikm/+7t7D/y8jC/9fUz//d2tb/3tzY/+Dd2f/e3Nf/3dvV/9vY + 0//Y1dD/1tPN/9PQyv/QzMX/zcnC/8nFvv/FwLn/wLuz/7m0rP+wq6L/pqCZ/56Zkf+ZlI3/lpGL/5OO + hv+QjIP/kIuD/46Kgf+Lh3//gHx1/09NRP9CQTf/LCwk/wkJBv8QCQL0Fg0DTyMVBQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHl4dABycG0ooJ6a7Xd1cf9RT0r/amZg/3dzbP98eHH/f3t0/4F8dP+CfXX/jId//6Oe + lv++urT/2NXQ/+fl4v/u7On/7u3q/+3r6P/q6OX/5+Xi/+Ti3//i4Nz/393Y/93a1f/a19L/19TP/9TR + zP/Sz8j/0MzF/83Jwv/Kxr//x8O8/8TAuP/BvbT/vbiw/7mzq/+0sKj/s6+o/7Ovqf+qp6D/mZSM/42I + gP+KhH3/hoJ7/2ViWv9DQjf/Ojkw/xUVEP8JBQH/GA0EoQIAAAcKBQIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKA + fACHhYIogX977FFPS/9cWVT/b2tl/3ZybP96dm7/fHhw/4eCev+inpb/yMS//+Xj4P/z8vD/9vX0//Tz + 8f/x8O7/7+3q/+zq5//p5+T/5+Xi/+Xj3//j4N3/4N7Z/97b1v/b2NP/2NXQ/9bTzv/T0Mr/0c7H/87L + xP/Mx8H/ycS9/8bCuv/Dv7f/wLyz/7+6sf++ubD/vLev/725sv/Fwrz/w8C7/6mlnv+OiYL/hYB5/3Zy + a/9JRz7/QkE2/yYlHv8GBQP/EwsC5Q4GADMJAwAABgUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBuagB4dnIfV1VR2E1L + R/9kYVv/cG1n/3Vxav97d3D/lZGK/8TBvP/o5+T/+Pf2//n49//29vT/9PPx//Lx7//w7+3/7u3q/+zq + 5//p5+T/5+Xi/+Xj3//j4d3/4d/a/9/c1//d2dT/2tbR/9fUz//U0cz/0s/I/9DMxf/NycL/y8a//8fD + vP/Fwbn/wr61/8C8s//AvLP/wLuy/7+6sf/BvbT/zcrE/9bU0f+7uLP/j4uF/397c/9WVEz/QkE3/zQz + Kv8VFRL/Ih0X/y8nH3wAAAABAQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRABMSUUXREI+yVJQS/9nZF//cG1m/315 + c/+npJ7/3NrX//b29P/5+fj/+Pf2//b19P/19PL/8/Lw//Lw7v/w7uz/7uzp/+zq5//p5+T/6OXi/+bj + 4P/j4d7/4d/b/9/d1//d2tX/29fS/9jVz//V0s3/09DJ/9HNxv/PysT/zMfA/8nFvv/Gwrv/xL+3/8K+ + tf/CvbX/wb20/8G8tP/AvLP/wr62/9TQy//l4+H/yMbC/4+LhP9pZV7/SEY9/19eVv90cm3/rKaf/2Ve + VsoAAAAZGxYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVEPwBFQz4hRkRA41dUT/9oZV//fXp0/7a0sP/p6Ob/+fj3//j3 + 9v/29fT/9fTz//X08v/z8vD/8vHv//Hv7f/v7uv/7uvo/+vp5v/p5+T/5+Xi/+bj4P/k4d3/4t/b/9/d + 2P/e2tX/29jT/9jV0P/W083/1NDL/9LOx//PzMT/zcjC/8vGv//Iw7z/xcG6/8TAuP/Ev7f/w7+2/8O/ + tv/DvrX/wr61/8bBuf/a2NP/7ezq/8PBvf9+enT/ZWJc/7q4s/9ubWj/T0xH/yIbFPYSCQJXGA4FABcK + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAExLRwBMS0clTEpH7VpXUv9zcGv/tbOv//Hx7//5+Pf/9vXz//Xz8v/08/H/9PPx//Py + 8P/y8e7/8e/t//Du6//u7On/7ern/+ro5f/p5uP/5+Xi/+bj4P/k4d3/4t/b/+Dd2P/e2tX/3NjT/9nW + 0f/X087/1dHL/9LPyP/QzcX/zsrC/8zHwP/JxL3/x8K7/8bCuv/Gwbr/xcG5/8XBuf/FwLj/xMC3/8S/ + t//Iw7z/4d7b//Lx8P+2tLD/Y2Bb/2NhWP9DQTj/FRUQ/wYDAP8YDgSmAAAACQYCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBO + SgBPTkolT01J7GRiXf+qqKT/7u3r//j39v/08/H/8/Lw//Py8P/y8e//8vHv//Hw7v/x7+3/8O7r/+/t + 6f/t6+j/7Onm/+ro5P/o5uP/5+Xh/+Xj3//k4d3/4t/b/+Dd1//e29b/3NjT/9rW0f/X1M7/1dLM/9PQ + yf/Rzcb/z8vD/83Iwf/Lxr//ycS9/8nEvf/Iw7z/yMO8/8fDu//Hwrv/xsK6/8bBuv/Fwbn/zcnC/+7t + 6f/r6+n/jouH/0A+Nv9DQjf/Kikh/wgHBP8SCgLnFg0DMx4RBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBQT0slUlBM7IeF + gf/g393/9/b1//Px7//y8O7/8vDu//Lw7v/x8O3/8e/t//Du7P/v7ur/7+3p/+7r6P/s6ub/6+jl/+nn + 5P/o5eL/5uTg/+Xj3v/j4dz/4t/a/+Dd1//f29b/3dnT/9vW0f/Y1M//1tLM/9TQyv/Szsf/0MzE/87J + wv/Mx8D/zMa//8vGvv/Lxb7/ysW+/8rEvf/JxL3/ycS8/8jDvP/Hw7v/yMK7/9vY0v/5+Pf/wcC+/09N + SP9ZV07/XlxU/xUUEP8LBgH+FAsCeyUWBgD/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFNSTgBPTkolX15a7Le1s//z8vD/8/Hu//Dv + 7P/x7+z/8O/s//Dv7P/w7uv/8O7q/+/t6f/u7Oj/7evn/+3q5v/r6eX/6ufk/+jm4//n5eH/5uTg/+Xi + 3v/j4Nz/4t/Z/+Dd1//f3Nb/3dnT/9vX0f/Y1c//1tPN/9XRy//Tz8f/0c3F/8/Lw//OycL/zcnB/83I + wf/Nx8D/zMfA/8zGv//Mxr//y8a//8vFvv/Kxb7/ycS9/8/Kw//t6+j/4uHg/2VkYP9YVk/9hoN9/z07 + Nf8FAwH/FAsDwgsEABMHAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtZVgBRUEwlfHp37NrZ1//z8u//8O7q//Dt6v/w7er/7+3q/+/t + 6f/v7en/7+zo/+7s6P/t6+f/7enm/+zo5f/q6OT/6efj/+jl4v/n5OD/5uPf/+Xi3f/j4Nz/4t/Z/+Dd + 1//f3Nb/3dnU/9vX0f/Z1c//19PN/9XRy//T0Mj/0s7G/9HMxP/QzMT/0MzE/8/Lw//Py8P/z8rC/87J + wv/OyMH/zcjB/83HwP/Mx8D/zMa//8zHv//e29b/7+7t/4uKh/45ODLrh4V//V9dVv8REQ3/CgUA8xQK + AUccDwIABQMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGlnZABbWlcmlZSR7ejm5P/x7+v/7+3p/+/t6P/v7en/7+3p/+7s6P/u7Oj/7uvn/+3q + 5//t6eb/7Onl/+ro5P/p5+P/6ebi/+jl4f/n5OD/5eLe/+Th3f/j4Nv/4t7Z/+Dd1//f3NX/3dnU/9zX + 0f/a1dD/2NTO/9bSy//U0Mn/08/H/9POx//Szsb/0s7G/9HNxf/RzcX/0czE/9DMxP/Qy8P/z8vD/8/L + w//PysL/zsnC/83Iwf/W0sz/7uzq/6Oinv4zMi7CbWtk3ISBe/8sKyX/BgQB/xEJApQDAQACAQEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJx + bQBlZGAnoqGe7ezp5v/w7en/7uzn/+7s6P/u7Oj/7uzo/+7r5//u6uf/7erm/+3p5v/s6OX/6+jk/+rn + 4//p5uL/6OXh/+fk4P/m49//5eLe/+Th3f/j4Nv/4d7Y/+Dd1v/f3NX/3tnU/9zX0v/a1tD/2NTO/9fT + zP/V0cv/1dHK/9XQyf/U0Mn/1M/I/9PPx//Tz8f/0s7G/9LOxv/SzcX/0c3F/9HNxf/QzMT/0MzE/8/L + w//Tz8j/6Obi/6mnpP88OzecTkxFkoiGgP9WVE7/CQgG/w4HAdULBQEgBAEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHt5dgBxcGwpq6mm7e3r + 6P/v7ej/7uzn/+7s5//u6+f/7urn/+7q5//t6ub/7enm/+zp5f/s6OT/6ufj/+nn4//p5uL/6OXh/+fk + 4P/m497/5eLd/+Th3P/j4Nr/4t7Y/+Hd1//g3NX/3tnU/93Y0v/b1tD/2dXO/9fTzf/X083/19LM/9bS + zP/W0sv/1dHL/9XRyv/V0Mr/1NDJ/9TPyP/Tz8j/08/H/9POx//Szsb/0s3G/9HNxf/Uz8j/5ePe/6im + ov9CQT2PJyYfOYOBe+h7eXP/IiEc/wUCAPgRCQFbIxICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKBfQB7enYnr66q6u3r5//v7Of/7uvn/+7q + 5v/u6ub/7urm/+3q5v/t6eX/7enl/+zo5P/r6OP/6ufj/+nn4v/p5uH/6OXg/+fk3//m497/5eLd/+Th + 3P/j4Nr/4t7Y/+Hd1//g3Nb/39rU/93Y0v/c19H/29bQ/9vWz//a1c//2dXO/9nUzv/Y1M3/2NPN/9fT + zP/X0sz/1tLM/9bSy//W0cv/1dHK/9XQyv/U0Mn/1NDI/9PPx//V0cn/4+Db/6Shnv9DQj56AAAABmJg + Wp+Uko3/SEZA/wYFAv8KBACmDAIACAsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHp3cwBsamYcpaKf0+fk4f/v6+f/7uvm/+7q5v/u6ub/7urm/+3p + 5f/t6eX/7Ojk/+zo5P/r6OP/6ufi/+nm4v/p5uH/6OXg/+fk3//m497/5uLd/+Xh3P/k4Nr/49/Y/+He + 1//g3Nb/39vU/97Z0//e2NL/3djS/93Y0v/c19H/3NfR/9vW0P/b1tD/29bP/9rVz//Z1c//2dTO/9jU + zv/Y083/19PN/9fTzP/W0sz/1tLL/9bRy//X08z/39zX/5uYlPtGREBUW1lTADk3MkSPjYjsdXJs/xgX + FP8FAQDeCgMAJgkDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHFuagBQTUoKnZuWp93b1//w7ej/7+vm/+7q5v/u6ub/7urm/+7q5f/t6eX/7enk/+zo + 5P/s6OP/6ufi/+nm4v/p5uH/6OXg/+jk3//n497/5uLd/+Xh3P/k4Nv/49/Z/+Le1//h3db/4d3W/+Dc + 1f/g29T/39rU/9/a1P/e2dP/3tnT/97Y0v/d2NL/3dfR/9zX0f/c19H/29bQ/9rW0P/a1c//2dXP/9nV + zv/Z1M7/2NTN/9jTzf/a1s//2dXP/5CNiOZGREEuRUM/AAAAAAt6eXOolJKO/z89N/8CAQD5BAAAZAUA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1q + aACqp6IAkY6Kbs/Lx/zv7Of/7+zm/+/r5v/v6ub/7+rm/+7q5f/u6eX/7enk/+3o5P/s6OP/6+fi/+rn + 4v/p5uH/6eXg/+jk3//n497/5uPd/+bi3P/l4dv/5ODa/+Pf2f/j39j/4t7Y/+Le1//h3df/4d3W/+Hd + 1v/g3NX/4NzV/9/b1P/f29T/39rT/97Z0//e2NL/3djS/93Y0v/d19H/3NfR/9zW0P/b1tD/29bP/9rV + z//c2NH/x8S+/316dbY8ODQOT0tHAGVjWQBQT0hQmJeT8nRxa/8SEQ7/AAAApAAAAAkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL+4tACQjIkAhIF9Kri1 + sdvp5eD/8O3n//Ds5//v6+b/7+rm/+/q5f/u6eX/7unl/+3o5P/t6OP/7Oji/+zn4v/q5uH/6eXh/+nl + 3//o5N//5+Pe/+fj3f/m4t3/5uLc/+Xh2//l4dv/5ODa/+Tg2f/j39n/49/Y/+Pf2P/i3tf/4t7X/+Le + 1//h3db/4d3W/+Dc1f/g3NX/39vU/9/b1P/f2tT/3tnT/97Z0//d2NL/3djS/93X0f/b1tD/sK2n+nVy + bmiloZkAQ0ZLACIhHAAAAAANkI+KspeUkP9APjn/AAAA3wAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFg38AVlZSBJ6cl4bU0Mz+8Ozn//Ht + 5//x7ef/8Ozm/+/r5v/v6uX/7unl/+7p5P/u6eT/7ejj/+zo4v/s5+L/6ubh/+rm4f/p5eD/6eXg/+nl + 3//o5N//6OTe/+fj3v/n493/5uLd/+bi3P/m4tz/5eHb/+Xh2//k4Nr/5ODZ/+Tg2f/j39j/49/Y/+Le + 1//i3tf/4d3W/+Hd1v/h3db/4NzV/+Dc1f/f29T/39rU/9/a1P/OycP/mpeRxHZybRp+e3UAU1NTAAAA + AADGwr0AeHZxT6imo/N5dnD/ERAO+QAAAGMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPytAAhoN9AIiGgCCyr6vI49/a//Dt5//x7ef/8e3o//Ds + 5//w6+b/8Ovm/+/r5f/v6uX/7+rl/+7p5P/u6eT/7ejj/+3o4//s6OL/6+fi/+vn4f/q5uH/6ubg/+nl + 4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3f/m4t3/5uLc/+bi3P/l4dv/5eHa/+Tg2v/k4Nn/49/Z/+Pf + 2P/j39j/4t7X/+Le1//h3db/4d3W/9vX0f+yr6nth4N+VJ6UlQB7e3IAAAAAAAUFAAAmJSIAPjw4DKup + pq+hnpr/RUM+/wAAAKAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDwsIAGz0YALu4tQCWk5BKwb645erm4P/x7Of/8u7p//Lv6v/w7ef/8Ozm//Ds + 5v/w7Ob/8Ozm/+/r5f/v6+X/7+vl/+/q5f/u6uT/7unk/+7o4//t6OP/7Oji/+zn4v/r5+L/6+bh/+rm + 4f/q5eD/6eXg/+nl3//o5N//6OTe/+fj3v/n493/5+Pd/+bi3f/m4tz/5eHc/+Xh2//k4Nr/5ODa/+Tg + 2f/j39n/4NzW/8C8t/uTkIqFdnRxCI2LhQDPz8EAAAAAAAAAAAAuLisA1dTRANXTzkmvrarzkIyG/xwb + GdEAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAeHhvAHZ2cgANFBQCnpyWZ8vHwu7q5eD/8e3o//Pw7P/z8Ov/8e3n//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm/+/r5f/v6+X/7+rl/+7p5P/u6eT/7ejj/+3o4//s5+L/7Ofi/+vn + 4f/r5uH/6ubh/+rl4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3v/n493/5uLd/+bi3P/j39n/yMW//5mW + kaNoZWERfn95AP///wAAAAAAAAAAAAAAAAAAAAAA0c3FAOPe1Q/KycazqaWh/3FtaPQODQxCLCooAK+n + nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH19 + eACRkIsAnJmVBaGfmmfBvrnl5uHc//Lu6f/18u7/9PHt//Hu6f/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/7+vl/+/q5f/u6uT/7unk/+7p5P/t6OP/7ejj/+zn + 4v/s5+L/6+fh/+rm4f/q5uD/6eXg/+nl4P/p5d//6eXf/+Pf2v/EwLv6m5eSm3p4cxuMioMAoKmXAH5z + cwAAAAAAAAAAAAAAAAAAAAAA3NjSAN7c2QDh3ttQtbOx8bKtp/+inZaLZmFaAq6mngAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVkYoAlZONAFJY + VQKZlZBOuLWw0dvX0v/v6+b/9fPv//b08P/z8Oz/8e3n//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm//Dr5v/v6+X/7+rl/+/q5f/u6eT/7unk/+3o + 4//t6OP/7Ofi/+zn4v/q5eD/3dnU/7u4s+uVkYx/dHNtD5SSjQAAAAAALCsqAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAODc1gDk3tYK2NbTn6qmo//HwLnTwbuyHbmyqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj42IAIaCfgD///8AiYeFK6ej + n5/Hw77x5eHc//Tx7f/49vP/9/Tx//Pw7P/x7ej/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w6+b/8Ovm//Dr5v/v6uX/7unk/+bh + 3P/KxsH9qKahxIuJhU1UUE4Fe3lzAI6LhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wDl4t4A7OnlLrCuqsOnpJ6e2NDJD763sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH11cgCPgoEAkI6LAHdycAuXlJBUsq+ruMvH + w/Xl4t7/9fPx//r49v/59/T/9fLu//Lv6v/x7ef/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8e3n/+/r5f/l4Nv/zsrF/bKuqdiTkIx7endzHK2m + qwCHhH8Ah4N/ALa2tgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM/JxADLyMEA4+DYAb26 + sxOmpJ4NuLGrAL63sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AY2VzAIyIhQCxq6cAiIaDE5qYlFauq6exxsTA6dza + 1//v7ev/+Pf1//r59//49vP/9vPv//Tw7P/y7+r/8u7p//Hu6P/x7ef/8e3n//Ht5//x7ef/8e3n//Hu + 6P/y7uj/8O3n/+3p4//m4tz/2tbR/8bCvfauq6bGmJWQeICAeigAAAABiYeCAHh4cADJyLwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADX1M8A6ebhALm2sACmpJ4AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSUjgCanJYAkI2KALy4swBqamkLjoyIN6OhnXSysKy3xsTA39bU + 0ffi4d7/7Orn//Du7P/z8O3/8/Dt//Lw7P/x7ur/8Ozo/+/r5//u6uX/6OXg/+Lf2v/Z1dD/zMjD/b67 + t+etqqbJn5yXko6MiExwb2sY////AI2MhQB+fHoAlpOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAADAwMAP///wCEgoEAlJGPAP///wBcW1kJe3p2LJGPi1KjoZ13rKmmmrKv + rLq5trPRvLq33L27t+C6uLTlt7Wx4ra0sNuyr6vYqKaiwaGemqeal5OEjouHYHx6djhraGcSAAAAAZqY + kwB+fHgAXmBeAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8Axse9ADUzMQBqa2oAfHt4AJCOigD///8AOjs5BmJgXg1+fHoThYSAFYB/ + eiF6eXUvenl2JoSDgBV8enYUZmVhDklHRQkAAAAClZKOAHx5dQBwbWwAeHd1AHJvbAD07OQABQUFAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUwB4eHUAenl3AIOBfwCUko8AmZeUAI+OigCGhYEAiIaDAJeV + kgCQjooAgn98AHRybwBwb2kAc3NsAP///wAgICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8AAAD///////////8A + AAD/////AH////8AAAD////AAAD///8AAAD///wAAAAf//8AAAD///gAAAAP//8AAAD//+AAAAAB//8A + AAD//4AAAAAA//8AAAD//wAAAAAAf/8AAAD//gAAAAAAP/8AAAD//AAAAAAAH/8AAAD/+AAAAAAAD/8A + AAD/8AAAAAAAB/8AAAD/8AAAAAAAB/8AAAD/8AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAP8A + AAD/4AAAAAAAAP8AAAD/4AAAAAAAAP8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8A + AAD/4AAAAAAAAD8AAAD/4AAAAAAAAD8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAA8A + AAD/4AAAAAAAAA8AAAD/4AAAAAAAAA8AAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcA + AAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAEA + AAD/8AAAAAAABAEAAAD/8AAAAAAABgEAAAD/+AAAAAAABwAAAAD/+AAAAAAADwAAAAD//gAAAAAAH4AA + AAD//wAAAAAAP4AAAAD//wAAAAAAf4AAAAD//4AAAAAA/8EAAAD///AAAAAD//8AAAD///wAAAAP//8A + AAD///4AAAA///8AAAD////gAAP///8AAAD///////////8AAAD///////////8AAAD///////////8A + AAA= + + + \ No newline at end of file diff --git a/DBChm/Images/DBCHM000.png b/DBChm/Images/DBCHM000.png new file mode 100644 index 0000000..97a63fe Binary files /dev/null and b/DBChm/Images/DBCHM000.png differ diff --git a/DBChm/Images/DBCHM001.png b/DBChm/Images/DBCHM001.png new file mode 100644 index 0000000..f2351f0 Binary files /dev/null and b/DBChm/Images/DBCHM001.png differ diff --git a/DBChm/Images/DBCHM002.png b/DBChm/Images/DBCHM002.png new file mode 100644 index 0000000..8fb641c Binary files /dev/null and b/DBChm/Images/DBCHM002.png differ diff --git a/DBChm/Images/DBCHM003.png b/DBChm/Images/DBCHM003.png new file mode 100644 index 0000000..d4b6861 Binary files /dev/null and b/DBChm/Images/DBCHM003.png differ diff --git a/DBChm/Images/DBCHM004.png b/DBChm/Images/DBCHM004.png new file mode 100644 index 0000000..dc8915f Binary files /dev/null and b/DBChm/Images/DBCHM004.png differ diff --git a/DBChm/Images/DBCHM005.png b/DBChm/Images/DBCHM005.png new file mode 100644 index 0000000..e1f8fe2 Binary files /dev/null and b/DBChm/Images/DBCHM005.png differ diff --git a/DBChm/Images/ORA-28040.png b/DBChm/Images/ORA-28040.png new file mode 100644 index 0000000..84e7e57 Binary files /dev/null and b/DBChm/Images/ORA-28040.png differ diff --git a/DBChm/Images/dbchm.png b/DBChm/Images/dbchm.png new file mode 100644 index 0000000..b4c2727 Binary files /dev/null and b/DBChm/Images/dbchm.png differ diff --git a/DBChm/ImportForm.cs b/DBChm/ImportForm.cs new file mode 100644 index 0000000..0289fe0 --- /dev/null +++ b/DBChm/ImportForm.cs @@ -0,0 +1,225 @@ +using ComponentFactory.Krypton.Toolkit; +using DBCHM.Common; +using DocTools; +using DocTools.Dtos; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Xml; + +namespace DBCHM +{ + public partial class ImportForm : KryptonForm + { + public ImportForm() + { + InitializeComponent(); + + CheckForIllegalCrossThreadCalls = false; + + //为KeyDown能应用到所有控件上 注册 KeyDown 事件 + foreach (Control control in this.Controls) + { + control.KeyDown += control_KeyDown; + } + } + public void control_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + this.Close(); + } + } + + private void ImportForm_Load(object sender, EventArgs e) + { + txtExplain.Text = @"本工具目前支持 pdm/xml 文件来 进行更新批注(注释): + pdm 由powerdesigner设计数据库时产生。 + xml 由visual studio设置 实体类库的项目属性,勾选 XML文档文件 后生成项目时产生。 + xml 由dbchm的 XML导出 而产生。"; + } + + private void BtnBrow_Click(object sender, EventArgs e) + { + var openFileDialog = new OpenFileDialog + { + //支持 pdm,实体类注释产生的xml文档,dbchm导出的xml文件 + Filter = "支持文件类型(*.pdm;*.xml)|*.pdm;*.xml|PowerDesigner文件(*.pdm)|*.pdm|xml文件|*.xml", + Multiselect = true + }; + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + txtMulItem.Text = string.Join("\r\n", openFileDialog.FileNames); + } + } + + private void BtnUpdateDisplayName_Click(object sender, EventArgs e) + { + if (DBUtils.Instance == null) + { + MessageBox.Show("更新批注,需连接数据库,请切换到要更新批注的数据库!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + return; + } + + if (string.IsNullOrWhiteSpace(txtMulItem.Text)) + { + MessageBox.Show("请先选择批注数据文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + return; + } + + + FormUtils.ShowProcessing("正在更新批注到数据库,请稍等......", this, arg => + { + string[] paths = txtMulItem.Text.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string ph in paths) + { + string extName = Path.GetExtension(ph).ToLower(); + try + { + if (File.Exists(ph)) + { + if (extName == ".pdm") + { + UpdateCommentByPDM(ph); + } + else if (extName == ".xml") + { + UpdateCommentByXML(ph); + } + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "出错", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + } + + MessageBox.Show("更新表列批注完成!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + FormUtils.IsOK_Close = true; + this.Close(); + + }, null); + } + + + /// + /// 通过pdm文件更新批注 + /// + /// + void UpdateCommentByPDM(string path) + { + var lstTabs = GetTables(path); + var dbInfo = DBUtils.Instance?.Info; + foreach (var tab in lstTabs) + { + string tab_Comment = tab.Name; + if (!string.IsNullOrWhiteSpace(tab_Comment) + && !tab.Code.Equals(tab_Comment, StringComparison.OrdinalIgnoreCase)) + { + dbInfo.SetTableComment(tab.Code, tab_Comment); + } + var lstCols = tab.Columns; + foreach (var col in lstCols) + { + string col_Comment = col.Name; + if (!string.IsNullOrWhiteSpace(col_Comment) + && !col.Code.Equals(col_Comment, StringComparison.OrdinalIgnoreCase)) + { + dbInfo.SetColumnComment(tab.Code, col.Code, col_Comment); + } + } + } + } + + + static IList GetTables(params string[] pdmPaths) + { + List lstTables = new List(); + var pdmReader = new PDM.PdmReader(); + foreach (string path in pdmPaths) + { + if (File.Exists(path)) + { + var models = pdmReader.ReadFromFile(path); + lstTables.AddRange(models.Tables); + } + } + lstTables = lstTables.OrderBy(t => t.Code).ToList(); + return lstTables; + } + + void UpdateCommentByXML(string path) + { + var xmlContent = File.ReadAllText(path, Encoding.UTF8); + if (xmlContent.Contains("ArrayOfTableDto")) + { + //通过 dbchm 导出的 XML文件 来更新 表列批注 + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xmlContent); + + var dbName = doc.DocumentElement.GetAttribute("databaseName"); + + if (!DBUtils.Instance.Info.DBName.Equals(dbName, StringComparison.OrdinalIgnoreCase)) + { + if (MessageBox.Show("检测到数据库名称不一致,确定要继续吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel) + { + return; + } + } + + var lstDTO = typeof(List).DeserializeXml(xmlContent) as List; + + foreach (var tabInfo in lstDTO) + { + if (DBUtils.Instance.Info.IsExistTable(tabInfo.TableName) && !string.IsNullOrWhiteSpace(tabInfo.Comment)) + { + DBUtils.Instance.Info.SetTableComment(tabInfo.TableName, tabInfo.Comment); + } + + foreach (var colInfo in tabInfo.Columns) + { + if (DBUtils.Instance.Info.IsExistColumn(tabInfo.TableName, colInfo.ColumnName) && !string.IsNullOrWhiteSpace(colInfo.Comment)) + { + DBUtils.Instance.Info.SetColumnComment(tabInfo.TableName, colInfo.ColumnName, colInfo.Comment); + } + } + } + } + else + { + + //通过 有 VS 生成的 实体类库 XML文档文件 来更新 表列批注 + + XmlAnalyze analyze = new XmlAnalyze(path); + + var data = analyze.Data; + + foreach (var item in data) + { + if (DBUtils.Instance.Info.IsExistTable(item.Key.Key) && !string.IsNullOrWhiteSpace(item.Key.Value)) + { + DBUtils.Instance.Info.SetTableComment(item.Key.Key, item.Key.Value); + } + + foreach (var colKV in item.Value) + { + if (DBUtils.Instance.Info.IsExistColumn(item.Key.Key, colKV.Key) && !string.IsNullOrWhiteSpace(colKV.Value)) + { + DBUtils.Instance.Info.SetColumnComment(item.Key.Key, colKV.Key, colKV.Value); + } + } + } + + } + } + } +} diff --git a/DBChm/ImportForm.designer.cs b/DBChm/ImportForm.designer.cs new file mode 100644 index 0000000..bd6fc53 --- /dev/null +++ b/DBChm/ImportForm.designer.cs @@ -0,0 +1,149 @@ +namespace DBCHM +{ + partial class ImportForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImportForm)); + this.txtMulItem = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.BtnBrow = new System.Windows.Forms.Button(); + this.BtnUpdateDisplayName = new System.Windows.Forms.Button(); + this.GP_Import = new System.Windows.Forms.GroupBox(); + this.gpxDesc = new System.Windows.Forms.GroupBox(); + this.txtExplain = new System.Windows.Forms.TextBox(); + this.GP_Import.SuspendLayout(); + this.gpxDesc.SuspendLayout(); + this.SuspendLayout(); + // + // txtMulItem + // + this.txtMulItem.Location = new System.Drawing.Point(77, 26); + this.txtMulItem.Multiline = true; + this.txtMulItem.Name = "txtMulItem"; + this.txtMulItem.ReadOnly = true; + this.txtMulItem.Size = new System.Drawing.Size(497, 71); + this.txtMulItem.TabIndex = 7; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 50); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(65, 12); + this.label1.TabIndex = 6; + this.label1.Text = "文件路径:"; + // + // BtnBrow + // + this.BtnBrow.Location = new System.Drawing.Point(178, 18); + this.BtnBrow.Name = "BtnBrow"; + this.BtnBrow.Size = new System.Drawing.Size(104, 36); + this.BtnBrow.TabIndex = 5; + this.BtnBrow.Text = "选择(可多选)"; + this.BtnBrow.UseVisualStyleBackColor = true; + this.BtnBrow.Click += new System.EventHandler(this.BtnBrow_Click); + // + // BtnUpdateDisplayName + // + this.BtnUpdateDisplayName.Location = new System.Drawing.Point(371, 18); + this.BtnUpdateDisplayName.Name = "BtnUpdateDisplayName"; + this.BtnUpdateDisplayName.Size = new System.Drawing.Size(104, 36); + this.BtnUpdateDisplayName.TabIndex = 8; + this.BtnUpdateDisplayName.Text = "更新表列批注"; + this.BtnUpdateDisplayName.UseVisualStyleBackColor = true; + this.BtnUpdateDisplayName.Click += new System.EventHandler(this.BtnUpdateDisplayName_Click); + // + // GP_Import + // + this.GP_Import.Controls.Add(this.label1); + this.GP_Import.Controls.Add(this.txtMulItem); + this.GP_Import.Location = new System.Drawing.Point(5, 59); + this.GP_Import.Margin = new System.Windows.Forms.Padding(2); + this.GP_Import.Name = "GP_Import"; + this.GP_Import.Padding = new System.Windows.Forms.Padding(2); + this.GP_Import.Size = new System.Drawing.Size(584, 113); + this.GP_Import.TabIndex = 9; + this.GP_Import.TabStop = false; + this.GP_Import.Text = "批注数据导入"; + // + // gpxDesc + // + this.gpxDesc.Controls.Add(this.txtExplain); + this.gpxDesc.Location = new System.Drawing.Point(5, 177); + this.gpxDesc.Name = "gpxDesc"; + this.gpxDesc.Size = new System.Drawing.Size(584, 100); + this.gpxDesc.TabIndex = 10; + this.gpxDesc.TabStop = false; + this.gpxDesc.Text = "使用说明"; + // + // txtExplain + // + this.txtExplain.ForeColor = System.Drawing.Color.Black; + this.txtExplain.Location = new System.Drawing.Point(7, 20); + this.txtExplain.Multiline = true; + this.txtExplain.Name = "txtExplain"; + this.txtExplain.ReadOnly = true; + this.txtExplain.Size = new System.Drawing.Size(571, 73); + this.txtExplain.TabIndex = 0; + // + // ImportForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(595, 282); + this.Controls.Add(this.gpxDesc); + this.Controls.Add(this.GP_Import); + this.Controls.Add(this.BtnUpdateDisplayName); + this.Controls.Add(this.BtnBrow); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ImportForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "批注上载"; + this.Load += new System.EventHandler(this.ImportForm_Load); + this.GP_Import.ResumeLayout(false); + this.GP_Import.PerformLayout(); + this.gpxDesc.ResumeLayout(false); + this.gpxDesc.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox txtMulItem; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button BtnBrow; + private System.Windows.Forms.Button BtnUpdateDisplayName; + private System.Windows.Forms.GroupBox GP_Import; + private System.Windows.Forms.GroupBox gpxDesc; + private System.Windows.Forms.TextBox txtExplain; + } +} \ No newline at end of file diff --git a/DBChm/ImportForm.resx b/DBChm/ImportForm.resx new file mode 100644 index 0000000..9f93bf7 --- /dev/null +++ b/DBChm/ImportForm.resx @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEASEgAAAEAIACIVAAAFgAAACgAAABIAAAAkAAAAAEAIAAAAAAAAFEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8vLwA+LS4ASkVBAEtL + RABLS0YAS0tEAElHQgBAQD8APz8/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE1KQwBMSUMAU1BLAFtYUgBdWlQAXVxVAGBeWABramMADggHAyotJQYpKykFKiwlBiEf + HQSAfXIAYF5ZAF9cVgBcWlQAWVdRAFZUTgBNTEYAQjc6AEc/PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0xJAAAAAABQSUEAQ0E+AFVS + TACEhYEAREE9CFVRTBVaV1A1WllSVl5cVm9iX1mCZmNdlGhmX5tpZ2CbaGZfm2VjXJhhX1mFX1xXdVxa + VF1ZVlFAVFFLHkpKQwwABAABW1pRAEtJQgAbJBgARUU5AElJPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBPzwAODY1AFFPSQCFgnYAREM/DVZTTjlcWlRwYV9YqGdl + X9RvbGXodXJr9Hl2b/59enL/hIB4/4eEfP+JhX3/iIR9/4WBev9/fHT/e3dw/3d0bfdyb2jsamdh3WRh + W7heXFWDWFVPSktKQxUAAAABWVZTAEhFQwBPS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT01HAExKRABYVk8AAAAAAk9NSCZbWVNwYmBavmxpYul0cWr/fnpy/4eDe/+Pi4L/lZCI/5qV + jP+dmZD/oZyT/6Self+ln5f/paCX/6Oflf+gm5L/nZiP/5qVjP+UkIj/jomB/4WBev96d3D/cG1m8mVj + XcxbWlOJU1BLOz07OwhcWlQATk1GAGdmWQAvLy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSAAAAAAAU1FMAP// + /wBQTkgsWFZQimJgWdlua2T+e3dw/4eDe/+RjIT/mJOL/56ZkP+inZT/pJ+X/6ehmP+po5r/qqSb/6ul + nP+rpZz/rKad/6ymnf+rpZz/qqSb/6mkm/+oopn/pZ+W/6Cbkv+alYz/kIyE/4SAef92c2z/aGVf6l1b + VaVTUUtBQ0M7BldVTgBIRj8AT0xHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkI/ADwxLQBTUEwAT05IE1lXUW9fXVfaamdh/3l1 + bv+Ggnv/kY2F/5mUi/+dmI//oJuS/6KdlP+kn5b/pqGY/6eimf+po5r/qqSb/6ulnP+rpZz/q6Wc/6ym + nf+rpZz/q6Wc/6ulnP+qpJv/qaOa/6eimf+loJf/op2T/5yXj/+Tj4b/hoJ6/3Vya/9kYlzsV1VPk09O + SCZbXFsAS0dCAExHQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABFRT8ARUI9AG1xaQBRTUkvWVdSrWNgWvpxbmf/gHx1/4yIf/+Uj4f/mZOL/5yW + jv+emZD/oJuS/6KdlP+kn5X/paCX/6ahmP+moJf/pqCX/6Wflv+inZT/op2U/6Wflv+nopj/qKKZ/6mj + mv+qpJv/qKOa/6eimf+loJf/pJ+V/6Kdk/+empH/mZSL/46Kgv9/e3T/a2hi/1pYUs5NTEZTQjs3BEhH + QQBHR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVF + PwBHRkAAoJKOAFJQSUdYVlDPZGFb/3Zya/+EgHj/jYmA/5KOhf+VkYn/mZOL/5uWjf+dmI//npmQ/5yX + jv+Yk4r/ko2E/4qFff+BfHT/fHhw/3Vxaf9taWL/a2hg/3JuZv94dGz/e3hv/4aBef+Qi4P/mZSM/5+a + kf+inZT/o56U/6Gdk/+fm5H/nZiP/5qUjP+SjYX/hYF5/3BtZv9bWVPtTkxIcjw9OQZCQj0AOjo1AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk1IAEdHQQCdjYwATUxGS1RS + TN5iX1n/dnJr/4R/eP+Lh37/j4qC/5GNhP+UkIj/l5GJ/5aQiP+RjIP/iYR8/4B7c/92cWn/bmli/2hk + Xf9iXlf/XVlT/11ZU/9dWVP/WlZR/1VRTP9UUUv/V1RN/1tXUf9hXVb/bGdg/3dya/+GgXj/k46F/5qV + jP+dmI//nJeO/5qVjf+Xkor/kY2F/4aCev9zcGj/W1lU80lIQ3MtLigGOzo2AD48OgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Pz8ATktFAE1KRwBMSkU8T01I3F5bVv9yb2j/gHx1/4eC + e/+Lhn7/jomB/5CLg/+Qi4P/jId+/4WAd/9/eXH/fHdv/355cf+Dfnb/iIN7/4+Kgv+RjYX/ko2G/5iT + i/+dl4//m5aO/5GMhf+Mh4D/i4Z//4SAeP99eXH/dnJr/3BsZf9vamP/dXBp/4B7c/+MiH//lpCI/5iS + iv+WkYn/k4+H/4+Kgv+EgHj/cW1n/1dVT/NEQj5wMjAvBD8+OwBGRkQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADGu4cATkxFAEpIQxtLSkW/V1VP/21pY/97d3D/gn52/4WBev+JhHz/i4V9/4uF + fP+Jg3r/iYN6/4+JgP+ZlIz/paCX/6+qov+1saj/uLOr/7u2rv+8t67/vLiv/724r/+9uK//vLev/7u2 + rv+6taz/ubOr/7axqP+yraX/raif/6Sfl/+YlIz/kIuD/4yHf/+Lh3//jYiA/5CLgv+SjYX/kY2E/4+L + gv+Lhn7/f3t0/2lmYP9OTEfqQkE9TT87NgBKS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABISEQAT1JRAkZEQH1OTEb9ZGFb/3Vxa/98eXH/gHx1/4N/d/+Ef3f/hoB4/4+JgP+dmI//r6qj/765 + sf/Gwrr/ycS9/8jDvP/Fwbn/wr61/8K9tP/CvbT/wb20/8G9tP/BvLP/wLyz/8C7sv+/u7L/vrqx/765 + sP++ubD/vbiv/7y3rv+6taz/tbCo/7Cro/+sp5//pKCa/6Kdl/+Yk4v/kIuD/42IgP+Lhn7/hYF5/3dz + bP9dW1X/R0ZBx0ZFQx9FREEAWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBGRT8ARUQ+KEdF + QNpYVVD/bWlj/3dzbP97d3D/fXly/396cv+KhX3/pKCY/7+7tP/OysX/0s7I/8/Lw//Lx8D/ycS9/8fD + u//Fwbn/xcC4/8S/t//AvLP/u7au/7q2rv+6tq7/ubWt/7m1rP+7tq7/wLyz/8C8s//AvLP/wLyz/8C7 + sv+/urH/vrmw/724sP+9uK//l5ON/7Cspv++u7b/r6ul/5eSi/+JhHz/hYF5/356c/9raGH/UE5J/ENC + P3UAAAAAXl5eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBAQDoAREI9cUtIQ/1hXlj/cGxm/3Vx + a/94dG3/gX11/52Ykf/Bvbf/19PO/9fUzv/Szsf/zcjB/8rGvv/FwLn/ubWu/6+ro/+empP/kY2H/4aD + fP+Cf3j/gn54/4SAef+EgXr/hIB5/4J/eP+AfXb/gn95/4uIgf+Xk4z/pqKa/7Gtpf+6tq3/wbyz/8C8 + s//AvLL/rquk/52Ykv/Fwrr/z83I/8bDv/+gnZf/hoF6/397c/9zb2j/WldS/0NBPchAQD8bQkJBAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBPTkkMQ0E9rFBOSf9mY13/cG1n/3ZybP+MiIL/uLaw/9rX + 0//d29b/1NHK/9DKw//Iw7z/ubSt/52ak/+Jhn//endx/3Rxav94dW7/fXpz/4F9dv+Ggnr/jIiA/5CL + g/+RjYT/kYyE/4+Lg/+Khn7/hIB4/4B8df97d3D/dHFr/3Zzbf+Bfnf/kY6H/6umnv+7tq7/yMS9/5qV + kf+8tq7/xsK7/9nX0v/X1dL/p6Oe/4F9dv92cmv/Yl9Z/0ZEQO87OjhFNDIvAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEZEQABGREAdRUM/2FVSTv9oZV//c3Bq/5aTjv/Pzcr/5+Xi/93a1P/Tz8j/ycW9/62o + ov+Lh4H/dXJs/3Vxa/97eHH/hoN7/4+Lg/+Wkon/m5aO/5+bkf+inpT/paCX/6eimf+oo5r/qaOa/6ij + mv+moZj/o56V/6Cbkv+bl47/lZGI/46Jgf+Cf3f/eHVu/3Rxav9+e3T/qqaf/62ppP+qpZ3/v7qx/8nF + vv/i4Nz/4N/d/6eln/96dm//ZmNd/0lHQ/06OTdsNDMrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpI + RABJSEQkSkhE7FlWUf9raGP/mJaR/9va1//q5+T/3NnT/87Kw/+tqqP/hoN9/3FuZ/90cGr/gX52/42I + gP+WkYj/nJeO/6Cbkv+jnpX/paCX/6eimf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6qk + nP+po5r/pqGY/6Oelf+emZD/lpGJ/4uHf/98eHH/fntz/66rpf9tamT/Y2Ba/7ezq//NycP/6Ofk/+Xk + 4/+fnJf/aWZg/0xJRf85NzWDXVhLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5NSQBOTUklTkxI7F1b + Vv+Ni4b/2tjV/+3r6f/d2dP/vrqz/4eEfv9qZ2H/cW1m/397dP+MiH//lZCH/5qVjf+emZD/oJuS/6Kd + lP+kn5b/pqGX/6eimf+po5v/qqSb/6ulnP+rpZz/rKWc/6ymnf+spp3/q6Wc/6ulnP+qpJv/qaOa/6ei + mf+loJf/o56U/5+ZkP+Xkon/i4d//25rY/8wLyj/Dg0L/3Rxbf/Ev7j/1dLM//X08//X1dP/fHl0/0xK + Rf82NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBPTkolUE5L7HNwbf/KyMb/7+7r/9zZ + 0/+tqqT/cG1n/2RhW/91cmv/hYF5/4+Lg/+VkYj/mZSM/5yWjv+emZD/oJuS/6Kdk/+jnpX/pJ+V/6Oe + lf+hnJT/oZuS/52Xjv+blY3/mpWM/5yXjv+hnJP/o56U/6agmP+oopn/qKKZ/6eimP+loJf/pJ+V/6Kd + k/+fm5H/nJeO/3BtZf83Ni3/FRQQ/xsaGP+Cf3n/wLy1/+Xi3v/29vX/qaek/09NSf8yMS6aAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJRTABPTkolWFZS7KKgnv/q6Ob/4d3Z/6Sgmv9kYlv/ZmNd/3l1 + bv+Hgnv/joqB/5KOhf+VkIj/mZOL/5uVjf+cl47/m5aN/5eSif+QjIP/iIN7/396cv92cWr/cGxl/2hj + Xf9jX1j/YV1X/2NfWf9rZl//cGxl/3p2b/+FgXn/kIuD/5mUi/+fmpD/oZyT/6Gck/+fmpH/npmQ/4iD + fP9IRz3/JSQd/wcGBf8zMi//fnt1/8XBuv/08/H/1NPR/19dWf8wLiyZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFZVUQBQTkolbGpn7MvJxv/o5eL/op+a/1xaVP9kYVv/eHRt/4WBef+Lh3//joqC/5GN + hP+Uj4f/lZCI/5ONhf+Mh37/g311/3p1bf9xbWX/bWhh/2llXv9lYVr/ZGFa/2djXP9lYVr/Y19Z/2Fd + WP9bV1L/WlZR/11YUv9fW1X/ZmFb/29rY/98d2//ioV9/5aRiP+bl43/nJaO/5eRif9VU0r/NDMq/w4O + Cv8VFBP/UlBL/3t4cv/Z1tL/6+vp/3p5df8wLy2ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg + XABXVVEliIaD7d/d2v+0saz/W1lT/2BeWP90cWr/gX12/4eCe/+Khn7/jYmA/4+Kgv+OiYH/iYR7/4N9 + dP+AenL/gXxz/4aBef+NiYH/lZCJ/5yXj/+emZH/op2V/6eimv+nopr/p6Ka/6WgmP+dmJD/mZSM/5OP + h/+Khn7/gn12/3l1bf91cGj/dnJq/355cv+Ig3v/kYyD/5aRiP9qaGD/PTwy/x8eGP8FBAP/QDw3/1xa + VP+KiIT/5+bk/5eVkv81NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG9uagBiYV0nn56b7cnH + xP9mZF//WldR/3BtZv98eHH/gX52/4WBev+Ig3z/iYN7/4qEe/+KhHv/joiA/5iTi/+moZn/s66m/7u2 + rv++urL/vrqx/7+7sv/Au7L/wLuy/7+7sv+/urH/v7qx/765sP+9ubD/vbiv/7u2rv+5tKv/tbCo/6+q + of+nopn/nZiR/5aSi/+Tj4j/kIyE/5GMhP98eHD/RUQ6/y8uJv8KCgb/HRkW/2FdV/9ZV1L/qKek/6Wj + oP89PDmaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHVzcABraWYooZ+c7YiGgv9RTkn/aGRe/3Zy + a/98eHH/gHx0/4J+dv+CfnX/iIN6/5iTiv+sp6D/vbix/8fDvP/Lxr//y8a//8nEvP/Fwbn/w762/8K+ + tf/CvrX/wr21/8K+tf/CvbT/wb20/8C8s//Au7L/v7uy/7+6sf++urH/vrmw/724sP+8t6//urSs/7aw + qP+yraX/sKyl/66qpP+dmJL/VlVM/zo5L/8YFxL/CAUD/0xIQv9iYFn/Y2Fd/4qJhf9DQj6YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIF+ewCCgH0pioiF7VdVUP9aV1L/bmpk/3dzbP96d2//fHhx/4F8 + dP+Sjob/sKyl/8nFv//U0cv/0s7I/83Jwv/Kxr7/yMS8/8bCuv/Dv7f/wby0/765sf+1san/sq2l/6mm + nv+opJz/p6Sc/6+ro/+yrqb/uLSs/7y4r/++ubD/v7uy/8G8s//Au7L/v7qx/765sP+8uK//vLev/766 + tP/Fwr3/goB6/0JBN/8oKCD/BgUD/yYhG/9lYVv/VVNN/1dWUv9DQj6H////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHh2cgCDgH0hX11Z3k1KRv9iX1r/cG1m/3Vxav95dG3/h4N8/6yoov/Pzcj/2tfS/9bS + zP/Qy8T/zMfA/8jDvP+8uLH/rKmi/5uYkf+NioL/gH13/3h1b/94dW7/fHhx/3p3cP96d3D/endv/3x5 + cv96d3D/eHVu/3t4cv+HhH3/lJCJ/6OfmP+wrKT/vrqx/8G8s//AvLP/v7qx/766sf/Fwbr/sa6p/09O + Rf82Niz/EREN/wwIBP9QS0P/XVpV/0A+Ov0zMzBoPzw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJP + SwBWU08YRkRBy1FPSv9nZF7/cGxm/3h0bv+Xk47/x8XA/+De2v/d2tX/08/I/87Jwv/Au7T/qaSe/4uH + gf97eHL/dXJs/3Vya/98eXH/g394/4mFff+OioH/k46G/5eSiv+ZlIv/mZSL/5eSiv+Tjob/jYmB/4iE + ff+Cfnf/endv/3VybP94dW//goB5/5qXj/+wraT/v7qy/8K9tP/BvbT/vrmy/2tpYf8+PTP/ISEa/wUD + Af8tJx//XltV/0RCPvw3NjNbKigjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZEPwBGQz8fRkQ/3lZT + T/9oZV//dnNt/6OhnP/a2NX/5+Tg/9rWz//QzMT/vbmx/5eUjf96d3D/cW1n/3h0bf+Cfnb/jIiA/5SQ + iP+alo3/n5qR/6OelP+loJf/qKKZ/6mkm/+qpJv/q6Wc/6qlnP+po5r/p6GY/6Wflv+hnJP/nJeO/5WQ + iP+Lh3//f3x0/3Rxav9zcGn/h4R9/6qmnv+/u7L/xcC4/4yJgf9FRDr/MTAo/wsLCP8TDQf/TEdA/0tJ + RP47OTZxNzUvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBLSUUlS0lF7FpXUv9va2b/paOf/+Tj + 4P/n5eH/2NTO/8XAuf+Zlo//dXJs/25rZf95dW7/h4N7/5GMhP+ZlIv/npmQ/6Gck/+kn5b/paCY/6ei + mf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6ulnP+qpJv/qKKZ/6Wgl/+hnJP/m5aN/5KN + hP+EgHj/dXFq/3BtZ/+Fgnv/r6uj/6+ro/9SUUf/PDwy/xoaFP8GAwD/MSoi/0lHQv85NzWHcGlYAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSQBPTUklT01J7GBeWf+cmpb/4+Lg/+ro5P/X083/r6ul/3p3 + cf9pZmD/dnJr/4SAef+Pi4P/l5GJ/5uWjv+emZD/oJuS/6KdlP+kn5X/pqGX/6eimf+po5r/qqSb/6qk + m/+rpZz/q6Wc/6ulnP+rpZz/q6Wc/6ulnP+qpJv/qKOa/6eimf+loJf/pJ+V/6Cckv+alo3/kIyE/4J+ + dv9va2X/bWpk/5GNhv9raWH/QkE2/ysqIv8IBwX/GBEK/z06Nf83NjOZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFBPSwBQTkslUU9L7H58eP/Y1tT/7uvo/9TQyv+ZlpD/aWZh/2tnYf98eHH/iYV9/5GN + hP+WkYn/mZSM/5uWjv+emZD/oJuS/6Gck/+inZP/oZuS/52Yj/+alYz/lpGI/5CLg/+OioH/joqB/4+K + gv+VkIf/mpWM/5+Zkf+jnZX/paCX/6WhmP+loJb/o56V/6Kdk/+gm5H/nJeP/5aRiP+JhX3/d3Ns/2dl + Xv9aWFH/REM5/zg4Lv8TEw//CgYB/yokHP8yMS+aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJR + TQBPTkolW1lW7K6sqf/t6+j/1tLN/4uIgv9fXVf/bGli/356c/+JhX3/j4uC/5KOhf+VkIj/mJOL/5qV + jf+alYz/l5KK/5GMhP+Ig3v/fnlx/3NvZ/9qZl//ZGBZ/11ZU/9aVlD/V1RO/1hUT/9dWVP/Yl9Y/2xn + Yf93cmr/g352/5GMg/+alYz/n5qR/6Cbkv+fmpH/nZiP/5uVjf+WkYn/jIiA/3x4cf9hX1n/QUA3/0FA + Nf8kIxz/BgUD/xoSC/8oJSKamv//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlXUwBRT0sldHJv7NLQ + zv/h3tn/jImD/1tYUv9qZ2H/fHhx/4aCev+Lh37/joqB/5GNhP+Tj4b/k46F/4+Kgf+Hgnr/fnlx/3dy + av9ybWb/cGxk/29rZP9taWL/cW1m/3JuZ/9wbGX/bmtk/25qZP9oZV//Y19Z/2RfWf9iXVf/ZGBa/2tm + X/91cGj/gn51/4+Kgv+Yk4r/m5WN/5qUjP+Xkor/k46G/4uHfv97eHD/UVBI/0JBN/8zMin/Dg0K/w4I + Av8eFw+nDTBaATUaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjYABZV1QmkI6L7eDe2/+fnJj/V1RP/2Zj + Xf94dW3/gn53/4aCe/+KhX7/jYiA/46JgP+Nh37/h4J4/4N+dP+Efnb/ioR8/5KOhf+dmJH/p6Ob/7Cs + pP+zr6f/urav/7y4sf+7t7D/urau/7i0rP+xrKX/qaWd/6Sfl/+alY3/kYyE/4aCev99eHD/enVu/355 + cf+FgHj/jYiA/5SPhv+UkIj/ko6G/4+Lgv+JhH3/amdg/0RDOv8+PTP/HBwW/wcFAv8XDgTGCQUBEgIA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFwbABkY18noqCd7b+9uv9dW1f/XVtV/3NvaP99eXH/gX12/4WA + ef+Hgnv/iIJ6/4mDev+Nh37/l5KJ/6ikm/+7t7D/y8jC/9fUz//d2tb/3tzY/+Dd2f/e3Nf/3dvV/9vY + 0//Y1dD/1tPN/9PQyv/QzMX/zcnC/8nFvv/FwLn/wLuz/7m0rP+wq6L/pqCZ/56Zkf+ZlI3/lpGL/5OO + hv+QjIP/kIuD/46Kgf+Lh3//gHx1/09NRP9CQTf/LCwk/wkJBv8QCQL0Fg0DTyMVBQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHl4dABycG0ooJ6a7Xd1cf9RT0r/amZg/3dzbP98eHH/f3t0/4F8dP+CfXX/jId//6Oe + lv++urT/2NXQ/+fl4v/u7On/7u3q/+3r6P/q6OX/5+Xi/+Ti3//i4Nz/393Y/93a1f/a19L/19TP/9TR + zP/Sz8j/0MzF/83Jwv/Kxr//x8O8/8TAuP/BvbT/vbiw/7mzq/+0sKj/s6+o/7Ovqf+qp6D/mZSM/42I + gP+KhH3/hoJ7/2ViWv9DQjf/Ojkw/xUVEP8JBQH/GA0EoQIAAAcKBQIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKA + fACHhYIogX977FFPS/9cWVT/b2tl/3ZybP96dm7/fHhw/4eCev+inpb/yMS//+Xj4P/z8vD/9vX0//Tz + 8f/x8O7/7+3q/+zq5//p5+T/5+Xi/+Xj3//j4N3/4N7Z/97b1v/b2NP/2NXQ/9bTzv/T0Mr/0c7H/87L + xP/Mx8H/ycS9/8bCuv/Dv7f/wLyz/7+6sf++ubD/vLev/725sv/Fwrz/w8C7/6mlnv+OiYL/hYB5/3Zy + a/9JRz7/QkE2/yYlHv8GBQP/EwsC5Q4GADMJAwAABgUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBuagB4dnIfV1VR2E1L + R/9kYVv/cG1n/3Vxav97d3D/lZGK/8TBvP/o5+T/+Pf2//n49//29vT/9PPx//Lx7//w7+3/7u3q/+zq + 5//p5+T/5+Xi/+Xj3//j4d3/4d/a/9/c1//d2dT/2tbR/9fUz//U0cz/0s/I/9DMxf/NycL/y8a//8fD + vP/Fwbn/wr61/8C8s//AvLP/wLuy/7+6sf/BvbT/zcrE/9bU0f+7uLP/j4uF/397c/9WVEz/QkE3/zQz + Kv8VFRL/Ih0X/y8nH3wAAAABAQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRABMSUUXREI+yVJQS/9nZF//cG1m/315 + c/+npJ7/3NrX//b29P/5+fj/+Pf2//b19P/19PL/8/Lw//Lw7v/w7uz/7uzp/+zq5//p5+T/6OXi/+bj + 4P/j4d7/4d/b/9/d1//d2tX/29fS/9jVz//V0s3/09DJ/9HNxv/PysT/zMfA/8nFvv/Gwrv/xL+3/8K+ + tf/CvbX/wb20/8G8tP/AvLP/wr62/9TQy//l4+H/yMbC/4+LhP9pZV7/SEY9/19eVv90cm3/rKaf/2Ve + VsoAAAAZGxYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVEPwBFQz4hRkRA41dUT/9oZV//fXp0/7a0sP/p6Ob/+fj3//j3 + 9v/29fT/9fTz//X08v/z8vD/8vHv//Hv7f/v7uv/7uvo/+vp5v/p5+T/5+Xi/+bj4P/k4d3/4t/b/9/d + 2P/e2tX/29jT/9jV0P/W083/1NDL/9LOx//PzMT/zcjC/8vGv//Iw7z/xcG6/8TAuP/Ev7f/w7+2/8O/ + tv/DvrX/wr61/8bBuf/a2NP/7ezq/8PBvf9+enT/ZWJc/7q4s/9ubWj/T0xH/yIbFPYSCQJXGA4FABcK + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAExLRwBMS0clTEpH7VpXUv9zcGv/tbOv//Hx7//5+Pf/9vXz//Xz8v/08/H/9PPx//Py + 8P/y8e7/8e/t//Du6//u7On/7ern/+ro5f/p5uP/5+Xi/+bj4P/k4d3/4t/b/+Dd2P/e2tX/3NjT/9nW + 0f/X087/1dHL/9LPyP/QzcX/zsrC/8zHwP/JxL3/x8K7/8bCuv/Gwbr/xcG5/8XBuf/FwLj/xMC3/8S/ + t//Iw7z/4d7b//Lx8P+2tLD/Y2Bb/2NhWP9DQTj/FRUQ/wYDAP8YDgSmAAAACQYCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBO + SgBPTkolT01J7GRiXf+qqKT/7u3r//j39v/08/H/8/Lw//Py8P/y8e//8vHv//Hw7v/x7+3/8O7r/+/t + 6f/t6+j/7Onm/+ro5P/o5uP/5+Xh/+Xj3//k4d3/4t/b/+Dd1//e29b/3NjT/9rW0f/X1M7/1dLM/9PQ + yf/Rzcb/z8vD/83Iwf/Lxr//ycS9/8nEvf/Iw7z/yMO8/8fDu//Hwrv/xsK6/8bBuv/Fwbn/zcnC/+7t + 6f/r6+n/jouH/0A+Nv9DQjf/Kikh/wgHBP8SCgLnFg0DMx4RBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBQT0slUlBM7IeF + gf/g393/9/b1//Px7//y8O7/8vDu//Lw7v/x8O3/8e/t//Du7P/v7ur/7+3p/+7r6P/s6ub/6+jl/+nn + 5P/o5eL/5uTg/+Xj3v/j4dz/4t/a/+Dd1//f29b/3dnT/9vW0f/Y1M//1tLM/9TQyv/Szsf/0MzE/87J + wv/Mx8D/zMa//8vGvv/Lxb7/ysW+/8rEvf/JxL3/ycS8/8jDvP/Hw7v/yMK7/9vY0v/5+Pf/wcC+/09N + SP9ZV07/XlxU/xUUEP8LBgH+FAsCeyUWBgD/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFNSTgBPTkolX15a7Le1s//z8vD/8/Hu//Dv + 7P/x7+z/8O/s//Dv7P/w7uv/8O7q/+/t6f/u7Oj/7evn/+3q5v/r6eX/6ufk/+jm4//n5eH/5uTg/+Xi + 3v/j4Nz/4t/Z/+Dd1//f3Nb/3dnT/9vX0f/Y1c//1tPN/9XRy//Tz8f/0c3F/8/Lw//OycL/zcnB/83I + wf/Nx8D/zMfA/8zGv//Mxr//y8a//8vFvv/Kxb7/ycS9/8/Kw//t6+j/4uHg/2VkYP9YVk/9hoN9/z07 + Nf8FAwH/FAsDwgsEABMHAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtZVgBRUEwlfHp37NrZ1//z8u//8O7q//Dt6v/w7er/7+3q/+/t + 6f/v7en/7+zo/+7s6P/t6+f/7enm/+zo5f/q6OT/6efj/+jl4v/n5OD/5uPf/+Xi3f/j4Nz/4t/Z/+Dd + 1//f3Nb/3dnU/9vX0f/Z1c//19PN/9XRy//T0Mj/0s7G/9HMxP/QzMT/0MzE/8/Lw//Py8P/z8rC/87J + wv/OyMH/zcjB/83HwP/Mx8D/zMa//8zHv//e29b/7+7t/4uKh/45ODLrh4V//V9dVv8REQ3/CgUA8xQK + AUccDwIABQMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGlnZABbWlcmlZSR7ejm5P/x7+v/7+3p/+/t6P/v7en/7+3p/+7s6P/u7Oj/7uvn/+3q + 5//t6eb/7Onl/+ro5P/p5+P/6ebi/+jl4f/n5OD/5eLe/+Th3f/j4Nv/4t7Z/+Dd1//f3NX/3dnU/9zX + 0f/a1dD/2NTO/9bSy//U0Mn/08/H/9POx//Szsb/0s7G/9HNxf/RzcX/0czE/9DMxP/Qy8P/z8vD/8/L + w//PysL/zsnC/83Iwf/W0sz/7uzq/6Oinv4zMi7CbWtk3ISBe/8sKyX/BgQB/xEJApQDAQACAQEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJx + bQBlZGAnoqGe7ezp5v/w7en/7uzn/+7s6P/u7Oj/7uzo/+7r5//u6uf/7erm/+3p5v/s6OX/6+jk/+rn + 4//p5uL/6OXh/+fk4P/m49//5eLe/+Th3f/j4Nv/4d7Y/+Dd1v/f3NX/3tnU/9zX0v/a1tD/2NTO/9fT + zP/V0cv/1dHK/9XQyf/U0Mn/1M/I/9PPx//Tz8f/0s7G/9LOxv/SzcX/0c3F/9HNxf/QzMT/0MzE/8/L + w//Tz8j/6Obi/6mnpP88OzecTkxFkoiGgP9WVE7/CQgG/w4HAdULBQEgBAEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHt5dgBxcGwpq6mm7e3r + 6P/v7ej/7uzn/+7s5//u6+f/7urn/+7q5//t6ub/7enm/+zp5f/s6OT/6ufj/+nn4//p5uL/6OXh/+fk + 4P/m497/5eLd/+Th3P/j4Nr/4t7Y/+Hd1//g3NX/3tnU/93Y0v/b1tD/2dXO/9fTzf/X083/19LM/9bS + zP/W0sv/1dHL/9XRyv/V0Mr/1NDJ/9TPyP/Tz8j/08/H/9POx//Szsb/0s3G/9HNxf/Uz8j/5ePe/6im + ov9CQT2PJyYfOYOBe+h7eXP/IiEc/wUCAPgRCQFbIxICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKBfQB7enYnr66q6u3r5//v7Of/7uvn/+7q + 5v/u6ub/7urm/+3q5v/t6eX/7enl/+zo5P/r6OP/6ufj/+nn4v/p5uH/6OXg/+fk3//m497/5eLd/+Th + 3P/j4Nr/4t7Y/+Hd1//g3Nb/39rU/93Y0v/c19H/29bQ/9vWz//a1c//2dXO/9nUzv/Y1M3/2NPN/9fT + zP/X0sz/1tLM/9bSy//W0cv/1dHK/9XQyv/U0Mn/1NDI/9PPx//V0cn/4+Db/6Shnv9DQj56AAAABmJg + Wp+Uko3/SEZA/wYFAv8KBACmDAIACAsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHp3cwBsamYcpaKf0+fk4f/v6+f/7uvm/+7q5v/u6ub/7urm/+3p + 5f/t6eX/7Ojk/+zo5P/r6OP/6ufi/+nm4v/p5uH/6OXg/+fk3//m497/5uLd/+Xh3P/k4Nr/49/Y/+He + 1//g3Nb/39vU/97Z0//e2NL/3djS/93Y0v/c19H/3NfR/9vW0P/b1tD/29bP/9rVz//Z1c//2dTO/9jU + zv/Y083/19PN/9fTzP/W0sz/1tLL/9bRy//X08z/39zX/5uYlPtGREBUW1lTADk3MkSPjYjsdXJs/xgX + FP8FAQDeCgMAJgkDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHFuagBQTUoKnZuWp93b1//w7ej/7+vm/+7q5v/u6ub/7urm/+7q5f/t6eX/7enk/+zo + 5P/s6OP/6ufi/+nm4v/p5uH/6OXg/+jk3//n497/5uLd/+Xh3P/k4Nv/49/Z/+Le1//h3db/4d3W/+Dc + 1f/g29T/39rU/9/a1P/e2dP/3tnT/97Y0v/d2NL/3dfR/9zX0f/c19H/29bQ/9rW0P/a1c//2dXP/9nV + zv/Z1M7/2NTN/9jTzf/a1s//2dXP/5CNiOZGREEuRUM/AAAAAAt6eXOolJKO/z89N/8CAQD5BAAAZAUA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1q + aACqp6IAkY6Kbs/Lx/zv7Of/7+zm/+/r5v/v6ub/7+rm/+7q5f/u6eX/7enk/+3o5P/s6OP/6+fi/+rn + 4v/p5uH/6eXg/+jk3//n497/5uPd/+bi3P/l4dv/5ODa/+Pf2f/j39j/4t7Y/+Le1//h3df/4d3W/+Hd + 1v/g3NX/4NzV/9/b1P/f29T/39rT/97Z0//e2NL/3djS/93Y0v/d19H/3NfR/9zW0P/b1tD/29bP/9rV + z//c2NH/x8S+/316dbY8ODQOT0tHAGVjWQBQT0hQmJeT8nRxa/8SEQ7/AAAApAAAAAkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL+4tACQjIkAhIF9Kri1 + sdvp5eD/8O3n//Ds5//v6+b/7+rm/+/q5f/u6eX/7unl/+3o5P/t6OP/7Oji/+zn4v/q5uH/6eXh/+nl + 3//o5N//5+Pe/+fj3f/m4t3/5uLc/+Xh2//l4dv/5ODa/+Tg2f/j39n/49/Y/+Pf2P/i3tf/4t7X/+Le + 1//h3db/4d3W/+Dc1f/g3NX/39vU/9/b1P/f2tT/3tnT/97Z0//d2NL/3djS/93X0f/b1tD/sK2n+nVy + bmiloZkAQ0ZLACIhHAAAAAANkI+KspeUkP9APjn/AAAA3wAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFg38AVlZSBJ6cl4bU0Mz+8Ozn//Ht + 5//x7ef/8Ozm/+/r5v/v6uX/7unl/+7p5P/u6eT/7ejj/+zo4v/s5+L/6ubh/+rm4f/p5eD/6eXg/+nl + 3//o5N//6OTe/+fj3v/n493/5uLd/+bi3P/m4tz/5eHb/+Xh2//k4Nr/5ODZ/+Tg2f/j39j/49/Y/+Le + 1//i3tf/4d3W/+Hd1v/h3db/4NzV/+Dc1f/f29T/39rU/9/a1P/OycP/mpeRxHZybRp+e3UAU1NTAAAA + AADGwr0AeHZxT6imo/N5dnD/ERAO+QAAAGMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPytAAhoN9AIiGgCCyr6vI49/a//Dt5//x7ef/8e3o//Ds + 5//w6+b/8Ovm/+/r5f/v6uX/7+rl/+7p5P/u6eT/7ejj/+3o4//s6OL/6+fi/+vn4f/q5uH/6ubg/+nl + 4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3f/m4t3/5uLc/+bi3P/l4dv/5eHa/+Tg2v/k4Nn/49/Z/+Pf + 2P/j39j/4t7X/+Le1//h3db/4d3W/9vX0f+yr6nth4N+VJ6UlQB7e3IAAAAAAAUFAAAmJSIAPjw4DKup + pq+hnpr/RUM+/wAAAKAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDwsIAGz0YALu4tQCWk5BKwb645erm4P/x7Of/8u7p//Lv6v/w7ef/8Ozm//Ds + 5v/w7Ob/8Ozm/+/r5f/v6+X/7+vl/+/q5f/u6uT/7unk/+7o4//t6OP/7Oji/+zn4v/r5+L/6+bh/+rm + 4f/q5eD/6eXg/+nl3//o5N//6OTe/+fj3v/n493/5+Pd/+bi3f/m4tz/5eHc/+Xh2//k4Nr/5ODa/+Tg + 2f/j39n/4NzW/8C8t/uTkIqFdnRxCI2LhQDPz8EAAAAAAAAAAAAuLisA1dTRANXTzkmvrarzkIyG/xwb + GdEAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAeHhvAHZ2cgANFBQCnpyWZ8vHwu7q5eD/8e3o//Pw7P/z8Ov/8e3n//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm/+/r5f/v6+X/7+rl/+7p5P/u6eT/7ejj/+3o4//s5+L/7Ofi/+vn + 4f/r5uH/6ubh/+rl4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3v/n493/5uLd/+bi3P/j39n/yMW//5mW + kaNoZWERfn95AP///wAAAAAAAAAAAAAAAAAAAAAA0c3FAOPe1Q/KycazqaWh/3FtaPQODQxCLCooAK+n + nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH19 + eACRkIsAnJmVBaGfmmfBvrnl5uHc//Lu6f/18u7/9PHt//Hu6f/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/7+vl/+/q5f/u6uT/7unk/+7p5P/t6OP/7ejj/+zn + 4v/s5+L/6+fh/+rm4f/q5uD/6eXg/+nl4P/p5d//6eXf/+Pf2v/EwLv6m5eSm3p4cxuMioMAoKmXAH5z + cwAAAAAAAAAAAAAAAAAAAAAA3NjSAN7c2QDh3ttQtbOx8bKtp/+inZaLZmFaAq6mngAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVkYoAlZONAFJY + VQKZlZBOuLWw0dvX0v/v6+b/9fPv//b08P/z8Oz/8e3n//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm//Dr5v/v6+X/7+rl/+/q5f/u6eT/7unk/+3o + 4//t6OP/7Ofi/+zn4v/q5eD/3dnU/7u4s+uVkYx/dHNtD5SSjQAAAAAALCsqAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAODc1gDk3tYK2NbTn6qmo//HwLnTwbuyHbmyqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj42IAIaCfgD///8AiYeFK6ej + n5/Hw77x5eHc//Tx7f/49vP/9/Tx//Pw7P/x7ej/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w6+b/8Ovm//Dr5v/v6uX/7unk/+bh + 3P/KxsH9qKahxIuJhU1UUE4Fe3lzAI6LhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wDl4t4A7OnlLrCuqsOnpJ6e2NDJD763sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH11cgCPgoEAkI6LAHdycAuXlJBUsq+ruMvH + w/Xl4t7/9fPx//r49v/59/T/9fLu//Lv6v/x7ef/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8e3n/+/r5f/l4Nv/zsrF/bKuqdiTkIx7endzHK2m + qwCHhH8Ah4N/ALa2tgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM/JxADLyMEA4+DYAb26 + sxOmpJ4NuLGrAL63sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AY2VzAIyIhQCxq6cAiIaDE5qYlFauq6exxsTA6dza + 1//v7ev/+Pf1//r59//49vP/9vPv//Tw7P/y7+r/8u7p//Hu6P/x7ef/8e3n//Ht5//x7ef/8e3n//Hu + 6P/y7uj/8O3n/+3p4//m4tz/2tbR/8bCvfauq6bGmJWQeICAeigAAAABiYeCAHh4cADJyLwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADX1M8A6ebhALm2sACmpJ4AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSUjgCanJYAkI2KALy4swBqamkLjoyIN6OhnXSysKy3xsTA39bU + 0ffi4d7/7Orn//Du7P/z8O3/8/Dt//Lw7P/x7ur/8Ozo/+/r5//u6uX/6OXg/+Lf2v/Z1dD/zMjD/b67 + t+etqqbJn5yXko6MiExwb2sY////AI2MhQB+fHoAlpOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAADAwMAP///wCEgoEAlJGPAP///wBcW1kJe3p2LJGPi1KjoZ13rKmmmrKv + rLq5trPRvLq33L27t+C6uLTlt7Wx4ra0sNuyr6vYqKaiwaGemqeal5OEjouHYHx6djhraGcSAAAAAZqY + kwB+fHgAXmBeAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8Axse9ADUzMQBqa2oAfHt4AJCOigD///8AOjs5BmJgXg1+fHoThYSAFYB/ + eiF6eXUvenl2JoSDgBV8enYUZmVhDklHRQkAAAAClZKOAHx5dQBwbWwAeHd1AHJvbAD07OQABQUFAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUwB4eHUAenl3AIOBfwCUko8AmZeUAI+OigCGhYEAiIaDAJeV + kgCQjooAgn98AHRybwBwb2kAc3NsAP///wAgICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8AAAD///////////8A + AAD/////AH////8AAAD////AAAD///8AAAD///wAAAAf//8AAAD///gAAAAP//8AAAD//+AAAAAB//8A + AAD//4AAAAAA//8AAAD//wAAAAAAf/8AAAD//gAAAAAAP/8AAAD//AAAAAAAH/8AAAD/+AAAAAAAD/8A + AAD/8AAAAAAAB/8AAAD/8AAAAAAAB/8AAAD/8AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAP8A + AAD/4AAAAAAAAP8AAAD/4AAAAAAAAP8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8A + AAD/4AAAAAAAAD8AAAD/4AAAAAAAAD8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAA8A + AAD/4AAAAAAAAA8AAAD/4AAAAAAAAA8AAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcA + AAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAEA + AAD/8AAAAAAABAEAAAD/8AAAAAAABgEAAAD/+AAAAAAABwAAAAD/+AAAAAAADwAAAAD//gAAAAAAH4AA + AAD//wAAAAAAP4AAAAD//wAAAAAAf4AAAAD//4AAAAAA/8EAAAD///AAAAAD//8AAAD///wAAAAP//8A + AAD///4AAAA///8AAAD////gAAP///8AAAD///////////8AAAD///////////8AAAD///////////8A + AAA= + + + \ No newline at end of file diff --git a/DBChm/MainForm.cs b/DBChm/MainForm.cs new file mode 100644 index 0000000..d8c3980 --- /dev/null +++ b/DBChm/MainForm.cs @@ -0,0 +1,1121 @@ +using ComponentFactory.Krypton.Toolkit; +using DocTools; +using DocTools.DBDoc; +using DocTools.Dtos; +using MJTop.Data; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Windows.Forms; + + +namespace DBCHM +{ + public partial class MainForm : KryptonForm + { + public MainForm() + { + Control.CheckForIllegalCrossThreadCalls = false; + KryptonManager kryptonManager = new KryptonManager(); + this.InitializeComponent(); + this.InitializeRibbonTabContainer(); + kryptonManager.GlobalPaletteMode = PaletteModeManager.Office2010Blue; + } + + + #region Ribbon + + /// + /// Const Field RIBBON_COLLAPSE_HEIGHT. + /// + private const int RIBBON_COLLAPSE_HEIGHT = 22; + + /// + /// Const Field RIBBON_EXPAND_HEIGHT. + /// + private const int RIBBON_EXPAND_HEIGHT = 100; + + /// + /// Const Field FORM_BORDER_HEIGHT. + /// + private const int FORM_BORDER_HEIGHT = 60; + + /// + /// Field _isRibbonTabExpand. + /// + private bool _isRibbonTabExpand; + + /// + /// Field _isRibbonTabShow. + /// + private bool _isRibbonTabShow; + + /// + /// Method InitializeRibbonTabContainer. + /// + private void InitializeRibbonTabContainer() + { + this._isRibbonTabExpand = true; + this._isRibbonTabShow = true; + this.CollapseRibbonTabContainer(!this._isRibbonTabExpand); + this.RibbonTabContainer.LostFocus += this.HideRibbon; + this.ribbonPageFile.ItemClicked += this.HideRibbon; + this.ribbonPageAbout.ItemClicked += this.HideRibbon; + } + + + /// + /// Method RibbonTabContainer_MouseDoubleClick. + /// + /// Event sender. + /// Instance of MouseEventArgs. + private void RibbonTabContainer_MouseDoubleClick(object sender, MouseEventArgs e) + { + this.CollapseRibbonTabContainer(this._isRibbonTabExpand); + } + + /// + /// Method CollapseRibbonTabContainer. + /// + /// Indicate whether collapse or not. + private void CollapseRibbonTabContainer(bool whetherCollapse) + { + if (whetherCollapse) + { + this.RibbonTabContainer.Height = RIBBON_COLLAPSE_HEIGHT; + this._isRibbonTabExpand = false; + this._isRibbonTabShow = false; + } + else + { + this.RibbonTabContainer.Height = RIBBON_EXPAND_HEIGHT; + this._isRibbonTabExpand = true; + this._isRibbonTabShow = true; + } + } + + /// + /// Method RibbonTabContainer_MouseClick. + /// + /// Event sender. + /// Instance of MouseEventArgs. + private void RibbonTabContainer_MouseClick(object sender, MouseEventArgs e) + { + if (!this._isRibbonTabExpand) + { + if (!this._isRibbonTabShow) + { + this.RibbonTabContainer.Height = RIBBON_EXPAND_HEIGHT; + this.RibbonTabContainer.BringToFront(); + this._isRibbonTabShow = true; + } + else + { + this.RibbonTabContainer.Height = RIBBON_COLLAPSE_HEIGHT; + this._isRibbonTabShow = false; + } + } + } + + /// + /// Method RibbonTabContainer_Selected. + /// + /// Event sender. + /// Instance of TabControlEventArgs. + private void RibbonTabContainer_Selected(object sender, TabControlEventArgs e) + { + this._isRibbonTabShow = false; + } + + /// + /// Method RibbonTabContainer_Selecting. + /// + /// Event sender. + /// Instance of TabControlCancelEventArgs. + private void RibbonTabContainer_Selecting(object sender, TabControlCancelEventArgs e) + { + if (!this._isRibbonTabExpand) + { + this.RibbonTabContainer.Height = RIBBON_EXPAND_HEIGHT; + this.RibbonTabContainer.BringToFront(); + } + } + + /// + /// Method HideRibbon. + /// + /// Event sender. + /// Instance of EventArgs. + private void HideRibbon(object sender, EventArgs e) + { + if (!this._isRibbonTabExpand) + { + if (this._isRibbonTabShow) + { + this.RibbonTabContainer.Height = RIBBON_COLLAPSE_HEIGHT; + this._isRibbonTabShow = false; + } + } + } + + private void ribbonPageFile_ItemClicked(object sender, ToolStripItemClickedEventArgs e) + { + + } + #endregion + + private string selectedCountDesc = "共选择{0}个项目"; + + private string selectedItemDesc = "表:{0},视图:{1},存储过程:{2}"; + + private delegate void ikMethod(); + + /// + /// 搜索关键字 + /// + public List SearchWords { get; set; } = new List(); + + public DBDto DbDto { get; set; } + + private void MainForm_Load(object sender, EventArgs e) + { + this.Hide(); + if (DBUtils.Instance == null) + { + GridFormMgr conMgrForm = new GridFormMgr(); + var diaRes = conMgrForm.ShowDialog(this); + if (diaRes == DialogResult.OK || FormUtils.IsOK_Close) + { + FormUtils.IsOK_Close = false; + _selectedTables.Clear(); + InitTree(); + this.Show(); + } + } + } + + + #region 初始化树 + + private PdmModels.SelectedTables _selectedTables = new PdmModels.SelectedTables(); + + private void InitTree() + { + if (DBUtils.Instance?.Info == null) + { + return; + } + + lblTip.Text = string.Empty; + treeDB.Nodes.Clear(); + + if (!string.IsNullOrWhiteSpace(DBUtils.Instance?.Info?.DBName)) + { + var ver = ((System.Reflection.AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)?.FirstOrDefault())?.Version; + if (ver == null) + { + ver = Assembly.GetExecutingAssembly().GetName()?.Version?.ToString(); + } + this.Text = DBUtils.Instance?.Info?.DBName + "(" + DBUtils.Instance.DBType.ToString() + ") - " + "DBCHM v" + ver; + } + + BtnSaveGridData.Enabled = true; + + if (DBUtils.Instance.DBType == MJTop.Data.DBType.SQLite) + { + SetMsg(DBUtils.Instance.DBType + "数据库不支持批注功能!", false); + BtnSaveGridData.Enabled = false; + } + + if (DBUtils.Instance.Info.TableNames.Count > 0) + { + TreeNode tnTable = new TreeNode("表", 0, 0) { Name = "table" }; + int ckCount = 0; + foreach (var tbName in DBUtils.Instance.Info.TableNames) + { + if (SearchWords.Any()) + { + foreach (var word in SearchWords) + { + if (tbName.ToLower().Contains(word.ToLower())) + { + TreeNode node = new TreeNode(tbName, 1, 1); + node.Name = tbName; + if (this._selectedTables.tables.Any(u => String.Equals(u.TableName, tbName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnTable.Nodes.Add(node); + break; + } + } + } + else + { + //tnTable.Nodes.Add(tbName, tbName, 1, 1); + TreeNode node = new TreeNode(tbName, 1, 1); + node.Name = tbName; + if (this._selectedTables.tables.Any(u => String.Equals(u.TableName, tbName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnTable.Nodes.Add(node); + } + } + + //try + { + tnTable.Checked = tnTable.Nodes.Count == ckCount; + treeDB.Nodes.Add(tnTable); + } + //catch (Exception ex) + //{ + //} + } + + if (DBUtils.Instance.Info.Views.Keys.Count > 0) + { + TreeNode tnView = new TreeNode("视图", 0, 0) { Name = "view" }; + int ckCount = 0; + foreach (var vwName in DBUtils.Instance.Info.Views.AllKeys) + { + if (SearchWords.Any()) + { + foreach (var word in SearchWords) + { + if (vwName.ToLower().Contains(word.ToLower())) + { + //tnView.Nodes.Add(vwName, vwName, 2, 2); + TreeNode node = new TreeNode(vwName, 2, 2); + node.Name = vwName; + if (this._selectedTables.viewDict.Keys.Any(u => String.Equals(u, vwName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnView.Nodes.Add(node); + break; + } + } + } + else + { + //tnView.Nodes.Add(vwName, vwName, 2, 2); + TreeNode node = new TreeNode(vwName, 2, 2); + node.Name = vwName; + if (this._selectedTables.viewDict.Keys.Any(u => String.Equals(u, vwName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnView.Nodes.Add(node); + } + } + tnView.Checked = tnView.Nodes.Count == ckCount; + treeDB.Nodes.Add(tnView); + } + + if (DBUtils.Instance.Info.Procs.Keys.Count > 0) + { + TreeNode tnProc = new TreeNode("存储过程", 0, 0) { Name = "proc" }; + int ckCount = 0; + foreach (var procName in DBUtils.Instance.Info.Procs.AllKeys) + { + if (SearchWords.Any()) + { + foreach (var word in SearchWords) + { + if (procName.ToLower().Contains(word.ToLower())) + { + //tnProc.Nodes.Add(procName, procName, 3, 3); + TreeNode node = new TreeNode(procName, 3, 3); + node.Name = procName; + if (this._selectedTables.procDict.Keys.Any(u => String.Equals(u, procName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnProc.Nodes.Add(node); + break; + } + } + } + else + { + //tnProc.Nodes.Add(procName, procName, 3, 3); + TreeNode node = new TreeNode(procName, 3, 3); + node.Name = procName; + if (this._selectedTables.procDict.Keys.Any(u => String.Equals(u, procName, StringComparison.OrdinalIgnoreCase))) + { + node.Checked = true; + ckCount++; + } + tnProc.Nodes.Add(node); + } + } + tnProc.Checked = tnProc.Nodes.Count == ckCount; + treeDB.Nodes.Add(tnProc); + } + + foreach (TreeNode node in treeDB.Nodes) + { + if (node.Nodes.Count > 0) + { + treeDB.SelectedNode = node.Nodes[0]; + node.Expand(); + break; + } + } + + //treeDB.ExpandAll(); + //this.TransToDto(); + this.TongJi(); + } + + /// + /// 根据选择项转换为DTO数据 + /// + public void TransToDto(bool import = false) + { + if (DBUtils.Instance == null) + { + return; + } + + this.DbDto = new DBDto() + { + DBName = DBUtils.Instance.Info.DBName, + DBType = DBUtils.Instance.DBType.ToString() + }; + + var tableNode = treeDB.Nodes["table"]; + + var viewNode = treeDB.Nodes["view"]; + + var procNode = treeDB.Nodes["proc"]; + + //只要有任意一个1个勾选,勾选就起作用 + var isHaveChecked = (tableNode != null && tableNode.Nodes.OfType().Any(t => t.Checked)) + || this._selectedTables.tables.Count > 0 + || (viewNode != null && viewNode.Nodes.OfType().Any(t => t.Checked)) + || this._selectedTables.viewDict.Count > 0 + || (procNode != null && procNode.Nodes.OfType().Any(t => t.Checked)) + || this._selectedTables.procDict.Count > 0; + + + //var tables = new List(); + //var viewDict = new Dictionary(); + //var procDict = new Dictionary(); + + if (tableNode != null) + { + if (isHaveChecked || tableNode.Nodes.OfType().Any(t => t.Checked)) + { + var xh = 1; + foreach (TreeNode node in tableNode.Nodes) + { + string tableName = node.Name; + if (node.Checked) + { + if (this._selectedTables.tables.All(u => !String.Equals(u.TableName, tableName, StringComparison.OrdinalIgnoreCase))) + { + this._selectedTables.tables.Add(Trans2Table(node, ref xh)); + } + } + else + { + var table = this._selectedTables.tables.FirstOrDefault(u => String.Equals(u.TableName, tableName, StringComparison.OrdinalIgnoreCase)); + if (table != null) + { + this._selectedTables.tables.Remove(table); + } + } + } + } + else if (import) //导出时再选中所有 + { + var xh = 1; + foreach (TreeNode node in tableNode.Nodes) + { + string tableName = node.Name; + if (this._selectedTables.tables.All(u => !String.Equals(u.TableName, tableName, StringComparison.OrdinalIgnoreCase))) + { + this._selectedTables.tables.Add(Trans2Table(node, ref xh)); + } + //tables.Add(Trans2Table(node, ref xh)); + } + } + } + + if (viewNode != null) + { + if (isHaveChecked || viewNode.Nodes.OfType().Any(t => t.Checked)) + { + foreach (TreeNode node in viewNode.Nodes) + { + string viewName = node.Name; + var view = this._selectedTables.viewDict.Keys.FirstOrDefault(u => String.Equals(u, viewName, StringComparison.OrdinalIgnoreCase)); + if (node.Checked) + { + if (String.IsNullOrEmpty(view)) + { + this._selectedTables.viewDict.Add(node.Name, DBUtils.Instance.Info.Views[node.Name]); + } + } + else + { + if (!String.IsNullOrEmpty(view)) + { + this._selectedTables.viewDict.Remove(view); + } + } + } + } + else if (import) + { + var dict = DBUtils.Instance.Info.Views.ToDictionary(); + var keys = dict.Keys; + foreach (var key in keys) + { + this._selectedTables.viewDict[key] = dict[key]; + } + } + } + + if (procNode != null) + { + if (isHaveChecked || procNode.Nodes.OfType().Any(t => t.Checked)) + { + foreach (TreeNode node in procNode.Nodes) + { + var procKey = this._selectedTables.procDict.Keys.FirstOrDefault(u => String.Equals(u, node.Name, StringComparison.OrdinalIgnoreCase)); + if (node.Checked) + { + if (String.IsNullOrEmpty(procKey)) + { + this._selectedTables.procDict.Add(node.Name, DBUtils.Instance.Info.Procs[node.Name]); + } + } + else + { + if (!String.IsNullOrEmpty(procKey)) + { + this._selectedTables.procDict.Remove(procKey); + } + } + } + } + else if (import) + { + var dict = DBUtils.Instance.Info.Procs.ToDictionary(); + foreach (var key in dict.Keys) + { + this._selectedTables.procDict[key] = dict[key]; + } + } + } + + DbDto.Tables = this._selectedTables.tables; + DbDto.Views = this._selectedTables.viewDict; + DbDto.Procs = this._selectedTables.procDict; + } + + private TableDto Trans2Table(TreeNode node, ref int xh) + { + TableDto tbDto = new TableDto(); + tbDto.TableOrder = xh.ToString(); + tbDto.TableName = node.Name; + tbDto.Comment = DBUtils.Instance.Info.TableComments[node.Name]; + tbDto.DBType = DBUtils.Instance.DBType.ToString(); + + var lst_col_dto = new List(); + var columns = DBUtils.Instance.Info.TableColumnInfoDict[node.Name]; + foreach (var col in columns) + { + ColumnDto colDto = new ColumnDto(); + colDto.ColumnOrder = col.Colorder.ToString(); + colDto.ColumnName = col.ColumnName; + // 数据类型 + colDto.ColumnTypeName = col.TypeName; + // 长度 + colDto.Length = (col.Length.HasValue ? col.Length.Value.ToString() : ""); + // 小数位 + colDto.Scale = (col.Scale.HasValue ? col.Scale.Value.ToString() : ""); + // 主键 + colDto.IsPK = (col.IsPK ? "√" : ""); + // 自增 + colDto.IsIdentity = (col.IsIdentity ? "√" : ""); + // 允许空 + colDto.CanNull = (col.CanNull ? "√" : ""); + // 默认值 + colDto.DefaultVal = (!string.IsNullOrWhiteSpace(col.DefaultVal) ? col.DefaultVal : ""); + // 列注释(说明) + colDto.Comment = (!string.IsNullOrWhiteSpace(col.DeText) ? col.DeText : ""); + + lst_col_dto.Add(colDto); + } + + tbDto.Columns = lst_col_dto; + xh++; + return tbDto; + } + + #endregion + + + /// + /// 模糊搜索数据表名 + /// + /// + /// + private void TxtSearchWords_TextChanged(object sender, EventArgs e) + { + string searchWords = TxtSearchWords.Text.Trim(); + this.SearchWords = new List(); + if (!string.IsNullOrWhiteSpace(searchWords)) + { + var words = searchWords.Split(new string[] { ",", "," }, StringSplitOptions.RemoveEmptyEntries); + + this.SearchWords = new List(words.Distinct()); + } + + ikMethod method = new ikMethod(InitTree); + this.Invoke(method); + } + + + /// + /// 数据连接 + /// + /// + /// + private void tsbConnect_Click(object sender, EventArgs e) + { + GridFormMgr conMgrForm = new GridFormMgr(); + var diaRes = conMgrForm.ShowDialog(this); + if (diaRes == DialogResult.OK || FormUtils.IsOK_Close) //当前窗体 是正常关闭的情况下 + { + FormUtils.IsOK_Close = false; + } + _selectedTables.Clear(); + InitTree(); + } + + /// + /// 重新获取 + /// + /// + /// + private void tsbRefresh_Click(object sender, EventArgs e) + { + FormUtils.ShowProcessing("正在查询表结构信息,请稍等......", this, arg => + { + DBUtils.Instance?.Info?.Refresh(); + this._selectedTables.Clear(); + TxtSearchWords_TextChanged(sender, e); + }, null); + } + + + private void tsbBuild_Click(object sender, EventArgs e) + { + var btn = sender as System.Windows.Forms.ToolStripButton; + if (btn.Tag == null) + { + MessageBox.Show("菜单未指定Tag值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + //如果所有节点都没选中,则导出全部 + var copySelected = this._selectedTables.DeepCopy(); + this.TransToDto(true); + this._selectedTables = copySelected; + + var docType = btn.Tag.ToString().GetEnum(); + var doc = DocFactory.CreateInstance(docType, this.DbDto); + + SaveFileDialog saveDia = new SaveFileDialog(); + saveDia.Filter = doc.Filter; + saveDia.Title = "另存文件为"; + saveDia.CheckPathExists = true; + saveDia.AddExtension = true; + saveDia.AutoUpgradeEnabled = true; + saveDia.DefaultExt = doc.Ext; + saveDia.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + saveDia.OverwritePrompt = true; + saveDia.ValidateNames = true; + saveDia.FileName = doc.Dto.DBName + "表结构信息" + doc.Ext; + var diaResult = saveDia.ShowDialog(); + + if (diaResult == DialogResult.OK) + { + FormUtils.ShowProcessing("正在导出文档,请稍等......", this, arg => + { + try + { + doc.Build(saveDia.FileName); + } + catch (Exception ex) + { + LogUtils.LogError("tsbBuild_Click", Developer.SysDefault, ex, saveDia.FileName, DBUtils.Instance.Info); + } + + }, null); + } + } + + private void SetMsg(string msg, bool isSuccess = true) + { + lblTip.Text = msg; + lblTip.ForeColor = isSuccess ? System.Drawing.Color.Green : System.Drawing.Color.Red; + } + + private void BtnSaveGridData_Click(object sender, EventArgs e) + { + TableDto tableDto = null; + try + { + DBUtils.Instance?.Info?.SetTableComment(LabCurrTabName.Text, TxtCurrTabComment.Text); + //tableDto = this.DbDto.Tables.Where(t => t.TableName == LabCurrTabName.Text).FirstOrDefault(); + + var tableNode = treeDB.Nodes["table"]; + if (tableNode != null) + { + var xh = 1; + foreach (TreeNode node in tableNode.Nodes) + { + string tableName = node.Name; + if(String.Equals(tableName, LabCurrTabName.Text, StringComparison.OrdinalIgnoreCase)) + { + tableDto = Trans2Table(node, ref xh); + break; + } + } + } + + tableDto.Comment = TxtCurrTabComment.Text; + } + catch (Exception ex) + { + LogUtils.LogError("Save TableComment", Developer.SysDefault, ex, LabCurrTabName.Text, TxtCurrTabComment.Text); + SetMsg(ex.Message, false); + return; + } + + for (int j = 0; j < GV_ColComments.Rows.Count; j++) + { + string columnName = GV_ColComments.Rows[j].Cells["ColName"].Value.ToString(); + + string colComment = (GV_ColComments.Rows[j].Cells["ColComment"].Value ?? string.Empty).ToString(); + + try + { + DBUtils.Instance?.Info?.SetColumnComment(LabCurrTabName.Text, columnName, colComment); + tableDto.Columns.Find(t => t.ColumnName == columnName).Comment = colComment; + } + catch (Exception ex) + { + LogUtils.LogError("Save ColComment", Developer.SysDefault, ex, columnName, ColComment); + SetMsg(ex.Message, false); + return; + } + } + + SetMsg("保存成功!"); + } + + + + + #region 工具 + + /// + /// pdm上传 + /// + /// + /// + private void tsbSaveUpload_Click(object sender, EventArgs e) + { + ImportForm pdmForm = new ImportForm(); + DialogResult dirRes = pdmForm.ShowDialog(this); + if (dirRes == DialogResult.OK || FormUtils.IsOK_Close) + { + FormUtils.IsOK_Close = false; + TxtSearchWords_TextChanged(sender, e); + } + } + + #endregion + + + #region 关于 + + private void toolStripButtonAbout_Click(object sender, EventArgs e) + { + AboutBox aboutForm = new AboutBox(); + aboutForm.ShowDialog(); + } + + #endregion + + private void MainForm_Resize(object sender, EventArgs e) + { + + } + + private void GV_ColComments_CellClick(object sender, DataGridViewCellEventArgs e) + { + if (GV_ColComments.Columns[e.ColumnIndex].Name.Equals("ColComment", StringComparison.OrdinalIgnoreCase)) + { + //object obj = GV_ColComments.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; + //string content = obj.ToString(); + + // 点击单元格 默认选中内容 + this.GV_ColComments.CurrentCell = this.GV_ColComments[e.ColumnIndex, e.RowIndex]; + this.GV_ColComments.BeginEdit(true); + } + } + + void TongJi() + { + TransToDto(); + + // 不是无限级,不需要用递归 + int tableCount = 0; + int viewCount = 0; + int procCount = 0; + + bool chk = false; + foreach (TreeNode node in treeDB.Nodes) + { + if (node.Nodes.Count > 0) + { + foreach (TreeNode item in node.Nodes) + { + chk = item.Checked; + if (chk) + { + break; + } + } + } + else + { + chk = node.Checked; + } + if (chk) + { + break; + } + } + + if (chk) //有选中 + { + tableCount = this._selectedTables.tables.Count; + viewCount = this._selectedTables.viewDict.Count; + procCount = this._selectedTables.procDict.Count; + } + + int totalCkCount = tableCount + viewCount + procCount; + //foreach (TreeNode node in treeDB.Nodes) + //{ + // int ckCount = 0; + // foreach (TreeNode item in node.Nodes) + // { + // if (item.Checked) + // { + // totalCkCount++; + // ckCount++; + // } + // } + // + // if (node.Name == "table") + // { + // tableCount = ckCount; + // } + // else if (node.Name == "view") + // { + // viewCount = ckCount; + // } + // else if (node.Name == "proc") + // { + // procCount = ckCount; + // } + //} + + lblSelectRes.Text = string.Format(selectedCountDesc, totalCkCount); + lblTongJi.Text = string.Format(selectedItemDesc, tableCount, viewCount, procCount); + + } + + #region 勾选选中 + + private bool _skipCheckedChanged = false; + + //全选 + private void CkAll_CheckedChanged(object sender, EventArgs e) + { + if (CkAll.Checked) + { + CkReverse.Checked = false; + } + else if (CkReverse.Checked) + { + return; + } + HandleNode(treeDB.Nodes, CkAll.Checked); + TongJi(); + } + + void HandleNode(TreeNodeCollection nodeColl, bool cked) + { + if (nodeColl != null) + { + foreach (TreeNode node in nodeColl) + { + node.Checked = cked; + HandleNode_SelectedTabls(node); + if (node.Nodes != null && node.Nodes.Count > 0) + { + HandleNode(node.Nodes, cked); + } + } + } + } + + /// + /// 选中后设置已选集合 + /// + /// + void HandleNode_SelectedTabls(TreeNode node) + { + bool cked = node.Checked; + switch (node.ImageIndex) + { + case 1: //表 + var table = this._selectedTables.tables.FirstOrDefault(u => String.Equals(u.TableName, node.Name, StringComparison.OrdinalIgnoreCase)); + if (cked) + { + if (table == null) + { + int xh = 0; + this._selectedTables.tables.Add(Trans2Table(node, ref xh)); + } + } + else + { + if (table != null) + { + this._selectedTables.tables.Remove(table); + } + } + break; + case 2: //视图 + var viewKey = this._selectedTables.viewDict.Keys.FirstOrDefault(u => String.Equals(u, node.Name, StringComparison.OrdinalIgnoreCase)); + if (cked) + { + if (String.IsNullOrEmpty(viewKey)) + { + this._selectedTables.viewDict.Add(node.Name, DBUtils.Instance.Info.Views[node.Name]); + } + } + else + { + if (!String.IsNullOrEmpty(viewKey)) + { + this._selectedTables.viewDict.Remove(viewKey); + } + } + break; + case 3: //存储过程 + var procKey = this._selectedTables.procDict.Keys.FirstOrDefault(u => String.Equals(u, node.Name, StringComparison.OrdinalIgnoreCase)); + if (cked) + { + if (String.IsNullOrEmpty(procKey)) + { + this._selectedTables.procDict.Add(node.Name, DBUtils.Instance.Info.Procs[node.Name]); + } + } + else + { + if (!String.IsNullOrEmpty(procKey)) + { + this._selectedTables.procDict.Remove(procKey); + } + } + break; + } + } + + //反选 + private void CkReverse_CheckedChanged(object sender, EventArgs e) + { + if (CkReverse.Checked) + { + CkAll.Checked = false; + } + else if (CkAll.Checked) + { + return; + } + ReverseHandleNode(treeDB.Nodes); + TongJi(); + } + void ReverseHandleNode(TreeNodeCollection nodeColl) + { + if (nodeColl != null) + { + foreach (TreeNode node in nodeColl) + { + if (node.Nodes != null && node.Nodes.Count > 0) + { + ReverseHandleNode(node.Nodes); + + //处理受影响的父节点 + int ckCount = 0; + foreach (TreeNode childNode in node.Nodes) + { + if (childNode.Checked) + { + ckCount++; + } + } + // 子节点个数 与 选中的子节点个数 比较 + node.Checked = node.Nodes.Count == ckCount; + } + else + { + node.Checked = !node.Checked; + HandleNode_SelectedTabls(node); + } + } + } + } + + private void treeDB_AfterCheck(object sender, TreeViewEventArgs e) + { + if (e.Action == TreeViewAction.ByMouse || e.Action == TreeViewAction.ByKeyboard) + { + var node = e.Node; + HandleNode_SelectedTabls(node); + HandleNode(node.Nodes, node.Checked); + + if (node.Parent != null) + { + //没有子节点的节点 + int ckCount = 0; + foreach (TreeNode childNode in node.Parent.Nodes) + { + if (childNode.Checked) + { + ckCount++; + } + } + // 子节点个数 与 选中的子节点个数 比较 + node.Parent.Checked = node.Parent.Nodes.Count == ckCount; + } + + TongJi(); + } + } + + #endregion + + + private void CodeAreaShow() + { + codePnl.Visible = true; + codePnl.Location = new Point(6, 20); + pizhuPnl.Visible = false; + } + + private void PiZhuAreaShow() + { + pizhuPnl.Visible = true; + pizhuPnl.Location = new Point(6, 20); + codePnl.Visible = false; + } + + private void treeDB_AfterSelect(object sender, TreeViewEventArgs e) + { + if (DBUtils.Instance.DBType != MJTop.Data.DBType.SQLite) + { + lblTip.Text = string.Empty; + } + + // 都是子项 + if (e.Node.Parent != null) + { + if (e.Node.Parent.Name == "table") + { + PiZhuAreaShow(); + + GV_ColComments.Rows.Clear(); + + var tabInfo = DBUtils.Instance.Info.TableInfoDict[e.Node.Text]; + LabCurrTabName.Text = tabInfo?.TableName; + TxtCurrTabComment.Text = tabInfo?.TabComment; + var columnInfos = tabInfo?.Colnumns; + if (columnInfos != null) + { + for (int j = 0; j < columnInfos.Count; j++) + { + var colInfo = columnInfos[j]; + + GV_ColComments.Rows.Add(j + 1, colInfo.ColumnName, colInfo.TypeName, colInfo.Length, colInfo.DeText); + } + } + + } + else + { + CodeAreaShow(); + + var code = string.Empty; + + if (e.Node.Parent.Name == "view") + { + code = JS.RunFmtSql(DBUtils.Instance.Info.Views[e.Node.Text], DBUtils.Instance.DBType.ToString()); + } + else if (e.Node.Parent.Name == "proc") + { + code = JS.RunFmtSql(DBUtils.Instance.Info.Procs[e.Node.Text], DBUtils.Instance.DBType.ToString()); + } + + txtCode.Text = code; + } + } + + } + + private void GV_ColComments_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + if (e.Control && e.KeyCode == Keys.V && GV_ColComments.SelectedCells.Count > 0) + { + var cell = GV_ColComments.SelectedCells[0]; + var colName = GV_ColComments.Columns[cell.ColumnIndex].Name; + var text = Clipboard.GetText(); + if (colName == "ColComment" && !string.IsNullOrWhiteSpace(text)) + { + var lines = text.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + if (lines.Length > 0) + { + for (int j = 0; j < lines.Length; j++) + { + GV_ColComments.Rows[cell.RowIndex + j].Cells[cell.ColumnIndex].Value = lines[j]; + } + } + } + } + } + } +} diff --git a/DBChm/MainForm.designer.cs b/DBChm/MainForm.designer.cs new file mode 100644 index 0000000..5ee8e64 --- /dev/null +++ b/DBChm/MainForm.designer.cs @@ -0,0 +1,837 @@ +namespace DBCHM +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle(); + this.imgList = new System.Windows.Forms.ImageList(this.components); + this.tlTip = new System.Windows.Forms.ToolTip(this.components); + this.TxtSearchWords = new System.Windows.Forms.TextBox(); + this.tabLayoutPnl = new System.Windows.Forms.TableLayoutPanel(); + this.mainPnl = new System.Windows.Forms.Panel(); + this.treePnl = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.lblTongJi = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.CkAll = new System.Windows.Forms.CheckBox(); + this.CkReverse = new System.Windows.Forms.CheckBox(); + this.lblSelectRes = new System.Windows.Forms.Label(); + this.gpxHandle = new System.Windows.Forms.GroupBox(); + this.codePnl = new System.Windows.Forms.Panel(); + this.txtCode = new ICSharpCode.TextEditor.TextEditorControl(); + this.pizhuPnl = new System.Windows.Forms.Panel(); + this.gpCurrTable = new System.Windows.Forms.GroupBox(); + this.labCurrTabComment = new System.Windows.Forms.Label(); + this.TxtCurrTabComment = new System.Windows.Forms.TextBox(); + this.LabCurrTabName = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.GpColumns = new System.Windows.Forms.GroupBox(); + this.lblTip = new System.Windows.Forms.Label(); + this.GV_ColComments = new System.Windows.Forms.DataGridView(); + this.ColXuHao = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ColDataType = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ColLength = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ColComment = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.BtnSaveGridData = new ComponentFactory.Krypton.Toolkit.KryptonButton(); + this.RibbonTabContainer = new System.Windows.Forms.TabControl(); + this.tpfile = new System.Windows.Forms.TabPage(); + this.ribbonPageFile = new System.Windows.Forms.ToolStrip(); + this.tsbConnect = new System.Windows.Forms.ToolStripButton(); + this.tsbRefresh = new System.Windows.Forms.ToolStripButton(); + this.tsbBuild = new System.Windows.Forms.ToolStripButton(); + this.tsWordExp = new System.Windows.Forms.ToolStripButton(); + this.tsExcelExp = new System.Windows.Forms.ToolStripButton(); + this.tsPdfExp = new System.Windows.Forms.ToolStripButton(); + this.tsHtmlExp = new System.Windows.Forms.ToolStripButton(); + this.tsXmlExp = new System.Windows.Forms.ToolStripButton(); + this.tsMarkDownExp = new System.Windows.Forms.ToolStripButton(); + this.tptool = new System.Windows.Forms.TabPage(); + this.tsbPDMUpload = new System.Windows.Forms.ToolStrip(); + this.toolStripButton3 = new System.Windows.Forms.ToolStripButton(); + this.apabout = new System.Windows.Forms.TabPage(); + this.ribbonPageAbout = new System.Windows.Forms.ToolStrip(); + this.toolStripButtonAbout = new System.Windows.Forms.ToolStripButton(); + this.miniToolStrip = new System.Windows.Forms.ToolStrip(); + this.treeDB = new DBCHM.TreeViewEnhanced(); + this.tabLayoutPnl.SuspendLayout(); + this.mainPnl.SuspendLayout(); + this.treePnl.SuspendLayout(); + this.gpxHandle.SuspendLayout(); + this.codePnl.SuspendLayout(); + this.pizhuPnl.SuspendLayout(); + this.gpCurrTable.SuspendLayout(); + this.GpColumns.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.GV_ColComments)).BeginInit(); + this.RibbonTabContainer.SuspendLayout(); + this.tpfile.SuspendLayout(); + this.ribbonPageFile.SuspendLayout(); + this.tptool.SuspendLayout(); + this.tsbPDMUpload.SuspendLayout(); + this.apabout.SuspendLayout(); + this.ribbonPageAbout.SuspendLayout(); + this.SuspendLayout(); + // + // imgList + // + this.imgList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imgList.ImageStream"))); + this.imgList.TransparentColor = System.Drawing.Color.Transparent; + this.imgList.Images.SetKeyName(0, "folder"); + this.imgList.Images.SetKeyName(1, "table"); + this.imgList.Images.SetKeyName(2, "view"); + this.imgList.Images.SetKeyName(3, "proc"); + this.imgList.Images.SetKeyName(4, "func"); + // + // tlTip + // + this.tlTip.AutoPopDelay = 5000; + this.tlTip.InitialDelay = 100; + this.tlTip.IsBalloon = true; + this.tlTip.ReshowDelay = 100; + this.tlTip.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + this.tlTip.ToolTipTitle = "提示"; + // + // TxtSearchWords + // + this.TxtSearchWords.Location = new System.Drawing.Point(58, 9); + this.TxtSearchWords.Margin = new System.Windows.Forms.Padding(2); + this.TxtSearchWords.Name = "TxtSearchWords"; + this.TxtSearchWords.Size = new System.Drawing.Size(221, 21); + this.TxtSearchWords.TabIndex = 13; + this.tlTip.SetToolTip(this.TxtSearchWords, "多个关键字搜索用英文逗号(,)隔开!"); + this.TxtSearchWords.TextChanged += new System.EventHandler(this.TxtSearchWords_TextChanged); + // + // tabLayoutPnl + // + this.tabLayoutPnl.AutoSize = true; + this.tabLayoutPnl.ColumnCount = 1; + this.tabLayoutPnl.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tabLayoutPnl.Controls.Add(this.mainPnl, 0, 1); + this.tabLayoutPnl.Controls.Add(this.RibbonTabContainer, 0, 0); + this.tabLayoutPnl.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabLayoutPnl.Location = new System.Drawing.Point(0, 0); + this.tabLayoutPnl.Name = "tabLayoutPnl"; + this.tabLayoutPnl.RowCount = 2; + this.tabLayoutPnl.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 15F)); + this.tabLayoutPnl.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 85F)); + this.tabLayoutPnl.Size = new System.Drawing.Size(1076, 700); + this.tabLayoutPnl.TabIndex = 5; + // + // mainPnl + // + this.mainPnl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.mainPnl.Controls.Add(this.treePnl); + this.mainPnl.Controls.Add(this.gpxHandle); + this.mainPnl.Location = new System.Drawing.Point(3, 108); + this.mainPnl.Name = "mainPnl"; + this.mainPnl.Size = new System.Drawing.Size(1070, 589); + this.mainPnl.TabIndex = 14; + // + // treePnl + // + this.treePnl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.treePnl.Controls.Add(this.label2); + this.treePnl.Controls.Add(this.treeDB); + this.treePnl.Controls.Add(this.lblTongJi); + this.treePnl.Controls.Add(this.label1); + this.treePnl.Controls.Add(this.CkAll); + this.treePnl.Controls.Add(this.TxtSearchWords); + this.treePnl.Controls.Add(this.CkReverse); + this.treePnl.Controls.Add(this.lblSelectRes); + this.treePnl.Location = new System.Drawing.Point(3, 0); + this.treePnl.Name = "treePnl"; + this.treePnl.Size = new System.Drawing.Size(324, 589); + this.treePnl.TabIndex = 20; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(5, 571); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(0, 12); + this.label2.TabIndex = 19; + // + // lblTongJi + // + this.lblTongJi.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblTongJi.AutoSize = true; + this.lblTongJi.Location = new System.Drawing.Point(5, 571); + this.lblTongJi.Name = "lblTongJi"; + this.lblTongJi.Size = new System.Drawing.Size(0, 12); + this.lblTongJi.TabIndex = 19; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(14, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(29, 12); + this.label1.TabIndex = 16; + this.label1.Text = "搜索"; + // + // CkAll + // + this.CkAll.AutoSize = true; + this.CkAll.Location = new System.Drawing.Point(16, 34); + this.CkAll.Name = "CkAll"; + this.CkAll.Size = new System.Drawing.Size(48, 16); + this.CkAll.TabIndex = 15; + this.CkAll.Text = "全选"; + this.CkAll.UseVisualStyleBackColor = true; + this.CkAll.CheckedChanged += new System.EventHandler(this.CkAll_CheckedChanged); + // + // CkReverse + // + this.CkReverse.AutoSize = true; + this.CkReverse.Location = new System.Drawing.Point(70, 35); + this.CkReverse.Name = "CkReverse"; + this.CkReverse.Size = new System.Drawing.Size(48, 16); + this.CkReverse.TabIndex = 17; + this.CkReverse.Text = "反选"; + this.CkReverse.UseVisualStyleBackColor = true; + this.CkReverse.CheckedChanged += new System.EventHandler(this.CkReverse_CheckedChanged); + // + // lblSelectRes + // + this.lblSelectRes.AutoSize = true; + this.lblSelectRes.Location = new System.Drawing.Point(191, 36); + this.lblSelectRes.Name = "lblSelectRes"; + this.lblSelectRes.Size = new System.Drawing.Size(83, 12); + this.lblSelectRes.TabIndex = 16; + this.lblSelectRes.Text = "已选择0个项目"; + // + // gpxHandle + // + this.gpxHandle.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gpxHandle.Controls.Add(this.codePnl); + this.gpxHandle.Controls.Add(this.pizhuPnl); + this.gpxHandle.Location = new System.Drawing.Point(330, 3); + this.gpxHandle.Name = "gpxHandle"; + this.gpxHandle.Size = new System.Drawing.Size(737, 583); + this.gpxHandle.TabIndex = 21; + this.gpxHandle.TabStop = false; + this.gpxHandle.Text = "操作"; + // + // codePnl + // + this.codePnl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.codePnl.Controls.Add(this.txtCode); + this.codePnl.Location = new System.Drawing.Point(8000, 20); + this.codePnl.Name = "codePnl"; + this.codePnl.Size = new System.Drawing.Size(725, 557); + this.codePnl.TabIndex = 1; + // + // txtCode + // + this.txtCode.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtCode.Highlighting = "SQL"; + this.txtCode.Location = new System.Drawing.Point(13, 13); + this.txtCode.Name = "txtCode"; + this.txtCode.ShowSpaces = true; + this.txtCode.Size = new System.Drawing.Size(700, 530); + this.txtCode.TabIndex = 0; + this.txtCode.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; + // + // pizhuPnl + // + this.pizhuPnl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pizhuPnl.Controls.Add(this.gpCurrTable); + this.pizhuPnl.Controls.Add(this.GpColumns); + this.pizhuPnl.Location = new System.Drawing.Point(6, 20); + this.pizhuPnl.Name = "pizhuPnl"; + this.pizhuPnl.Size = new System.Drawing.Size(725, 557); + this.pizhuPnl.TabIndex = 0; + // + // gpCurrTable + // + this.gpCurrTable.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gpCurrTable.Controls.Add(this.labCurrTabComment); + this.gpCurrTable.Controls.Add(this.TxtCurrTabComment); + this.gpCurrTable.Controls.Add(this.LabCurrTabName); + this.gpCurrTable.Controls.Add(this.label3); + this.gpCurrTable.Location = new System.Drawing.Point(8, 13); + this.gpCurrTable.Margin = new System.Windows.Forms.Padding(2); + this.gpCurrTable.Name = "gpCurrTable"; + this.gpCurrTable.Padding = new System.Windows.Forms.Padding(2); + this.gpCurrTable.Size = new System.Drawing.Size(705, 73); + this.gpCurrTable.TabIndex = 11; + this.gpCurrTable.TabStop = false; + this.gpCurrTable.Text = "表批注"; + // + // labCurrTabComment + // + this.labCurrTabComment.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom))); + this.labCurrTabComment.AutoSize = true; + this.labCurrTabComment.Location = new System.Drawing.Point(199, 42); + this.labCurrTabComment.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labCurrTabComment.Name = "labCurrTabComment"; + this.labCurrTabComment.Size = new System.Drawing.Size(53, 12); + this.labCurrTabComment.TabIndex = 7; + this.labCurrTabComment.Text = "表批注:"; + // + // TxtCurrTabComment + // + this.TxtCurrTabComment.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom))); + this.TxtCurrTabComment.Location = new System.Drawing.Point(254, 39); + this.TxtCurrTabComment.Margin = new System.Windows.Forms.Padding(2); + this.TxtCurrTabComment.Name = "TxtCurrTabComment"; + this.TxtCurrTabComment.Size = new System.Drawing.Size(300, 21); + this.TxtCurrTabComment.TabIndex = 3; + // + // LabCurrTabName + // + this.LabCurrTabName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom))); + this.LabCurrTabName.AutoSize = true; + this.LabCurrTabName.Location = new System.Drawing.Point(259, 17); + this.LabCurrTabName.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.LabCurrTabName.Name = "LabCurrTabName"; + this.LabCurrTabName.Size = new System.Drawing.Size(0, 12); + this.LabCurrTabName.TabIndex = 5; + this.LabCurrTabName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(212, 17); + this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(41, 12); + this.label3.TabIndex = 6; + this.label3.Text = "表名:"; + // + // GpColumns + // + this.GpColumns.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GpColumns.Controls.Add(this.lblTip); + this.GpColumns.Controls.Add(this.GV_ColComments); + this.GpColumns.Controls.Add(this.BtnSaveGridData); + this.GpColumns.Location = new System.Drawing.Point(8, 90); + this.GpColumns.Margin = new System.Windows.Forms.Padding(2); + this.GpColumns.Name = "GpColumns"; + this.GpColumns.Padding = new System.Windows.Forms.Padding(2); + this.GpColumns.Size = new System.Drawing.Size(705, 453); + this.GpColumns.TabIndex = 10; + this.GpColumns.TabStop = false; + this.GpColumns.Text = "列批注"; + // + // lblTip + // + this.lblTip.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblTip.AutoSize = true; + this.lblTip.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblTip.Location = new System.Drawing.Point(22, 411); + this.lblTip.Name = "lblTip"; + this.lblTip.Size = new System.Drawing.Size(0, 21); + this.lblTip.TabIndex = 9; + // + // GV_ColComments + // + this.GV_ColComments.AllowUserToAddRows = false; + this.GV_ColComments.AllowUserToDeleteRows = false; + this.GV_ColComments.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GV_ColComments.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.DisplayedCells; + this.GV_ColComments.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.Raised; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle1.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.GV_ColComments.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; + this.GV_ColComments.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.GV_ColComments.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.ColXuHao, + this.ColName, + this.ColDataType, + this.ColLength, + this.ColComment}); + this.GV_ColComments.Location = new System.Drawing.Point(4, 18); + this.GV_ColComments.Margin = new System.Windows.Forms.Padding(2); + this.GV_ColComments.MultiSelect = false; + this.GV_ColComments.Name = "GV_ColComments"; + this.GV_ColComments.RowTemplate.Height = 27; + this.GV_ColComments.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.GV_ColComments.Size = new System.Drawing.Size(697, 363); + this.GV_ColComments.TabIndex = 0; + this.GV_ColComments.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.GV_ColComments_CellClick); + this.GV_ColComments.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.GV_ColComments_PreviewKeyDown); + // + // ColXuHao + // + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.ColXuHao.DefaultCellStyle = dataGridViewCellStyle2; + this.ColXuHao.FillWeight = 10F; + this.ColXuHao.HeaderText = "序号"; + this.ColXuHao.MinimumWidth = 30; + this.ColXuHao.Name = "ColXuHao"; + this.ColXuHao.ReadOnly = true; + this.ColXuHao.Width = 54; + // + // ColName + // + dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.ColName.DefaultCellStyle = dataGridViewCellStyle3; + this.ColName.FillWeight = 20F; + this.ColName.HeaderText = "列名"; + this.ColName.MinimumWidth = 150; + this.ColName.Name = "ColName"; + this.ColName.ReadOnly = true; + this.ColName.Width = 150; + // + // ColDataType + // + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.ColDataType.DefaultCellStyle = dataGridViewCellStyle4; + this.ColDataType.FillWeight = 23F; + this.ColDataType.HeaderText = "数据类型"; + this.ColDataType.MinimumWidth = 110; + this.ColDataType.Name = "ColDataType"; + this.ColDataType.ReadOnly = true; + this.ColDataType.Width = 110; + // + // ColLength + // + dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.ColLength.DefaultCellStyle = dataGridViewCellStyle5; + this.ColLength.FillWeight = 12F; + this.ColLength.HeaderText = "长度"; + this.ColLength.MinimumWidth = 30; + this.ColLength.Name = "ColLength"; + this.ColLength.ReadOnly = true; + this.ColLength.Width = 54; + // + // ColComment + // + this.ColComment.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.ColComment.DefaultCellStyle = dataGridViewCellStyle6; + this.ColComment.FillWeight = 35F; + this.ColComment.HeaderText = "列批注"; + this.ColComment.MinimumWidth = 320; + this.ColComment.Name = "ColComment"; + this.ColComment.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + // + // BtnSaveGridData + // + this.BtnSaveGridData.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BtnSaveGridData.Location = new System.Drawing.Point(593, 400); + this.BtnSaveGridData.Margin = new System.Windows.Forms.Padding(2); + this.BtnSaveGridData.Name = "BtnSaveGridData"; + this.BtnSaveGridData.Size = new System.Drawing.Size(98, 34); + this.BtnSaveGridData.TabIndex = 8; + this.BtnSaveGridData.Values.Text = "保存"; + this.BtnSaveGridData.Click += new System.EventHandler(this.BtnSaveGridData_Click); + // + // RibbonTabContainer + // + this.RibbonTabContainer.Controls.Add(this.tpfile); + this.RibbonTabContainer.Controls.Add(this.tptool); + this.RibbonTabContainer.Controls.Add(this.apabout); + this.RibbonTabContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.RibbonTabContainer.ItemSize = new System.Drawing.Size(65, 20); + this.RibbonTabContainer.Location = new System.Drawing.Point(3, 2); + this.RibbonTabContainer.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.RibbonTabContainer.Name = "RibbonTabContainer"; + this.RibbonTabContainer.SelectedIndex = 0; + this.RibbonTabContainer.Size = new System.Drawing.Size(1070, 101); + this.RibbonTabContainer.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.RibbonTabContainer.TabIndex = 3; + this.RibbonTabContainer.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.RibbonTabContainer_Selecting); + this.RibbonTabContainer.Selected += new System.Windows.Forms.TabControlEventHandler(this.RibbonTabContainer_Selected); + this.RibbonTabContainer.MouseClick += new System.Windows.Forms.MouseEventHandler(this.RibbonTabContainer_MouseClick); + this.RibbonTabContainer.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.RibbonTabContainer_MouseDoubleClick); + // + // tpfile + // + this.tpfile.Controls.Add(this.ribbonPageFile); + this.tpfile.Location = new System.Drawing.Point(4, 24); + this.tpfile.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.tpfile.Name = "tpfile"; + this.tpfile.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.tpfile.Size = new System.Drawing.Size(1062, 73); + this.tpfile.TabIndex = 0; + this.tpfile.Text = "文件"; + this.tpfile.ToolTipText = "文件"; + // + // ribbonPageFile + // + this.ribbonPageFile.BackColor = System.Drawing.SystemColors.Control; + this.ribbonPageFile.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + this.ribbonPageFile.Dock = System.Windows.Forms.DockStyle.Fill; + this.ribbonPageFile.Font = new System.Drawing.Font("Segoe UI", 9F); + this.ribbonPageFile.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.ribbonPageFile.ImageScalingSize = new System.Drawing.Size(48, 48); + this.ribbonPageFile.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsbConnect, + this.tsbRefresh, + this.tsbBuild, + this.tsWordExp, + this.tsExcelExp, + this.tsPdfExp, + this.tsHtmlExp, + this.tsXmlExp, + this.tsMarkDownExp}); + this.ribbonPageFile.Location = new System.Drawing.Point(3, 2); + this.ribbonPageFile.Name = "ribbonPageFile"; + this.ribbonPageFile.Size = new System.Drawing.Size(1056, 69); + this.ribbonPageFile.TabIndex = 0; + this.ribbonPageFile.Text = "toolStripFile"; + this.ribbonPageFile.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.ribbonPageFile_ItemClicked); + // + // tsbConnect + // + this.tsbConnect.Image = ((System.Drawing.Image)(resources.GetObject("tsbConnect.Image"))); + this.tsbConnect.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsbConnect.Name = "tsbConnect"; + this.tsbConnect.Size = new System.Drawing.Size(63, 66); + this.tsbConnect.Text = "数据连接"; + this.tsbConnect.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsbConnect.Click += new System.EventHandler(this.tsbConnect_Click); + // + // tsbRefresh + // + this.tsbRefresh.Image = ((System.Drawing.Image)(resources.GetObject("tsbRefresh.Image"))); + this.tsbRefresh.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsbRefresh.Name = "tsbRefresh"; + this.tsbRefresh.Size = new System.Drawing.Size(63, 66); + this.tsbRefresh.Text = "重新获取"; + this.tsbRefresh.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsbRefresh.Click += new System.EventHandler(this.tsbRefresh_Click); + // + // tsbBuild + // + this.tsbBuild.Image = ((System.Drawing.Image)(resources.GetObject("tsbBuild.Image"))); + this.tsbBuild.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsbBuild.Name = "tsbBuild"; + this.tsbBuild.Size = new System.Drawing.Size(65, 66); + this.tsbBuild.Tag = "chm"; + this.tsbBuild.Text = "CHM导出"; + this.tsbBuild.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsbBuild.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsWordExp + // + this.tsWordExp.Image = ((System.Drawing.Image)(resources.GetObject("tsWordExp.Image"))); + this.tsWordExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsWordExp.Name = "tsWordExp"; + this.tsWordExp.Size = new System.Drawing.Size(66, 66); + this.tsWordExp.Tag = "word"; + this.tsWordExp.Text = "Word导出"; + this.tsWordExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsWordExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsExcelExp + // + this.tsExcelExp.Image = ((System.Drawing.Image)(resources.GetObject("tsExcelExp.Image"))); + this.tsExcelExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsExcelExp.Name = "tsExcelExp"; + this.tsExcelExp.Size = new System.Drawing.Size(63, 66); + this.tsExcelExp.Tag = "excel"; + this.tsExcelExp.Text = "Excel导出"; + this.tsExcelExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsExcelExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsPdfExp + // + this.tsPdfExp.Image = ((System.Drawing.Image)(resources.GetObject("tsPdfExp.Image"))); + this.tsPdfExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsPdfExp.Name = "tsPdfExp"; + this.tsPdfExp.Size = new System.Drawing.Size(58, 66); + this.tsPdfExp.Tag = "pdf"; + this.tsPdfExp.Text = "PDF导出"; + this.tsPdfExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsPdfExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsHtmlExp + // + this.tsHtmlExp.Image = ((System.Drawing.Image)(resources.GetObject("tsHtmlExp.Image"))); + this.tsHtmlExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsHtmlExp.Name = "tsHtmlExp"; + this.tsHtmlExp.Size = new System.Drawing.Size(64, 66); + this.tsHtmlExp.Tag = "html"; + this.tsHtmlExp.Text = "Html导出"; + this.tsHtmlExp.TextDirection = System.Windows.Forms.ToolStripTextDirection.Horizontal; + this.tsHtmlExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsHtmlExp.ToolTipText = "Html导出"; + this.tsHtmlExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsXmlExp + // + this.tsXmlExp.Image = ((System.Drawing.Image)(resources.GetObject("tsXmlExp.Image"))); + this.tsXmlExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsXmlExp.Name = "tsXmlExp"; + this.tsXmlExp.Size = new System.Drawing.Size(61, 66); + this.tsXmlExp.Tag = "xml"; + this.tsXmlExp.Text = "XML导出"; + this.tsXmlExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsXmlExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tsMarkDownExp + // + this.tsMarkDownExp.Image = ((System.Drawing.Image)(resources.GetObject("tsMarkDownExp.Image"))); + this.tsMarkDownExp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tsMarkDownExp.Name = "tsMarkDownExp"; + this.tsMarkDownExp.Size = new System.Drawing.Size(56, 66); + this.tsMarkDownExp.Tag = "markdown"; + this.tsMarkDownExp.Text = "MD导出"; + this.tsMarkDownExp.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.tsMarkDownExp.Click += new System.EventHandler(this.tsbBuild_Click); + // + // tptool + // + this.tptool.Controls.Add(this.tsbPDMUpload); + this.tptool.Location = new System.Drawing.Point(4, 24); + this.tptool.Name = "tptool"; + this.tptool.Size = new System.Drawing.Size(1062, 73); + this.tptool.TabIndex = 3; + this.tptool.Text = "工具"; + this.tptool.UseVisualStyleBackColor = true; + // + // tsbPDMUpload + // + this.tsbPDMUpload.BackColor = System.Drawing.SystemColors.Control; + this.tsbPDMUpload.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + this.tsbPDMUpload.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsbPDMUpload.Font = new System.Drawing.Font("Segoe UI", 9F); + this.tsbPDMUpload.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsbPDMUpload.ImageScalingSize = new System.Drawing.Size(48, 48); + this.tsbPDMUpload.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButton3}); + this.tsbPDMUpload.Location = new System.Drawing.Point(0, 0); + this.tsbPDMUpload.Name = "tsbPDMUpload"; + this.tsbPDMUpload.Size = new System.Drawing.Size(1062, 73); + this.tsbPDMUpload.TabIndex = 1; + this.tsbPDMUpload.Text = "toolStripFile"; + this.tsbPDMUpload.Click += new System.EventHandler(this.tsbSaveUpload_Click); + // + // toolStripButton3 + // + this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image"))); + this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton3.Name = "toolStripButton3"; + this.toolStripButton3.Size = new System.Drawing.Size(63, 70); + this.toolStripButton3.Text = "批注上载"; + this.toolStripButton3.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + // + // apabout + // + this.apabout.BackColor = System.Drawing.SystemColors.Control; + this.apabout.Controls.Add(this.ribbonPageAbout); + this.apabout.Location = new System.Drawing.Point(4, 24); + this.apabout.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.apabout.Name = "apabout"; + this.apabout.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.apabout.Size = new System.Drawing.Size(1062, 73); + this.apabout.TabIndex = 2; + this.apabout.Text = "关于"; + this.apabout.ToolTipText = "关于"; + // + // ribbonPageAbout + // + this.ribbonPageAbout.BackColor = System.Drawing.SystemColors.Control; + this.ribbonPageAbout.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + this.ribbonPageAbout.Dock = System.Windows.Forms.DockStyle.Fill; + this.ribbonPageAbout.Font = new System.Drawing.Font("Segoe UI", 9F); + this.ribbonPageAbout.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.ribbonPageAbout.ImageScalingSize = new System.Drawing.Size(48, 48); + this.ribbonPageAbout.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButtonAbout}); + this.ribbonPageAbout.Location = new System.Drawing.Point(3, 2); + this.ribbonPageAbout.Name = "ribbonPageAbout"; + this.ribbonPageAbout.Size = new System.Drawing.Size(1056, 69); + this.ribbonPageAbout.TabIndex = 0; + this.ribbonPageAbout.Text = "toolStripAbout"; + // + // toolStripButtonAbout + // + this.toolStripButtonAbout.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonAbout.Image"))); + this.toolStripButtonAbout.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButtonAbout.Name = "toolStripButtonAbout"; + this.toolStripButtonAbout.Size = new System.Drawing.Size(52, 66); + this.toolStripButtonAbout.Text = "About"; + this.toolStripButtonAbout.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; + this.toolStripButtonAbout.Click += new System.EventHandler(this.toolStripButtonAbout_Click); + // + // miniToolStrip + // + this.miniToolStrip.AccessibleName = "新项选择"; + this.miniToolStrip.AccessibleRole = System.Windows.Forms.AccessibleRole.ButtonDropDown; + this.miniToolStrip.AutoSize = false; + this.miniToolStrip.BackColor = System.Drawing.SystemColors.Control; + this.miniToolStrip.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + this.miniToolStrip.CanOverflow = false; + this.miniToolStrip.Dock = System.Windows.Forms.DockStyle.None; + this.miniToolStrip.Font = new System.Drawing.Font("Segoe UI", 9F); + this.miniToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.miniToolStrip.ImageScalingSize = new System.Drawing.Size(48, 48); + this.miniToolStrip.Location = new System.Drawing.Point(0, 0); + this.miniToolStrip.Name = "miniToolStrip"; + this.miniToolStrip.Size = new System.Drawing.Size(894, 70); + this.miniToolStrip.TabIndex = 0; + // + // treeDB + // + this.treeDB.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.treeDB.CheckBoxes = true; + this.treeDB.HideSelection = false; + this.treeDB.ImageIndex = 0; + this.treeDB.ImageList = this.imgList; + this.treeDB.Indent = 16; + this.treeDB.Location = new System.Drawing.Point(0, 57); + this.treeDB.Name = "treeDB"; + this.treeDB.SelectedImageIndex = 0; + this.treeDB.Size = new System.Drawing.Size(321, 511); + this.treeDB.TabIndex = 18; + this.treeDB.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeDB_AfterCheck); + this.treeDB.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeDB_AfterSelect); + // + // MainForm + // + this.AcceptButton = this.BtnSaveGridData; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1076, 700); + this.Controls.Add(this.tabLayoutPnl); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "DBCHM"; + this.Load += new System.EventHandler(this.MainForm_Load); + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.tabLayoutPnl.ResumeLayout(false); + this.mainPnl.ResumeLayout(false); + this.treePnl.ResumeLayout(false); + this.treePnl.PerformLayout(); + this.gpxHandle.ResumeLayout(false); + this.codePnl.ResumeLayout(false); + this.pizhuPnl.ResumeLayout(false); + this.gpCurrTable.ResumeLayout(false); + this.gpCurrTable.PerformLayout(); + this.GpColumns.ResumeLayout(false); + this.GpColumns.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.GV_ColComments)).EndInit(); + this.RibbonTabContainer.ResumeLayout(false); + this.tpfile.ResumeLayout(false); + this.tpfile.PerformLayout(); + this.ribbonPageFile.ResumeLayout(false); + this.ribbonPageFile.PerformLayout(); + this.tptool.ResumeLayout(false); + this.tptool.PerformLayout(); + this.tsbPDMUpload.ResumeLayout(false); + this.tsbPDMUpload.PerformLayout(); + this.apabout.ResumeLayout(false); + this.apabout.PerformLayout(); + this.ribbonPageAbout.ResumeLayout(false); + this.ribbonPageAbout.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.ToolTip tlTip; + private System.Windows.Forms.ImageList imgList; + private System.Windows.Forms.TableLayoutPanel tabLayoutPnl; + private System.Windows.Forms.TabControl RibbonTabContainer; + private System.Windows.Forms.TabPage tpfile; + private System.Windows.Forms.ToolStrip ribbonPageFile; + private System.Windows.Forms.ToolStripButton tsbConnect; + private System.Windows.Forms.ToolStripButton tsbRefresh; + private System.Windows.Forms.ToolStripButton tsbBuild; + private System.Windows.Forms.ToolStripButton tsWordExp; + private System.Windows.Forms.ToolStripButton tsExcelExp; + private System.Windows.Forms.ToolStripButton tsPdfExp; + private System.Windows.Forms.ToolStripButton tsHtmlExp; + private System.Windows.Forms.ToolStripButton tsXmlExp; + private System.Windows.Forms.ToolStripButton tsMarkDownExp; + private System.Windows.Forms.TabPage tptool; + private System.Windows.Forms.TabPage apabout; + private System.Windows.Forms.ToolStrip ribbonPageAbout; + private System.Windows.Forms.ToolStripButton toolStripButtonAbout; + private System.Windows.Forms.Panel mainPnl; + private System.Windows.Forms.Label label1; + private TreeViewEnhanced treeDB; + private System.Windows.Forms.CheckBox CkReverse; + private System.Windows.Forms.Label lblSelectRes; + private System.Windows.Forms.TextBox TxtSearchWords; + private System.Windows.Forms.CheckBox CkAll; + private System.Windows.Forms.ToolStrip miniToolStrip; + private System.Windows.Forms.Label lblTongJi; + private System.Windows.Forms.Panel treePnl; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.GroupBox gpxHandle; + private System.Windows.Forms.Panel pizhuPnl; + private System.Windows.Forms.GroupBox gpCurrTable; + private System.Windows.Forms.Label labCurrTabComment; + private System.Windows.Forms.TextBox TxtCurrTabComment; + private System.Windows.Forms.Label LabCurrTabName; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.GroupBox GpColumns; + private System.Windows.Forms.Label lblTip; + private System.Windows.Forms.DataGridView GV_ColComments; + private ComponentFactory.Krypton.Toolkit.KryptonButton BtnSaveGridData; + private System.Windows.Forms.Panel codePnl; + private ICSharpCode.TextEditor.TextEditorControl txtCode; + private System.Windows.Forms.ToolStrip tsbPDMUpload; + private System.Windows.Forms.ToolStripButton toolStripButton3; + private System.Windows.Forms.DataGridViewTextBoxColumn ColXuHao; + private System.Windows.Forms.DataGridViewTextBoxColumn ColName; + private System.Windows.Forms.DataGridViewTextBoxColumn ColDataType; + private System.Windows.Forms.DataGridViewTextBoxColumn ColLength; + private System.Windows.Forms.DataGridViewTextBoxColumn ColComment; + } +} \ No newline at end of file diff --git a/DBChm/MainForm.resx b/DBChm/MainForm.resx new file mode 100644 index 0000000..7fae45d --- /dev/null +++ b/DBChm/MainForm.resx @@ -0,0 +1,944 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 544, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACU + DAAAAk1TRnQBSQFMAgEBBQEAAUABBAFAAQQBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABQAMAASADAAEBAQABCAYAAQgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/wEAEKYwAAGzAeIC4Qfc + BNsBpjAAAbMB4gEZAeEG3AXbAaYwAAGzAeIBkgESB9wE2wGmMAABswHiAdwBkgFtAdwD4QPcA9sBpjAA + AbMC4gEZAdwBEgHcAYsBbAHcAWwBiwHbAtwBpjAAAbMD4gHcARIBGQHcARQBEQETAdwB2wLcAaYwAAGz + A+IB3AESARkB4gHcAREBkQTcAaYwAAGzAfQC4gHcARUB4QHiAdwBEAGLAtsC3AGmMAABswH0AuIBtQFD + AuoB7AEQAdwBiwHsAtwBpjAAAbMB/wLiARkB7AHqA+ICGQHhAtwBpjAAAbMB/wH0AuIB3AHqAhkB4gIZ + AeEC3AGmMAABswH/AvQB4gEZAuwBbQHcAeICGQLhAaYwAAGzAf8D9AHiA9wD4gIZAeEBpjAAAbMF/wH0 + COIBpjAAELNSAAH/CfMB/zUAAQcB7Af3AesB8BQAAfQG8wbyAfMB/wIAAbUKZgFsBAABBwHyAfQB8wH0 + AfMB9ALzAvcD8wIAA60BiwWGAmYBhgMAAbwBHAFzAW4BSwJKAUQEFQFDARUB8wIAAfcB/wLxAfADvAMH + AWwEAAEHAfMBBwHvAfIB9AG8Ae0BBwHtAa4BuwEHAe8CAAGtAvQC8wEZAfECGQHdAQkBhgMAAbwBmgGg + AXoCWQFYBVIBSwEVAfMCAAG1BP8F9AEHAWwEAAEHAfMB8gEHAfIB/wG8Ae8B9AHtAZEB9AEZAe8CAAGt + Af8HiwIJAYYDAAG8AZkBoAGaBHoBWQNTAVIBSgH0AgAB7wH/AbwBBwH/ArsB9AK1AQkBbAQAAQcB8wH0 + Av8B8wP/Ae0BkQH0ARkBtQIAAa0B/wL0AvMBGQHzARkB3QEJAYYDAAG8ARoBoAKaA3oCWQJTATEBSgH0 + AgABuwb/A/QBvAFsBAABBwHzAfQB/wH0AfMC/wH0Ae0BkQH0ARkBtQIAAa0B/wKLAfQCiwIZAt0BhgMA + AbwBGgHDAaACmgN6AlkBUwExAUoB9AIAAbsB/wK8Af8CBwH/AQcBuwHwAWwEAAEHAfMB8gHvAfEB/wG8 + AfcB/wHtAZEB9AHzAbUCAAGtAv8C9ALzAhkB3QEZAYYDAAG8ARoBwwKgApoDegFZAVMBMQFuAfQCAAG7 + Cf8B8QFsBAABBwHzAQcBvAH0Af8B8wH3AQcB9wGRAfQB8wHvAgAB1AT/AfQC8wEZAfEBGQGGAwABvAEa + AsMDoAGaA3oBWQFSAXMB9AIAAbsB/wK8Af8CvAH/AgcB8wFsBAABBwHwAbwB9AHzAfIB9AHyAfAB7AHt + Af8BGQHvAgAB1QH/AosB9AH/AfQBGQHzAhkBiwMAAbwBGgHDAhoEoAGaA3oBcwH0AgABuwr/AWwEAAG8 + COwBbQEHAf8B8wHvAgAB1QH2Bf8B9AHzAhkBiwMAAfAEmQMcBZkBvAH/AgAB3APWAdUCtAWtBAAB/wHz + AbwB8QbwAfMC9AG7AgABtALVAc8GrQGLAa0DAAHwAZkDGgGZAe8B8wH/BPQB/wMAAdwDCQLcAtYD1QGt + BQAB/wQZAd0DCQTcAgABtAMJAdwD1gPVAa0DAAH/ArwBBwK8AfQKAAHcBNYB1QS0Aq0FAAH/AxkB3QMJ + BdwCAAG1AdUDtALOBa0EAAX/HAAB/wbzBhlQAAFCAU0BPgcAAT4DAAEoAwABQAMAASADAAEBAQABAQYA + AQEWAAP/gQAE/wHAAQcG/wHAAQcC/wGAAQABwAEDAcABAAHAAQMBgAEAAcABAwHAAQABwAEDAYABAAHA + AQMBwAEAAcABAwGAAQABwAEDAcABAAHAAQMBgAEAAcABAwHAAQABwAEDAYABAAHAAQMBwAEAAcABAwGA + AQABwAEDAcABAAHAAQMBgAEAAcABAwHAAQABwAEDAYABAAHAAQMBwAEAAcABAwGAAQABwAEDAcABAAHA + AQMBgAEBAcABAwHgAQABwAEDAYAB/wHAAQMB4AEAAcABAwHBA/8B4AEACv8L + + + + 466, 17 + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + 169, 17 + + + + + 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 + HhM4NfU38485g/fFut8AAAAASUVORK5CYII= + + + + + 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 + + + + 769, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAMPSURBVHhe7ZxPq01RGMaPf8WUsZCQKEL5VwwNfAhjkvKn + +Axi4hsw4guYiRBXDAgpGRkxY6CUAc9zr12rfd93v2ufs9faq73ep3517r537XP6tZ619733nDXzeLJk + LdgINk2IdWChrAF7wQVwDzwGTybEfXAZHAS9ZdHwDfAV/J0438FtsAVEhXJugT9AOuFUeQBMSawVZ05t + cho4MTYANVxzaqiVxg9wCqjhgiwNrImbQAwv5bxaSYNq4hHgLc2q8CAv5dKgmuAtAC9Uq8KD/KY0qCZc + kIELMnBBBi7IoGhBH8Gb1rHcFCvoPTgB9oGX/4+NQZGCXoMDoMluwBs26WdTU5wgzpxQTpOd4BmQxqSk + KEGUcxJoYd2WgDQ2FcUIatdKS+66FSFIq5WWnHUbXZBVKy256jaqoNhaaclRt9EE9a2VltR1G0XQvLXS + krJu2QXxV4chZk47qeqWVdBQtdKSom7ZBA1RK/4bysrQdcsiKLZWnAFHVx6KOQ2OrDzszJB1Sy4otlY7 + wFNwbfkrORcBz3d4+avuDFW3pILegphabQUPAcdc5QEl50Fz3kM8YGQ/eAHC19SXZIJ+g3PACmdOI4dc + AVoaQSRW0lnwEzTj+pJ0BvEvgl0ziHLazxEriLwDXXXbA56DcExfkgoiH4C08Ia1CukjiGgzaRcY4vUn + F0Qo6Rho0q5VSF9BpC2JM2eo155FEPkEjoPtoOu88wgirBsl8RK/6MIckk0Q4ZpkrQnzCiK8BXjVOrYo + WQXFsIigFLggAxdk4IIMXJCBCzJwQQYuyMAFGbggAxdk4IIMXJCBCzIoTtB1wI8g8flD1oNLQBqTkuIE + fQHSxz157DOQxqSEz12UoNJwQQYuyMAFGbggAxdk4IIMVEHcnoHbNUiDaoL3X+KHehnuZSENqok7QA3f + +PQNSANr4Bc4A9SwZtyeQRpcA3eBuP6E2QxqXIv4HsdtICrcBYUziXtZSCebEqwVZ060nCb8cwM3+uBe + FrTLy9+U4NWKCzLXHLNWVqa2PRdRL+Uez1CZzf4B3nCy+2JKK9UAAAAASUVORK5CYII= + + + + 309, 17 + + + + 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 + RK5CYII= + + + + 638, 17 + + + 66 + + + + AAABAAEASEgAAAEAIACIVAAAFgAAACgAAABIAAAAkAAAAAEAIAAAAAAAAFEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8vLwA+LS4ASkVBAEtL + RABLS0YAS0tEAElHQgBAQD8APz8/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAE1KQwBMSUMAU1BLAFtYUgBdWlQAXVxVAGBeWABramMADggHAyotJQYpKykFKiwlBiEf + HQSAfXIAYF5ZAF9cVgBcWlQAWVdRAFZUTgBNTEYAQjc6AEc/PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0xJAAAAAABQSUEAQ0E+AFVS + TACEhYEAREE9CFVRTBVaV1A1WllSVl5cVm9iX1mCZmNdlGhmX5tpZ2CbaGZfm2VjXJhhX1mFX1xXdVxa + VF1ZVlFAVFFLHkpKQwwABAABW1pRAEtJQgAbJBgARUU5AElJPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBPzwAODY1AFFPSQCFgnYAREM/DVZTTjlcWlRwYV9YqGdl + X9RvbGXodXJr9Hl2b/59enL/hIB4/4eEfP+JhX3/iIR9/4WBev9/fHT/e3dw/3d0bfdyb2jsamdh3WRh + W7heXFWDWFVPSktKQxUAAAABWVZTAEhFQwBPS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAT01HAExKRABYVk8AAAAAAk9NSCZbWVNwYmBavmxpYul0cWr/fnpy/4eDe/+Pi4L/lZCI/5qV + jP+dmZD/oZyT/6Self+ln5f/paCX/6Oflf+gm5L/nZiP/5qVjP+UkIj/jomB/4WBev96d3D/cG1m8mVj + XcxbWlOJU1BLOz07OwhcWlQATk1GAGdmWQAvLy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSAAAAAAAU1FMAP// + /wBQTkgsWFZQimJgWdlua2T+e3dw/4eDe/+RjIT/mJOL/56ZkP+inZT/pJ+X/6ehmP+po5r/qqSb/6ul + nP+rpZz/rKad/6ymnf+rpZz/qqSb/6mkm/+oopn/pZ+W/6Cbkv+alYz/kIyE/4SAef92c2z/aGVf6l1b + VaVTUUtBQ0M7BldVTgBIRj8AT0xHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkI/ADwxLQBTUEwAT05IE1lXUW9fXVfaamdh/3l1 + bv+Ggnv/kY2F/5mUi/+dmI//oJuS/6KdlP+kn5b/pqGY/6eimf+po5r/qqSb/6ulnP+rpZz/q6Wc/6ym + nf+rpZz/q6Wc/6ulnP+qpJv/qaOa/6eimf+loJf/op2T/5yXj/+Tj4b/hoJ6/3Vya/9kYlzsV1VPk09O + SCZbXFsAS0dCAExHQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABFRT8ARUI9AG1xaQBRTUkvWVdSrWNgWvpxbmf/gHx1/4yIf/+Uj4f/mZOL/5yW + jv+emZD/oJuS/6KdlP+kn5X/paCX/6ahmP+moJf/pqCX/6Wflv+inZT/op2U/6Wflv+nopj/qKKZ/6mj + mv+qpJv/qKOa/6eimf+loJf/pJ+V/6Kdk/+empH/mZSL/46Kgv9/e3T/a2hi/1pYUs5NTEZTQjs3BEhH + QQBHR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVF + PwBHRkAAoJKOAFJQSUdYVlDPZGFb/3Zya/+EgHj/jYmA/5KOhf+VkYn/mZOL/5uWjf+dmI//npmQ/5yX + jv+Yk4r/ko2E/4qFff+BfHT/fHhw/3Vxaf9taWL/a2hg/3JuZv94dGz/e3hv/4aBef+Qi4P/mZSM/5+a + kf+inZT/o56U/6Gdk/+fm5H/nZiP/5qUjP+SjYX/hYF5/3BtZv9bWVPtTkxIcjw9OQZCQj0AOjo1AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk1IAEdHQQCdjYwATUxGS1RS + TN5iX1n/dnJr/4R/eP+Lh37/j4qC/5GNhP+UkIj/l5GJ/5aQiP+RjIP/iYR8/4B7c/92cWn/bmli/2hk + Xf9iXlf/XVlT/11ZU/9dWVP/WlZR/1VRTP9UUUv/V1RN/1tXUf9hXVb/bGdg/3dya/+GgXj/k46F/5qV + jP+dmI//nJeO/5qVjf+Xkor/kY2F/4aCev9zcGj/W1lU80lIQ3MtLigGOzo2AD48OgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Pz8ATktFAE1KRwBMSkU8T01I3F5bVv9yb2j/gHx1/4eC + e/+Lhn7/jomB/5CLg/+Qi4P/jId+/4WAd/9/eXH/fHdv/355cf+Dfnb/iIN7/4+Kgv+RjYX/ko2G/5iT + i/+dl4//m5aO/5GMhf+Mh4D/i4Z//4SAeP99eXH/dnJr/3BsZf9vamP/dXBp/4B7c/+MiH//lpCI/5iS + iv+WkYn/k4+H/4+Kgv+EgHj/cW1n/1dVT/NEQj5wMjAvBD8+OwBGRkQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADGu4cATkxFAEpIQxtLSkW/V1VP/21pY/97d3D/gn52/4WBev+JhHz/i4V9/4uF + fP+Jg3r/iYN6/4+JgP+ZlIz/paCX/6+qov+1saj/uLOr/7u2rv+8t67/vLiv/724r/+9uK//vLev/7u2 + rv+6taz/ubOr/7axqP+yraX/raif/6Sfl/+YlIz/kIuD/4yHf/+Lh3//jYiA/5CLgv+SjYX/kY2E/4+L + gv+Lhn7/f3t0/2lmYP9OTEfqQkE9TT87NgBKS0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABISEQAT1JRAkZEQH1OTEb9ZGFb/3Vxa/98eXH/gHx1/4N/d/+Ef3f/hoB4/4+JgP+dmI//r6qj/765 + sf/Gwrr/ycS9/8jDvP/Fwbn/wr61/8K9tP/CvbT/wb20/8G9tP/BvLP/wLyz/8C7sv+/u7L/vrqx/765 + sP++ubD/vbiv/7y3rv+6taz/tbCo/7Cro/+sp5//pKCa/6Kdl/+Yk4v/kIuD/42IgP+Lhn7/hYF5/3dz + bP9dW1X/R0ZBx0ZFQx9FREEAWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBGRT8ARUQ+KEdF + QNpYVVD/bWlj/3dzbP97d3D/fXly/396cv+KhX3/pKCY/7+7tP/OysX/0s7I/8/Lw//Lx8D/ycS9/8fD + u//Fwbn/xcC4/8S/t//AvLP/u7au/7q2rv+6tq7/ubWt/7m1rP+7tq7/wLyz/8C8s//AvLP/wLyz/8C7 + sv+/urH/vrmw/724sP+9uK//l5ON/7Cspv++u7b/r6ul/5eSi/+JhHz/hYF5/356c/9raGH/UE5J/ENC + P3UAAAAAXl5eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtHQwBAQDoAREI9cUtIQ/1hXlj/cGxm/3Vx + a/94dG3/gX11/52Ykf/Bvbf/19PO/9fUzv/Szsf/zcjB/8rGvv/FwLn/ubWu/6+ro/+empP/kY2H/4aD + fP+Cf3j/gn54/4SAef+EgXr/hIB5/4J/eP+AfXb/gn95/4uIgf+Xk4z/pqKa/7Gtpf+6tq3/wbyz/8C8 + s//AvLL/rquk/52Ykv/Fwrr/z83I/8bDv/+gnZf/hoF6/397c/9zb2j/WldS/0NBPchAQD8bQkJBAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBPTkkMQ0E9rFBOSf9mY13/cG1n/3ZybP+MiIL/uLaw/9rX + 0//d29b/1NHK/9DKw//Iw7z/ubSt/52ak/+Jhn//endx/3Rxav94dW7/fXpz/4F9dv+Ggnr/jIiA/5CL + g/+RjYT/kYyE/4+Lg/+Khn7/hIB4/4B8df97d3D/dHFr/3Zzbf+Bfnf/kY6H/6umnv+7tq7/yMS9/5qV + kf+8tq7/xsK7/9nX0v/X1dL/p6Oe/4F9dv92cmv/Yl9Z/0ZEQO87OjhFNDIvAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEZEQABGREAdRUM/2FVSTv9oZV//c3Bq/5aTjv/Pzcr/5+Xi/93a1P/Tz8j/ycW9/62o + ov+Lh4H/dXJs/3Vxa/97eHH/hoN7/4+Lg/+Wkon/m5aO/5+bkf+inpT/paCX/6eimf+oo5r/qaOa/6ij + mv+moZj/o56V/6Cbkv+bl47/lZGI/46Jgf+Cf3f/eHVu/3Rxav9+e3T/qqaf/62ppP+qpZ3/v7qx/8nF + vv/i4Nz/4N/d/6eln/96dm//ZmNd/0lHQ/06OTdsNDMrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpI + RABJSEQkSkhE7FlWUf9raGP/mJaR/9va1//q5+T/3NnT/87Kw/+tqqP/hoN9/3FuZ/90cGr/gX52/42I + gP+WkYj/nJeO/6Cbkv+jnpX/paCX/6eimf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6qk + nP+po5r/pqGY/6Oelf+emZD/lpGJ/4uHf/98eHH/fntz/66rpf9tamT/Y2Ba/7ezq//NycP/6Ofk/+Xk + 4/+fnJf/aWZg/0xJRf85NzWDXVhLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5NSQBOTUklTkxI7F1b + Vv+Ni4b/2tjV/+3r6f/d2dP/vrqz/4eEfv9qZ2H/cW1m/397dP+MiH//lZCH/5qVjf+emZD/oJuS/6Kd + lP+kn5b/pqGX/6eimf+po5v/qqSb/6ulnP+rpZz/rKWc/6ymnf+spp3/q6Wc/6ulnP+qpJv/qaOa/6ei + mf+loJf/o56U/5+ZkP+Xkon/i4d//25rY/8wLyj/Dg0L/3Rxbf/Ev7j/1dLM//X08//X1dP/fHl0/0xK + Rf82NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBPTkolUE5L7HNwbf/KyMb/7+7r/9zZ + 0/+tqqT/cG1n/2RhW/91cmv/hYF5/4+Lg/+VkYj/mZSM/5yWjv+emZD/oJuS/6Kdk/+jnpX/pJ+V/6Oe + lf+hnJT/oZuS/52Xjv+blY3/mpWM/5yXjv+hnJP/o56U/6agmP+oopn/qKKZ/6eimP+loJf/pJ+V/6Kd + k/+fm5H/nJeO/3BtZf83Ni3/FRQQ/xsaGP+Cf3n/wLy1/+Xi3v/29vX/qaek/09NSf8yMS6aAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJRTABPTkolWFZS7KKgnv/q6Ob/4d3Z/6Sgmv9kYlv/ZmNd/3l1 + bv+Hgnv/joqB/5KOhf+VkIj/mZOL/5uVjf+cl47/m5aN/5eSif+QjIP/iIN7/396cv92cWr/cGxl/2hj + Xf9jX1j/YV1X/2NfWf9rZl//cGxl/3p2b/+FgXn/kIuD/5mUi/+fmpD/oZyT/6Gck/+fmpH/npmQ/4iD + fP9IRz3/JSQd/wcGBf8zMi//fnt1/8XBuv/08/H/1NPR/19dWf8wLiyZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFZVUQBQTkolbGpn7MvJxv/o5eL/op+a/1xaVP9kYVv/eHRt/4WBef+Lh3//joqC/5GN + hP+Uj4f/lZCI/5ONhf+Mh37/g311/3p1bf9xbWX/bWhh/2llXv9lYVr/ZGFa/2djXP9lYVr/Y19Z/2Fd + WP9bV1L/WlZR/11YUv9fW1X/ZmFb/29rY/98d2//ioV9/5aRiP+bl43/nJaO/5eRif9VU0r/NDMq/w4O + Cv8VFBP/UlBL/3t4cv/Z1tL/6+vp/3p5df8wLy2ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg + XABXVVEliIaD7d/d2v+0saz/W1lT/2BeWP90cWr/gX12/4eCe/+Khn7/jYmA/4+Kgv+OiYH/iYR7/4N9 + dP+AenL/gXxz/4aBef+NiYH/lZCJ/5yXj/+emZH/op2V/6eimv+nopr/p6Ka/6WgmP+dmJD/mZSM/5OP + h/+Khn7/gn12/3l1bf91cGj/dnJq/355cv+Ig3v/kYyD/5aRiP9qaGD/PTwy/x8eGP8FBAP/QDw3/1xa + VP+KiIT/5+bk/5eVkv81NTKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG9uagBiYV0nn56b7cnH + xP9mZF//WldR/3BtZv98eHH/gX52/4WBev+Ig3z/iYN7/4qEe/+KhHv/joiA/5iTi/+moZn/s66m/7u2 + rv++urL/vrqx/7+7sv/Au7L/wLuy/7+7sv+/urH/v7qx/765sP+9ubD/vbiv/7u2rv+5tKv/tbCo/6+q + of+nopn/nZiR/5aSi/+Tj4j/kIyE/5GMhP98eHD/RUQ6/y8uJv8KCgb/HRkW/2FdV/9ZV1L/qKek/6Wj + oP89PDmaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHVzcABraWYooZ+c7YiGgv9RTkn/aGRe/3Zy + a/98eHH/gHx0/4J+dv+CfnX/iIN6/5iTiv+sp6D/vbix/8fDvP/Lxr//y8a//8nEvP/Fwbn/w762/8K+ + tf/CvrX/wr21/8K+tf/CvbT/wb20/8C8s//Au7L/v7uy/7+6sf++urH/vrmw/724sP+8t6//urSs/7aw + qP+yraX/sKyl/66qpP+dmJL/VlVM/zo5L/8YFxL/CAUD/0xIQv9iYFn/Y2Fd/4qJhf9DQj6YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIF+ewCCgH0pioiF7VdVUP9aV1L/bmpk/3dzbP96d2//fHhx/4F8 + dP+Sjob/sKyl/8nFv//U0cv/0s7I/83Jwv/Kxr7/yMS8/8bCuv/Dv7f/wby0/765sf+1san/sq2l/6mm + nv+opJz/p6Sc/6+ro/+yrqb/uLSs/7y4r/++ubD/v7uy/8G8s//Au7L/v7qx/765sP+8uK//vLev/766 + tP/Fwr3/goB6/0JBN/8oKCD/BgUD/yYhG/9lYVv/VVNN/1dWUv9DQj6H////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHh2cgCDgH0hX11Z3k1KRv9iX1r/cG1m/3Vxav95dG3/h4N8/6yoov/Pzcj/2tfS/9bS + zP/Qy8T/zMfA/8jDvP+8uLH/rKmi/5uYkf+NioL/gH13/3h1b/94dW7/fHhx/3p3cP96d3D/endv/3x5 + cv96d3D/eHVu/3t4cv+HhH3/lJCJ/6OfmP+wrKT/vrqx/8G8s//AvLP/v7qx/766sf/Fwbr/sa6p/09O + Rf82Niz/EREN/wwIBP9QS0P/XVpV/0A+Ov0zMzBoPzw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJP + SwBWU08YRkRBy1FPSv9nZF7/cGxm/3h0bv+Xk47/x8XA/+De2v/d2tX/08/I/87Jwv/Au7T/qaSe/4uH + gf97eHL/dXJs/3Vya/98eXH/g394/4mFff+OioH/k46G/5eSiv+ZlIv/mZSL/5eSiv+Tjob/jYmB/4iE + ff+Cfnf/endv/3VybP94dW//goB5/5qXj/+wraT/v7qy/8K9tP/BvbT/vrmy/2tpYf8+PTP/ISEa/wUD + Af8tJx//XltV/0RCPvw3NjNbKigjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZEPwBGQz8fRkQ/3lZT + T/9oZV//dnNt/6OhnP/a2NX/5+Tg/9rWz//QzMT/vbmx/5eUjf96d3D/cW1n/3h0bf+Cfnb/jIiA/5SQ + iP+alo3/n5qR/6OelP+loJf/qKKZ/6mkm/+qpJv/q6Wc/6qlnP+po5r/p6GY/6Wflv+hnJP/nJeO/5WQ + iP+Lh3//f3x0/3Rxav9zcGn/h4R9/6qmnv+/u7L/xcC4/4yJgf9FRDr/MTAo/wsLCP8TDQf/TEdA/0tJ + RP47OTZxNzUvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRQBLSUUlS0lF7FpXUv9va2b/paOf/+Tj + 4P/n5eH/2NTO/8XAuf+Zlo//dXJs/25rZf95dW7/h4N7/5GMhP+ZlIv/npmQ/6Gck/+kn5b/paCY/6ei + mf+po5r/qqSb/6ulnP+rpZz/rKad/6ymnf+spp3/q6Wc/6ulnP+qpJv/qKKZ/6Wgl/+hnJP/m5aN/5KN + hP+EgHj/dXFq/3BtZ/+Fgnv/r6uj/6+ro/9SUUf/PDwy/xoaFP8GAwD/MSoi/0lHQv85NzWHcGlYAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9NSQBPTUklT01J7GBeWf+cmpb/4+Lg/+ro5P/X083/r6ul/3p3 + cf9pZmD/dnJr/4SAef+Pi4P/l5GJ/5uWjv+emZD/oJuS/6KdlP+kn5X/pqGX/6eimf+po5r/qqSb/6qk + m/+rpZz/q6Wc/6ulnP+rpZz/q6Wc/6ulnP+qpJv/qKOa/6eimf+loJf/pJ+V/6Cckv+alo3/kIyE/4J+ + dv9va2X/bWpk/5GNhv9raWH/QkE2/ysqIv8IBwX/GBEK/z06Nf83NjOZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFBPSwBQTkslUU9L7H58eP/Y1tT/7uvo/9TQyv+ZlpD/aWZh/2tnYf98eHH/iYV9/5GN + hP+WkYn/mZSM/5uWjv+emZD/oJuS/6Gck/+inZP/oZuS/52Yj/+alYz/lpGI/5CLg/+OioH/joqB/4+K + gv+VkIf/mpWM/5+Zkf+jnZX/paCX/6WhmP+loJb/o56V/6Kdk/+gm5H/nJeP/5aRiP+JhX3/d3Ns/2dl + Xv9aWFH/REM5/zg4Lv8TEw//CgYB/yokHP8yMS+aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJR + TQBPTkolW1lW7K6sqf/t6+j/1tLN/4uIgv9fXVf/bGli/356c/+JhX3/j4uC/5KOhf+VkIj/mJOL/5qV + jf+alYz/l5KK/5GMhP+Ig3v/fnlx/3NvZ/9qZl//ZGBZ/11ZU/9aVlD/V1RO/1hUT/9dWVP/Yl9Y/2xn + Yf93cmr/g352/5GMg/+alYz/n5qR/6Cbkv+fmpH/nZiP/5uVjf+WkYn/jIiA/3x4cf9hX1n/QUA3/0FA + Nf8kIxz/BgUD/xoSC/8oJSKamv//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlXUwBRT0sldHJv7NLQ + zv/h3tn/jImD/1tYUv9qZ2H/fHhx/4aCev+Lh37/joqB/5GNhP+Tj4b/k46F/4+Kgf+Hgnr/fnlx/3dy + av9ybWb/cGxk/29rZP9taWL/cW1m/3JuZ/9wbGX/bmtk/25qZP9oZV//Y19Z/2RfWf9iXVf/ZGBa/2tm + X/91cGj/gn51/4+Kgv+Yk4r/m5WN/5qUjP+Xkor/k46G/4uHfv97eHD/UVBI/0JBN/8zMin/Dg0K/w4I + Av8eFw+nDTBaATUaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjYABZV1QmkI6L7eDe2/+fnJj/V1RP/2Zj + Xf94dW3/gn53/4aCe/+KhX7/jYiA/46JgP+Nh37/h4J4/4N+dP+Efnb/ioR8/5KOhf+dmJH/p6Ob/7Cs + pP+zr6f/urav/7y4sf+7t7D/urau/7i0rP+xrKX/qaWd/6Sfl/+alY3/kYyE/4aCev99eHD/enVu/355 + cf+FgHj/jYiA/5SPhv+UkIj/ko6G/4+Lgv+JhH3/amdg/0RDOv8+PTP/HBwW/wcFAv8XDgTGCQUBEgIA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFwbABkY18noqCd7b+9uv9dW1f/XVtV/3NvaP99eXH/gX12/4WA + ef+Hgnv/iIJ6/4mDev+Nh37/l5KJ/6ikm/+7t7D/y8jC/9fUz//d2tb/3tzY/+Dd2f/e3Nf/3dvV/9vY + 0//Y1dD/1tPN/9PQyv/QzMX/zcnC/8nFvv/FwLn/wLuz/7m0rP+wq6L/pqCZ/56Zkf+ZlI3/lpGL/5OO + hv+QjIP/kIuD/46Kgf+Lh3//gHx1/09NRP9CQTf/LCwk/wkJBv8QCQL0Fg0DTyMVBQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHl4dABycG0ooJ6a7Xd1cf9RT0r/amZg/3dzbP98eHH/f3t0/4F8dP+CfXX/jId//6Oe + lv++urT/2NXQ/+fl4v/u7On/7u3q/+3r6P/q6OX/5+Xi/+Ti3//i4Nz/393Y/93a1f/a19L/19TP/9TR + zP/Sz8j/0MzF/83Jwv/Kxr//x8O8/8TAuP/BvbT/vbiw/7mzq/+0sKj/s6+o/7Ovqf+qp6D/mZSM/42I + gP+KhH3/hoJ7/2ViWv9DQjf/Ojkw/xUVEP8JBQH/GA0EoQIAAAcKBQIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKA + fACHhYIogX977FFPS/9cWVT/b2tl/3ZybP96dm7/fHhw/4eCev+inpb/yMS//+Xj4P/z8vD/9vX0//Tz + 8f/x8O7/7+3q/+zq5//p5+T/5+Xi/+Xj3//j4N3/4N7Z/97b1v/b2NP/2NXQ/9bTzv/T0Mr/0c7H/87L + xP/Mx8H/ycS9/8bCuv/Dv7f/wLyz/7+6sf++ubD/vLev/725sv/Fwrz/w8C7/6mlnv+OiYL/hYB5/3Zy + a/9JRz7/QkE2/yYlHv8GBQP/EwsC5Q4GADMJAwAABgUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBuagB4dnIfV1VR2E1L + R/9kYVv/cG1n/3Vxav97d3D/lZGK/8TBvP/o5+T/+Pf2//n49//29vT/9PPx//Lx7//w7+3/7u3q/+zq + 5//p5+T/5+Xi/+Xj3//j4d3/4d/a/9/c1//d2dT/2tbR/9fUz//U0cz/0s/I/9DMxf/NycL/y8a//8fD + vP/Fwbn/wr61/8C8s//AvLP/wLuy/7+6sf/BvbT/zcrE/9bU0f+7uLP/j4uF/397c/9WVEz/QkE3/zQz + Kv8VFRL/Ih0X/y8nH3wAAAABAQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtJRABMSUUXREI+yVJQS/9nZF//cG1m/315 + c/+npJ7/3NrX//b29P/5+fj/+Pf2//b19P/19PL/8/Lw//Lw7v/w7uz/7uzp/+zq5//p5+T/6OXi/+bj + 4P/j4d7/4d/b/9/d1//d2tX/29fS/9jVz//V0s3/09DJ/9HNxv/PysT/zMfA/8nFvv/Gwrv/xL+3/8K+ + tf/CvbX/wb20/8G8tP/AvLP/wr62/9TQy//l4+H/yMbC/4+LhP9pZV7/SEY9/19eVv90cm3/rKaf/2Ve + VsoAAAAZGxYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVEPwBFQz4hRkRA41dUT/9oZV//fXp0/7a0sP/p6Ob/+fj3//j3 + 9v/29fT/9fTz//X08v/z8vD/8vHv//Hv7f/v7uv/7uvo/+vp5v/p5+T/5+Xi/+bj4P/k4d3/4t/b/9/d + 2P/e2tX/29jT/9jV0P/W083/1NDL/9LOx//PzMT/zcjC/8vGv//Iw7z/xcG6/8TAuP/Ev7f/w7+2/8O/ + tv/DvrX/wr61/8bBuf/a2NP/7ezq/8PBvf9+enT/ZWJc/7q4s/9ubWj/T0xH/yIbFPYSCQJXGA4FABcK + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAExLRwBMS0clTEpH7VpXUv9zcGv/tbOv//Hx7//5+Pf/9vXz//Xz8v/08/H/9PPx//Py + 8P/y8e7/8e/t//Du6//u7On/7ern/+ro5f/p5uP/5+Xi/+bj4P/k4d3/4t/b/+Dd2P/e2tX/3NjT/9nW + 0f/X087/1dHL/9LPyP/QzcX/zsrC/8zHwP/JxL3/x8K7/8bCuv/Gwbr/xcG5/8XBuf/FwLj/xMC3/8S/ + t//Iw7z/4d7b//Lx8P+2tLD/Y2Bb/2NhWP9DQTj/FRUQ/wYDAP8YDgSmAAAACQYCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBO + SgBPTkolT01J7GRiXf+qqKT/7u3r//j39v/08/H/8/Lw//Py8P/y8e//8vHv//Hw7v/x7+3/8O7r/+/t + 6f/t6+j/7Onm/+ro5P/o5uP/5+Xh/+Xj3//k4d3/4t/b/+Dd1//e29b/3NjT/9rW0f/X1M7/1dLM/9PQ + yf/Rzcb/z8vD/83Iwf/Lxr//ycS9/8nEvf/Iw7z/yMO8/8fDu//Hwrv/xsK6/8bBuv/Fwbn/zcnC/+7t + 6f/r6+n/jouH/0A+Nv9DQjf/Kikh/wgHBP8SCgLnFg0DMx4RBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBPSwBQT0slUlBM7IeF + gf/g393/9/b1//Px7//y8O7/8vDu//Lw7v/x8O3/8e/t//Du7P/v7ur/7+3p/+7r6P/s6ub/6+jl/+nn + 5P/o5eL/5uTg/+Xj3v/j4dz/4t/a/+Dd1//f29b/3dnT/9vW0f/Y1M//1tLM/9TQyv/Szsf/0MzE/87J + wv/Mx8D/zMa//8vGvv/Lxb7/ysW+/8rEvf/JxL3/ycS8/8jDvP/Hw7v/yMK7/9vY0v/5+Pf/wcC+/09N + SP9ZV07/XlxU/xUUEP8LBgH+FAsCeyUWBgD/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFNSTgBPTkolX15a7Le1s//z8vD/8/Hu//Dv + 7P/x7+z/8O/s//Dv7P/w7uv/8O7q/+/t6f/u7Oj/7evn/+3q5v/r6eX/6ufk/+jm4//n5eH/5uTg/+Xi + 3v/j4Nz/4t/Z/+Dd1//f3Nb/3dnT/9vX0f/Y1c//1tPN/9XRy//Tz8f/0c3F/8/Lw//OycL/zcnB/83I + wf/Nx8D/zMfA/8zGv//Mxr//y8a//8vFvv/Kxb7/ycS9/8/Kw//t6+j/4uHg/2VkYP9YVk/9hoN9/z07 + Nf8FAwH/FAsDwgsEABMHAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtZVgBRUEwlfHp37NrZ1//z8u//8O7q//Dt6v/w7er/7+3q/+/t + 6f/v7en/7+zo/+7s6P/t6+f/7enm/+zo5f/q6OT/6efj/+jl4v/n5OD/5uPf/+Xi3f/j4Nz/4t/Z/+Dd + 1//f3Nb/3dnU/9vX0f/Z1c//19PN/9XRy//T0Mj/0s7G/9HMxP/QzMT/0MzE/8/Lw//Py8P/z8rC/87J + wv/OyMH/zcjB/83HwP/Mx8D/zMa//8zHv//e29b/7+7t/4uKh/45ODLrh4V//V9dVv8REQ3/CgUA8xQK + AUccDwIABQMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGlnZABbWlcmlZSR7ejm5P/x7+v/7+3p/+/t6P/v7en/7+3p/+7s6P/u7Oj/7uvn/+3q + 5//t6eb/7Onl/+ro5P/p5+P/6ebi/+jl4f/n5OD/5eLe/+Th3f/j4Nv/4t7Z/+Dd1//f3NX/3dnU/9zX + 0f/a1dD/2NTO/9bSy//U0Mn/08/H/9POx//Szsb/0s7G/9HNxf/RzcX/0czE/9DMxP/Qy8P/z8vD/8/L + w//PysL/zsnC/83Iwf/W0sz/7uzq/6Oinv4zMi7CbWtk3ISBe/8sKyX/BgQB/xEJApQDAQACAQEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJx + bQBlZGAnoqGe7ezp5v/w7en/7uzn/+7s6P/u7Oj/7uzo/+7r5//u6uf/7erm/+3p5v/s6OX/6+jk/+rn + 4//p5uL/6OXh/+fk4P/m49//5eLe/+Th3f/j4Nv/4d7Y/+Dd1v/f3NX/3tnU/9zX0v/a1tD/2NTO/9fT + zP/V0cv/1dHK/9XQyf/U0Mn/1M/I/9PPx//Tz8f/0s7G/9LOxv/SzcX/0c3F/9HNxf/QzMT/0MzE/8/L + w//Tz8j/6Obi/6mnpP88OzecTkxFkoiGgP9WVE7/CQgG/w4HAdULBQEgBAEAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHt5dgBxcGwpq6mm7e3r + 6P/v7ej/7uzn/+7s5//u6+f/7urn/+7q5//t6ub/7enm/+zp5f/s6OT/6ufj/+nn4//p5uL/6OXh/+fk + 4P/m497/5eLd/+Th3P/j4Nr/4t7Y/+Hd1//g3NX/3tnU/93Y0v/b1tD/2dXO/9fTzf/X083/19LM/9bS + zP/W0sv/1dHL/9XRyv/V0Mr/1NDJ/9TPyP/Tz8j/08/H/9POx//Szsb/0s3G/9HNxf/Uz8j/5ePe/6im + ov9CQT2PJyYfOYOBe+h7eXP/IiEc/wUCAPgRCQFbIxICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKBfQB7enYnr66q6u3r5//v7Of/7uvn/+7q + 5v/u6ub/7urm/+3q5v/t6eX/7enl/+zo5P/r6OP/6ufj/+nn4v/p5uH/6OXg/+fk3//m497/5eLd/+Th + 3P/j4Nr/4t7Y/+Hd1//g3Nb/39rU/93Y0v/c19H/29bQ/9vWz//a1c//2dXO/9nUzv/Y1M3/2NPN/9fT + zP/X0sz/1tLM/9bSy//W0cv/1dHK/9XQyv/U0Mn/1NDI/9PPx//V0cn/4+Db/6Shnv9DQj56AAAABmJg + Wp+Uko3/SEZA/wYFAv8KBACmDAIACAsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHp3cwBsamYcpaKf0+fk4f/v6+f/7uvm/+7q5v/u6ub/7urm/+3p + 5f/t6eX/7Ojk/+zo5P/r6OP/6ufi/+nm4v/p5uH/6OXg/+fk3//m497/5uLd/+Xh3P/k4Nr/49/Y/+He + 1//g3Nb/39vU/97Z0//e2NL/3djS/93Y0v/c19H/3NfR/9vW0P/b1tD/29bP/9rVz//Z1c//2dTO/9jU + zv/Y083/19PN/9fTzP/W0sz/1tLL/9bRy//X08z/39zX/5uYlPtGREBUW1lTADk3MkSPjYjsdXJs/xgX + FP8FAQDeCgMAJgkDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHFuagBQTUoKnZuWp93b1//w7ej/7+vm/+7q5v/u6ub/7urm/+7q5f/t6eX/7enk/+zo + 5P/s6OP/6ufi/+nm4v/p5uH/6OXg/+jk3//n497/5uLd/+Xh3P/k4Nv/49/Z/+Le1//h3db/4d3W/+Dc + 1f/g29T/39rU/9/a1P/e2dP/3tnT/97Y0v/d2NL/3dfR/9zX0f/c19H/29bQ/9rW0P/a1c//2dXP/9nV + zv/Z1M7/2NTN/9jTzf/a1s//2dXP/5CNiOZGREEuRUM/AAAAAAt6eXOolJKO/z89N/8CAQD5BAAAZAUA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1q + aACqp6IAkY6Kbs/Lx/zv7Of/7+zm/+/r5v/v6ub/7+rm/+7q5f/u6eX/7enk/+3o5P/s6OP/6+fi/+rn + 4v/p5uH/6eXg/+jk3//n497/5uPd/+bi3P/l4dv/5ODa/+Pf2f/j39j/4t7Y/+Le1//h3df/4d3W/+Hd + 1v/g3NX/4NzV/9/b1P/f29T/39rT/97Z0//e2NL/3djS/93Y0v/d19H/3NfR/9zW0P/b1tD/29bP/9rV + z//c2NH/x8S+/316dbY8ODQOT0tHAGVjWQBQT0hQmJeT8nRxa/8SEQ7/AAAApAAAAAkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL+4tACQjIkAhIF9Kri1 + sdvp5eD/8O3n//Ds5//v6+b/7+rm/+/q5f/u6eX/7unl/+3o5P/t6OP/7Oji/+zn4v/q5uH/6eXh/+nl + 3//o5N//5+Pe/+fj3f/m4t3/5uLc/+Xh2//l4dv/5ODa/+Tg2f/j39n/49/Y/+Pf2P/i3tf/4t7X/+Le + 1//h3db/4d3W/+Dc1f/g3NX/39vU/9/b1P/f2tT/3tnT/97Z0//d2NL/3djS/93X0f/b1tD/sK2n+nVy + bmiloZkAQ0ZLACIhHAAAAAANkI+KspeUkP9APjn/AAAA3wAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFg38AVlZSBJ6cl4bU0Mz+8Ozn//Ht + 5//x7ef/8Ozm/+/r5v/v6uX/7unl/+7p5P/u6eT/7ejj/+zo4v/s5+L/6ubh/+rm4f/p5eD/6eXg/+nl + 3//o5N//6OTe/+fj3v/n493/5uLd/+bi3P/m4tz/5eHb/+Xh2//k4Nr/5ODZ/+Tg2f/j39j/49/Y/+Le + 1//i3tf/4d3W/+Hd1v/h3db/4NzV/+Dc1f/f29T/39rU/9/a1P/OycP/mpeRxHZybRp+e3UAU1NTAAAA + AADGwr0AeHZxT6imo/N5dnD/ERAO+QAAAGMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPytAAhoN9AIiGgCCyr6vI49/a//Dt5//x7ef/8e3o//Ds + 5//w6+b/8Ovm/+/r5f/v6uX/7+rl/+7p5P/u6eT/7ejj/+3o4//s6OL/6+fi/+vn4f/q5uH/6ubg/+nl + 4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3f/m4t3/5uLc/+bi3P/l4dv/5eHa/+Tg2v/k4Nn/49/Z/+Pf + 2P/j39j/4t7X/+Le1//h3db/4d3W/9vX0f+yr6nth4N+VJ6UlQB7e3IAAAAAAAUFAAAmJSIAPjw4DKup + pq+hnpr/RUM+/wAAAKAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDwsIAGz0YALu4tQCWk5BKwb645erm4P/x7Of/8u7p//Lv6v/w7ef/8Ozm//Ds + 5v/w7Ob/8Ozm/+/r5f/v6+X/7+vl/+/q5f/u6uT/7unk/+7o4//t6OP/7Oji/+zn4v/r5+L/6+bh/+rm + 4f/q5eD/6eXg/+nl3//o5N//6OTe/+fj3v/n493/5+Pd/+bi3f/m4tz/5eHc/+Xh2//k4Nr/5ODa/+Tg + 2f/j39n/4NzW/8C8t/uTkIqFdnRxCI2LhQDPz8EAAAAAAAAAAAAuLisA1dTRANXTzkmvrarzkIyG/xwb + GdEAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAeHhvAHZ2cgANFBQCnpyWZ8vHwu7q5eD/8e3o//Pw7P/z8Ov/8e3n//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm/+/r5f/v6+X/7+rl/+7p5P/u6eT/7ejj/+3o4//s5+L/7Ofi/+vn + 4f/r5uH/6ubh/+rl4P/p5eD/6eXf/+jk3//o5N7/5+Pe/+fj3v/n493/5uLd/+bi3P/j39n/yMW//5mW + kaNoZWERfn95AP///wAAAAAAAAAAAAAAAAAAAAAA0c3FAOPe1Q/KycazqaWh/3FtaPQODQxCLCooAK+n + nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH19 + eACRkIsAnJmVBaGfmmfBvrnl5uHc//Lu6f/18u7/9PHt//Hu6f/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/7+vl/+/q5f/u6uT/7unk/+7p5P/t6OP/7ejj/+zn + 4v/s5+L/6+fh/+rm4f/q5uD/6eXg/+nl4P/p5d//6eXf/+Pf2v/EwLv6m5eSm3p4cxuMioMAoKmXAH5z + cwAAAAAAAAAAAAAAAAAAAAAA3NjSAN7c2QDh3ttQtbOx8bKtp/+inZaLZmFaAq6mngAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVkYoAlZONAFJY + VQKZlZBOuLWw0dvX0v/v6+b/9fPv//b08P/z8Oz/8e3n//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ovm//Dr5v/v6+X/7+rl/+/q5f/u6eT/7unk/+3o + 4//t6OP/7Ofi/+zn4v/q5eD/3dnU/7u4s+uVkYx/dHNtD5SSjQAAAAAALCsqAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAODc1gDk3tYK2NbTn6qmo//HwLnTwbuyHbmyqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj42IAIaCfgD///8AiYeFK6ej + n5/Hw77x5eHc//Tx7f/49vP/9/Tx//Pw7P/x7ej/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w6+b/8Ovm//Dr5v/v6uX/7unk/+bh + 3P/KxsH9qKahxIuJhU1UUE4Fe3lzAI6LhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wDl4t4A7OnlLrCuqsOnpJ6e2NDJD763sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqqAH11cgCPgoEAkI6LAHdycAuXlJBUsq+ruMvH + w/Xl4t7/9fPx//r49v/59/T/9fLu//Lv6v/x7ef/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds + 5v/w7Ob/8Ozm//Ds5v/w7Ob/8Ozm//Ds5v/w7Ob/8e3n/+/r5f/l4Nv/zsrF/bKuqdiTkIx7endzHK2m + qwCHhH8Ah4N/ALa2tgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM/JxADLyMEA4+DYAb26 + sxOmpJ4NuLGrAL63sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AY2VzAIyIhQCxq6cAiIaDE5qYlFauq6exxsTA6dza + 1//v7ev/+Pf1//r59//49vP/9vPv//Tw7P/y7+r/8u7p//Hu6P/x7ef/8e3n//Ht5//x7ef/8e3n//Hu + 6P/y7uj/8O3n/+3p4//m4tz/2tbR/8bCvfauq6bGmJWQeICAeigAAAABiYeCAHh4cADJyLwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADX1M8A6ebhALm2sACmpJ4AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSUjgCanJYAkI2KALy4swBqamkLjoyIN6OhnXSysKy3xsTA39bU + 0ffi4d7/7Orn//Du7P/z8O3/8/Dt//Lw7P/x7ur/8Ozo/+/r5//u6uX/6OXg/+Lf2v/Z1dD/zMjD/b67 + t+etqqbJn5yXko6MiExwb2sY////AI2MhQB+fHoAlpOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAADAwMAP///wCEgoEAlJGPAP///wBcW1kJe3p2LJGPi1KjoZ13rKmmmrKv + rLq5trPRvLq33L27t+C6uLTlt7Wx4ra0sNuyr6vYqKaiwaGemqeal5OEjouHYHx6djhraGcSAAAAAZqY + kwB+fHgAXmBeAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8Axse9ADUzMQBqa2oAfHt4AJCOigD///8AOjs5BmJgXg1+fHoThYSAFYB/ + eiF6eXUvenl2JoSDgBV8enYUZmVhDklHRQkAAAAClZKOAHx5dQBwbWwAeHd1AHJvbAD07OQABQUFAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUwB4eHUAenl3AIOBfwCUko8AmZeUAI+OigCGhYEAiIaDAJeV + kgCQjooAgn98AHRybwBwb2kAc3NsAP///wAgICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8AAAD///////////8A + AAD/////AH////8AAAD////AAAD///8AAAD///wAAAAf//8AAAD///gAAAAP//8AAAD//+AAAAAB//8A + AAD//4AAAAAA//8AAAD//wAAAAAAf/8AAAD//gAAAAAAP/8AAAD//AAAAAAAH/8AAAD/+AAAAAAAD/8A + AAD/8AAAAAAAB/8AAAD/8AAAAAAAB/8AAAD/8AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8A + AAD/4AAAAAAAA/8AAAD/4AAAAAAAA/8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAf8AAAD/4AAAAAAAAP8A + AAD/4AAAAAAAAP8AAAD/4AAAAAAAAP8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8AAAD/4AAAAAAAAH8A + AAD/4AAAAAAAAD8AAAD/4AAAAAAAAD8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAB8AAAD/4AAAAAAAAA8A + AAD/4AAAAAAAAA8AAAD/4AAAAAAAAA8AAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcAAAD/4AAAAAAAAAcA + AAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAMAAAD/4AAAAAAAAAEA + AAD/8AAAAAAABAEAAAD/8AAAAAAABgEAAAD/+AAAAAAABwAAAAD/+AAAAAAADwAAAAD//gAAAAAAH4AA + AAD//wAAAAAAP4AAAAD//wAAAAAAf4AAAAD//4AAAAAA/8EAAAD///AAAAAD//8AAAD///wAAAAP//8A + AAD///4AAAA///8AAAD////gAAP///8AAAD///////////8AAAD///////////8AAAD///////////8A + AAA= + + + \ No newline at end of file diff --git a/DBChm/PDM/PdmReader.cs b/DBChm/PDM/PdmReader.cs new file mode 100644 index 0000000..1e713e1 --- /dev/null +++ b/DBChm/PDM/PdmReader.cs @@ -0,0 +1,240 @@ +using System; +using System.Linq; +using System.Xml; +using System.Collections.Generic; +using DBCHM.PdmModels; + +namespace DBCHM.PDM +{ + /// + /// PDM实体集合 + /// + public class PdmModels + { + public PdmModels() + { + this.Tables = new List(); + } + /// + /// 表集合 + /// + public IList Tables { get; private set; } + } + + public class PdmReader + { + /// + /// 读取指定Pdm文件的实体集合 + /// + /// Pdm文件名(全路径名) + /// 实体集合 + public PdmModels ReadFromFile(string pdmFile) + { + if (string.IsNullOrEmpty(pdmFile)) + { + return null; + } + //加载文件. + var xmlDoc = new XmlDocument(); + xmlDoc.Load(pdmFile); + //必须增加xml命名空间管理,否则读取会报错. + var xmlnsManager = new XmlNamespaceManager(xmlDoc.NameTable); + xmlnsManager.AddNamespace("a", "attribute"); + xmlnsManager.AddNamespace("c", "collection"); + xmlnsManager.AddNamespace("o", "object"); + var theModels = new PdmModels(); + + //读取所有表节点 + var xnTablesList = xmlDoc.SelectNodes("//c:Tables", xmlnsManager); + if (xnTablesList != null) + foreach (var xnTable in from XmlNode xmlTables in xnTablesList from XmlNode xnTable in xmlTables.ChildNodes where xnTable.Name != "o:Shortcut" select xnTable) + { + theModels.Tables.Add(GetTable(xnTable)); + } + return theModels; + } + + //初始化"o:Table"的节点 + private TableInfo GetTable(XmlNode xnTable) + { + var mTable = new TableInfo(); + var xe = (XmlElement)xnTable; + mTable.TableId = xe.GetAttribute("Id"); + var xnTProperty = xe.ChildNodes; + foreach (XmlNode xnP in xnTProperty) + { + switch (xnP.Name) + { + case "a:ObjectID": mTable.ObjectID = xnP.InnerText; + break; + case "a:Name": mTable.Name = xnP.InnerText; + break; + case "a:Code": mTable.Code = xnP.InnerText; + break; + case "a:CreationDate": mTable.CreationDate = String2DateTime(xnP.InnerText); + break; + case "a:Creator": mTable.Creator = xnP.InnerText; + break; + case "a:ModificationDate": mTable.ModificationDate = String2DateTime(xnP.InnerText); + break; + case "a:Modifier": mTable.Modifier = xnP.InnerText; + break; + case "a:Comment": mTable.Comment = xnP.InnerText; + break; + case "a:PhysicalOptions": mTable.PhysicalOptions = xnP.InnerText; + break; + case "c:Columns": InitColumns(xnP, mTable); + break; + case "c:Keys": InitKeys(xnP, mTable); + break; + case "c:PrimaryKey": + InitPrimaryKey(xnP, mTable); + break; + case "a:Description": mTable.Description = xnP.InnerText; + break; + } + } + return mTable; + } + + //PDM文件中的日期格式采用的是当前日期与1970年1月1日8点之差的秒树来保存. + private DateTime _baseDateTime = new DateTime(1970, 1, 1, 8, 0, 0); + private DateTime String2DateTime(string dateString) + { + Int64 theTicker = Int64.Parse(dateString); + return _baseDateTime.AddSeconds(theTicker); + } + + //初始化"c:Columns"的节点 + private void InitColumns(XmlNode xnColumns, TableInfo pTable) + { + foreach (XmlNode xnColumn in xnColumns) + { + pTable.AddColumn(GetColumn(xnColumn, pTable)); + } + } + + //初始化c:Keys"的节点 + private void InitKeys(XmlNode xnKeys, TableInfo pTable) + { + foreach (XmlNode xnKey in xnKeys) + { + pTable.AddKey(GetKey(xnKey, pTable)); + } + } + //初始化c:PrimaryKey"的节点 + private void InitPrimaryKey(XmlNode xnPrimaryKey, TableInfo pTable) + { + pTable.PrimaryKeyRefCode = GetPrimaryKey(xnPrimaryKey); + } + private static Boolean ConvertToBooleanPg(Object obj) + { + if (obj != null) + { + string mStr = obj.ToString(); + mStr = mStr.ToLower(); + if ((mStr.Equals("y") || mStr.Equals("1")) || mStr.Equals("true")) + { + return true; + } + } + return false; + } + + private ColumnInfo GetColumn(XmlNode xnColumn, TableInfo ownerTable) + { + var mColumn = new ColumnInfo(ownerTable); + var xe = (XmlElement)xnColumn; + mColumn.ColumnId = xe.GetAttribute("Id"); + var xnCProperty = xe.ChildNodes; + foreach (XmlNode xnP in xnCProperty) + { + switch (xnP.Name) + { + case "a:ObjectID": mColumn.ObjectID = xnP.InnerText; + break; + case "a:Name": mColumn.Name = xnP.InnerText; + break; + case "a:Code": mColumn.Code = xnP.InnerText; + break; + case "a:CreationDate": mColumn.CreationDate = String2DateTime(xnP.InnerText); + break; + case "a:Creator": mColumn.Creator = xnP.InnerText; + break; + case "a:ModificationDate": mColumn.ModificationDate = String2DateTime(xnP.InnerText); + break; + case "a:Modifier": mColumn.Modifier = xnP.InnerText; + break; + case "a:Comment": mColumn.Comment = xnP.InnerText; + break; + case "a:DataType": mColumn.DataType = xnP.InnerText; + break; + case "a:Length": mColumn.Length = xnP.InnerText; + break; + case "a:Identity": mColumn.Identity = ConvertToBooleanPg(xnP.InnerText); + break; + case "a:Mandatory": mColumn.Mandatory = ConvertToBooleanPg(xnP.InnerText); + break; + case "a:PhysicalOptions": mColumn.PhysicalOptions = xnP.InnerText; + break; + case "a:ExtendedAttributesText": mColumn.ExtendedAttributesText = xnP.InnerText; + break; + case "a:Precision": + mColumn.Precision = xnP.InnerText; + break; + } + } + return mColumn; + } + + private string GetPrimaryKey(XmlNode xnKey) + { + var xe = (XmlElement)xnKey; + if (xe.ChildNodes.Count <= 0) return ""; + var theKp = (XmlElement)xe.ChildNodes[0]; + return theKp.GetAttribute("Ref"); + } + private void InitKeyColumns(XmlNode xnKeyColumns, PdmKey Key) + { + var xe = (XmlElement)xnKeyColumns; + var xnKProperty = xe.ChildNodes; + foreach (var theRef in from XmlNode xnP in xnKProperty select ((XmlElement)xnP).GetAttribute("Ref")) + { + Key.AddColumnObjCode(theRef); + } + } + private PdmKey GetKey(XmlNode xnKey, TableInfo ownerTable) + { + var mKey = new PdmKey(ownerTable); + var xe = (XmlElement)xnKey; + mKey.KeyId = xe.GetAttribute("Id"); + var xnKProperty = xe.ChildNodes; + foreach (XmlNode xnP in xnKProperty) + { + switch (xnP.Name) + { + case "a:ObjectID": mKey.ObjectID = xnP.InnerText; + break; + case "a:Name": mKey.Name = xnP.InnerText; + break; + case "a:Code": mKey.Code = xnP.InnerText; + break; + case "a:CreationDate": mKey.CreationDate = String2DateTime(xnP.InnerText); + break; + case "a:Creator": mKey.Creator = xnP.InnerText; + break; + case "a:ModificationDate": mKey.ModificationDate = String2DateTime(xnP.InnerText); + break; + case "a:Modifier": mKey.Modifier = xnP.InnerText; + break; + case "c:Key.Columns": + InitKeyColumns(xnP, mKey); + break; + } + } + return mKey; + } + } + + +} diff --git a/DBChm/PdmModels/ColumnInfo.cs b/DBChm/PdmModels/ColumnInfo.cs new file mode 100644 index 0000000..6aa398f --- /dev/null +++ b/DBChm/PdmModels/ColumnInfo.cs @@ -0,0 +1,180 @@ +using System; + +namespace DBCHM.PdmModels +{ + /// + /// 表列信息 + /// + public class ColumnInfo + { + private TableInfo _OwnerTable; + /// + /// 所属表 + /// + public TableInfo OwnerTable + { + get { return _OwnerTable; } + } + public ColumnInfo(TableInfo OwnerTable) + { + this._OwnerTable = OwnerTable; + } + /// + /// 是否主键 + /// + public bool IsPrimaryKey + { + get + { + PdmKey theKey = _OwnerTable.PrimaryKey; + if (theKey != null) + { + if (theKey.ColumnObjCodes.Contains(columnId)) + { + return true; + } + } + return false; + } + } + + string columnId; + /// + /// 列标识 + /// + public string ColumnId + { + get { return columnId; } + set { columnId = value; } + } + string objectID; + /// + /// 对象Id,全局唯一. + /// + public string ObjectID + { + get { return objectID; } + set { objectID = value; } + } + string name; + /// + /// 列名 + /// + public string Name + { + get { return name; } + set { name = value; } + } + string code; + /// + /// 列代码,对应数据库表字段名 + /// + public string Code + { + get { return code; } + set { code = value; } + } + DateTime creationDate; + /// + /// 创建日期 + /// + public DateTime CreationDate + { + get { return creationDate; } + set { creationDate = value; } + } + string creator; + /// + /// 创建人 + /// + public string Creator + { + get { return creator; } + set { creator = value; } + } + DateTime modificationDate; + /// + /// 修改日期 + /// + public DateTime ModificationDate + { + get { return modificationDate; } + set { modificationDate = value; } + } + string modifier; + /// + /// 修改人 + /// + public string Modifier + { + get { return modifier; } + set { modifier = value; } + } + string comment; + /// + /// 注视 + /// + public string Comment + { + get { return comment; } + set { comment = value; } + } + string dataType; + /// + /// 数据类型 + /// + public string DataType + { + get { return dataType; } + set { dataType = value; } + } + string length; + /// + /// 数据长度 + /// + public string Length + { + get { return length; } + set { length = value; } + } + bool identity; + /// + /// 是否自增量 + /// + public bool Identity + { + get { return identity; } + set { identity = value; } + } + bool mandatory; + /// + /// 是否可空 + /// + public bool Mandatory + { + get { return mandatory; } + set { mandatory = value; } + } + string extendedAttributesText; + /// + /// 扩展属性 + /// + public string ExtendedAttributesText + { + get { return extendedAttributesText; } + set { extendedAttributesText = value; } + } + /// + /// 物理选项 + /// + public string PhysicalOptions { get; set; } + /// + /// 精度 + /// + public string Precision { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + } +} diff --git a/DBChm/PdmModels/PdmKey.cs b/DBChm/PdmModels/PdmKey.cs new file mode 100644 index 0000000..939f49c --- /dev/null +++ b/DBChm/PdmModels/PdmKey.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; + +namespace DBCHM.PdmModels +{ + public class PdmKey + { + + string keyId; + /// + /// 关键字标识 + /// + public string KeyId + { + get { return keyId; } + set { keyId = value; } + } + string objectID; + /// + /// 对象Id + /// + public string ObjectID + { + get { return objectID; } + set { objectID = value; } + } + string name; + /// + /// Key名 + /// + public string Name + { + get { return name; } + set { name = value; } + } + string code; + /// + /// Key代码,对应数据库中的Key. + /// + public string Code + { + get { return code; } + set { code = value; } + } + DateTime creationDate; + /// + /// 创建日期 + /// + public DateTime CreationDate + { + get { return creationDate; } + set { creationDate = value; } + } + string creator; + /// + /// 创建人 + /// + public string Creator + { + get { return creator; } + set { creator = value; } + } + DateTime modificationDate; + /// + /// 修改日期 + /// + public DateTime ModificationDate + { + get { return modificationDate; } + set { modificationDate = value; } + } + string modifier; + /// + /// 修改人 + /// + public string Modifier + { + get { return modifier; } + set { modifier = value; } + } + + IList columns; + /// + /// Key涉及的列 + /// + public IList Columns + { + get { return columns; } + } + + public void AddColumn(ColumnInfo mColumn) + { + if (columns == null) + columns = new List(); + columns.Add(mColumn); + } + + private List _ColumnObjCodes = new List(); + /// + /// Key涉及的列代码,根据辞可访问到列信息.对应列的ColumnId + /// + public List ColumnObjCodes + { + get { return _ColumnObjCodes; } + } + public void AddColumnObjCode(string ObjCode) + { + _ColumnObjCodes.Add(ObjCode); + } + + private TableInfo _OwnerTable = null; + + public PdmKey(TableInfo table) + { + _OwnerTable = table; + } + } + +} diff --git a/DBChm/PdmModels/PdmModels.cs b/DBChm/PdmModels/PdmModels.cs new file mode 100644 index 0000000..13c8769 --- /dev/null +++ b/DBChm/PdmModels/PdmModels.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace DBCHM.PdmModels +{ + /// + /// PDM实体集合 + /// + public class PdmModels + { + public PdmModels() + { + Tables = new List(); + } + /// + /// 表集合 + /// + public IList Tables { get; private set; } + } +} diff --git a/DBChm/PdmModels/SelectedTables.cs b/DBChm/PdmModels/SelectedTables.cs new file mode 100644 index 0000000..6764cc8 --- /dev/null +++ b/DBChm/PdmModels/SelectedTables.cs @@ -0,0 +1,32 @@ +using DocTools.Dtos; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DBCHM.PdmModels +{ + /// + /// 关键词过滤 + /// + [Serializable] + public class SelectedTables + { + public List tables { get; set; } = new List(); + + //var viewDict = new NameValueCollection(); + //var procDict = new NameValueCollection(); + + public Dictionary viewDict { get; set; } = new Dictionary(); + public Dictionary procDict { get; set; } = new Dictionary(); + + + public void Clear() + { + this.tables.Clear(); + this.viewDict.Clear(); + this.procDict.Clear(); + } + } +} diff --git a/DBChm/PdmModels/TableInfo.cs b/DBChm/PdmModels/TableInfo.cs new file mode 100644 index 0000000..76b49c8 --- /dev/null +++ b/DBChm/PdmModels/TableInfo.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; + +namespace DBCHM.PdmModels +{ +/// + /// 表信息 + /// + public class TableInfo + { + public TableInfo() + { + keys = new List(); + columns = new List(); + + } + string tableId; + /// + /// 表ID + /// + public string TableId + { + get { return tableId; } + set { tableId = value; } + } + string objectID; + /// + /// 对象ID + /// + public string ObjectID + { + get { return objectID; } + set { objectID = value; } + } + string name; + /// + /// 表名 + /// + public string Name + { + get { return name; } + set { name = value; } + } + string code; + /// + /// 表代码,对应数据库表名 + /// + public string Code + { + get { return code; } + set { code = value; } + } + DateTime creationDate; + /// + /// 创建日期 + /// + public DateTime CreationDate + { + get { return creationDate; } + set { creationDate = value; } + } + string creator; + /// + /// 创建人 + /// + public string Creator + { + get { return creator; } + set { creator = value; } + } + DateTime modificationDate; + /// + /// 修改日期 + /// + public DateTime ModificationDate + { + get { return modificationDate; } + set { modificationDate = value; } + } + string modifier; + /// + /// 修改人 + /// + public string Modifier + { + get { return modifier; } + set { modifier = value; } + } + string comment; + /// + /// 注释 + /// + public string Comment + { + get { return comment; } + set { comment = value; } + } + + string physicalOptions; + /// + /// 物理选项 + /// + public string PhysicalOptions + { + get { return physicalOptions; } + set { physicalOptions = value; } + } + + + IList columns; + /// + /// 表列集合 + /// + public IList Columns + { + get { return columns; } + } + + IList keys; + /// + /// 表Key集合 + /// + public IList Keys + { + get { return keys; } + } + + public void AddColumn(ColumnInfo mColumn) + { + if (columns == null) + columns = new List(); + columns.Add(mColumn); + } + + public void AddKey(PdmKey mKey) + { + if (keys == null) + keys = new List(); + keys.Add(mKey); + } + /// + /// 主键Key代码.=>KeyId + /// + public string PrimaryKeyRefCode { get; set; } + /// + /// 主关键字 + /// + public PdmKey PrimaryKey + { + get + { + foreach (var key in keys) + { + if (key.KeyId == PrimaryKeyRefCode) + { + return key; + } + } + return null; + } + } + /// + /// 表的描述=>PDM Notes. + /// + public string Description { get; set; } + } +} diff --git a/DBChm/Program.cs b/DBChm/Program.cs new file mode 100644 index 0000000..791ff96 --- /dev/null +++ b/DBChm/Program.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace DBCHM +{ + static class Program + { + public static bool IsAdministrator() + { + System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent(); + System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity); + return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); + } + + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + + Application.ThreadException += Application_ThreadException; + + + //判断当前登录用户是否为管理员 + if (IsAdministrator()) + { + //如果是管理员,则直接运行 + Application.Run(new MainForm()); + } + else + { + //创建启动对象 + System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.UseShellExecute = true; + startInfo.WorkingDirectory = Environment.CurrentDirectory; + startInfo.FileName = Application.ExecutablePath; + //设置启动动作,确保以管理员身份运行 + startInfo.Verb = "runas"; + try + { + System.Diagnostics.Process.Start(startInfo); + } + catch + { + return; + } + //退出 + Application.Exit(); + } + } + + private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) + { + LogUtils.LogError("Application_ThreadException", Developer. MJ, e.Exception); + } + } +} diff --git a/DBChm/Properties/AssemblyInfo.cs b/DBChm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b8b26f5 --- /dev/null +++ b/DBChm/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("DBCHM - 51Try.Top")] +[assembly: AssemblyDescription("数据库CHM生成工具")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("51Try.Top")] +[assembly: AssemblyProduct("DBCHM")] +[assembly: AssemblyCopyright("Copyright ©2018-2021 lztkdr")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +//将 ComVisible 设置为 false 将使此程序集中的类型 +//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("55b00db1-ba55-47b7-bd44-35c0fcd05fd5")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, +// 方法是按如下所示使用“*”: : +[assembly: AssemblyVersion("1.8.0.3")] +[assembly: AssemblyFileVersion("1.8.0.3-beta")] diff --git a/DBChm/Properties/Resources.Designer.cs b/DBChm/Properties/Resources.Designer.cs new file mode 100644 index 0000000..e13e73e --- /dev/null +++ b/DBChm/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace DBCHM.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DBCHM.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap loading { + get { + object obj = ResourceManager.GetObject("loading", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/DBChm/Properties/Resources.resx b/DBChm/Properties/Resources.resx new file mode 100644 index 0000000..57f16af --- /dev/null +++ b/DBChm/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\loading.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/DBChm/Properties/Settings.Designer.cs b/DBChm/Properties/Settings.Designer.cs new file mode 100644 index 0000000..69704e5 --- /dev/null +++ b/DBChm/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace DBCHM.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/DBChm/Properties/Settings.settings b/DBChm/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/DBChm/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DBChm/Resources/loading.gif b/DBChm/Resources/loading.gif new file mode 100644 index 0000000..09b57a7 Binary files /dev/null and b/DBChm/Resources/loading.gif differ diff --git a/DBChm/TreeViewEnhanced.cs b/DBChm/TreeViewEnhanced.cs new file mode 100644 index 0000000..c8ae0dd --- /dev/null +++ b/DBChm/TreeViewEnhanced.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace DBCHM +{ + /// + /// 在使用treeview控件过程中会碰到, + /// 当快速点击checkbox时,checkbox选中状态和实际状态不符,并且不会触发aftercheck事件, + /// 造成此问题的原因是:快速点击识别为双击事件。 + /// + public class TreeViewEnhanced : TreeView + { + protected override void WndProc(ref Message m) + { + if (m.Msg == 0x203) { m.Result = IntPtr.Zero; } + else base.WndProc(ref m); + } + } +} diff --git a/DBChm/app.config b/DBChm/app.config new file mode 100644 index 0000000..6fe68db --- /dev/null +++ b/DBChm/app.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DBChm/htmlhelp.exe b/DBChm/htmlhelp.exe new file mode 100644 index 0000000..0fd56d9 Binary files /dev/null and b/DBChm/htmlhelp.exe differ diff --git a/DBChm/ico/DBCHM.ico b/DBChm/ico/DBCHM.ico new file mode 100644 index 0000000..856b48b Binary files /dev/null and b/DBChm/ico/DBCHM.ico differ diff --git a/DBChm/ico/about.png b/DBChm/ico/about.png new file mode 100644 index 0000000..a2bbb57 Binary files /dev/null and b/DBChm/ico/about.png differ diff --git a/DBChm/ico/connect.png b/DBChm/ico/connect.png new file mode 100644 index 0000000..17d56c2 Binary files /dev/null and b/DBChm/ico/connect.png differ diff --git a/DBChm/ico/empty.png b/DBChm/ico/empty.png new file mode 100644 index 0000000..84a29dc Binary files /dev/null and b/DBChm/ico/empty.png differ diff --git a/DBChm/ico/excel.png b/DBChm/ico/excel.png new file mode 100644 index 0000000..f8c4934 Binary files /dev/null and b/DBChm/ico/excel.png differ diff --git a/DBChm/ico/exp.png b/DBChm/ico/exp.png new file mode 100644 index 0000000..c7bc9e1 Binary files /dev/null and b/DBChm/ico/exp.png differ diff --git a/DBChm/ico/html.png b/DBChm/ico/html.png new file mode 100644 index 0000000..9b33cfa Binary files /dev/null and b/DBChm/ico/html.png differ diff --git a/DBChm/ico/logo.png b/DBChm/ico/logo.png new file mode 100644 index 0000000..a8555e7 Binary files /dev/null and b/DBChm/ico/logo.png differ diff --git a/DBChm/ico/md.png b/DBChm/ico/md.png new file mode 100644 index 0000000..67d3e31 Binary files /dev/null and b/DBChm/ico/md.png differ diff --git a/DBChm/ico/pdf.png b/DBChm/ico/pdf.png new file mode 100644 index 0000000..690f9c6 Binary files /dev/null and b/DBChm/ico/pdf.png differ diff --git a/DBChm/ico/refresh_reload.png b/DBChm/ico/refresh_reload.png new file mode 100644 index 0000000..991f43a Binary files /dev/null and b/DBChm/ico/refresh_reload.png differ diff --git a/DBChm/ico/upload.png b/DBChm/ico/upload.png new file mode 100644 index 0000000..794b9a9 Binary files /dev/null and b/DBChm/ico/upload.png differ diff --git a/DBChm/ico/word.png b/DBChm/ico/word.png new file mode 100644 index 0000000..025144c Binary files /dev/null and b/DBChm/ico/word.png differ diff --git a/DBChm/ico/xml.png b/DBChm/ico/xml.png new file mode 100644 index 0000000..8772ee7 Binary files /dev/null and b/DBChm/ico/xml.png differ diff --git a/DBChm/lib/ComponentFactory.Krypton.Toolkit.dll b/DBChm/lib/ComponentFactory.Krypton.Toolkit.dll new file mode 100644 index 0000000..12e7ac1 Binary files /dev/null and b/DBChm/lib/ComponentFactory.Krypton.Toolkit.dll differ diff --git a/DBChm/packages.config b/DBChm/packages.config new file mode 100644 index 0000000..0e8a792 --- /dev/null +++ b/DBChm/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/DocTools/AppConst.cs b/DocTools/AppConst.cs new file mode 100644 index 0000000..d32b9e6 --- /dev/null +++ b/DocTools/AppConst.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools +{ + /// + /// dll库全局参数设置 + /// + public static class AppConst + { + /// + /// 修订日志 + /// + public const string LOG_CHAPTER_NAME = "修订日志"; + + /// + /// 数据库表目录 + /// + public const string TABLE_CHAPTER_NAME = "数据库表目录"; + + + /// + /// 数据库表结构 + /// + public const string TABLE_STRUCTURE_CHAPTER_NAME = "数据库表结构"; + } +} diff --git a/DocTools/ConfigUtils.cs b/DocTools/ConfigUtils.cs new file mode 100644 index 0000000..75237ce --- /dev/null +++ b/DocTools/ConfigUtils.cs @@ -0,0 +1,352 @@ +using MJTop.Data; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +namespace DocTools +{ + /// + /// 管理 配置的连接字符串 + /// + public static class ConfigUtils + { + /// + /// 当前应用程序的名称 + /// + private static string ConfigFileName = Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName).Replace(".vshost", ""); + /// + /// 定义配置存放的路径 + /// + public static string AppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), ConfigFileName); + + /// + /// sqlite db文件的存放路径 + /// + private static string ConfigFilePath = string.Empty; + + /// + /// 针对配置的 数据库操作对象 + /// + private static DB db = null; + + /// + /// 初始化静态数据 + /// 将sqlite数据库写入 C:\Users\用户名\AppData\Local\DBChm 目录中 + /// + static ConfigUtils() + { + try + { + if (!ZetaLongPaths.ZlpIOHelper.DirectoryExists(AppPath)) + { + ZetaLongPaths.ZlpIOHelper.CreateDirectory(AppPath); + } + AddSecurityControll2Folder(AppPath); + ConfigFilePath = Path.Combine(AppPath, ConfigFileName + ".db"); + Init(); + } + catch (Exception ex) + { + LogUtils.LogError("ConfigUtils初始化", Developer.SysDefault, ex); + } + } + + /// + /// 初始化创建配置数据库 + /// + private static void Init() + { + db = DBMgr.UseDB(DBType.SQLite, ConfigFilePath); + + string strSql = @"create table DBCHMConfig + ( + Id integer PRIMARY KEY autoincrement, + Name nvarchar(200) unique, + DBType varchar(30), + Server varchar(100), + Port integer, + DBName varchar(100), + Uid varchar(50), + Pwd varchar(100), + ConnTimeOut integer, + ConnString text, + Modified text + )"; + + //表不存在则创建 连接字符串 配置表 + if (db.Info.TableNames == null || !db.Info.TableNames.Contains(nameof(DBCHMConfig), StringComparer.OrdinalIgnoreCase)) + { + db.ExecSql(strSql); + //执行后,刷新实例 表结构信息 + db.Info.Refresh(); + } + else + { + // v1.7.3.7 版本 增加 连接超时 与 最后连接时间 + var info = db.Info; + if(!info.IsExistColumn(nameof(DBCHMConfig), nameof(DBCHMConfig.Modified))) + { + var configs = db.GetListDictionary("select * from " + nameof(DBCHMConfig)); + + db.Info.DropTable(nameof(DBCHMConfig)); + + db.ExecSql(strSql); + + //执行后,刷新实例 表结构信息 + db.Info.Refresh(); + + if (configs != null && configs.Count > 0) + { + foreach (var config in configs) + { + try + { + db.Insert(config, nameof(DBCHMConfig)); + } + catch (Exception ex) + { + LogUtils.LogError("Init", Developer.SysDefault, ex, config); + } + } + + db.ExecSql("update " + nameof(DBCHMConfig) + " set ConnTimeOut = 120 "); + } + } + } + } + + + + /// + /// 判断磁盘路径下是否安装存在某个文件,最后返回存在某个文件的路径 + /// + /// + /// + /// + public static bool IsInstall(string[] installPaths, out string installPath) + { + installPath = string.Empty; + var driInfos = DriveInfo.GetDrives(); + foreach (DriveInfo dInfo in driInfos) + { + if (dInfo.DriveType == DriveType.Fixed) + { + foreach (string ipath in installPaths) + { + string path = Path.Combine(dInfo.Name, ipath); + if (File.Exists(path)) + { + installPath = path; + return true; + } + } + } + } + return false; + } + + + + + + + /// + /// 搜索获取软件安装目录 + /// + /// 软件名称 或 软件的主程序带exe的文件名 + /// 获取安装目录 + public static string SearchInstallDir(params string[] orNames) + { + //即时刷新注册表 + SHChangeNotify(0x8000000, 0, IntPtr.Zero, IntPtr.Zero); + + string installDir = null; + + var or_install_addrs = new List + { + @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall", + @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" + }; + + var or_get_key_names = new List + { + "InstallLocation", + "InstallPath", + "Install_Dir", + "UninstallString" + }; + + + Microsoft.Win32.RegistryKey regKey = null; + try + { + regKey = Microsoft.Win32.Registry.LocalMachine; + + var arr_exe = orNames.Where(t => t.EndsWith(".exe")).ToList(); + var arr_name = orNames.Where(t => !t.EndsWith(".exe")).ToList(); + + if (arr_exe.Any()) + { + foreach (var exe_name in arr_exe) + { + var name_node = regKey.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{exe_name}", false); + if (name_node != null) + { + var keyValue = name_node.GetValue("Path"); + + if (keyValue == null) + { + //取 (默认) + keyValue = name_node.GetValue(""); + } + + if (keyValue != null) + { + // 值 可能 带双引号,去除双引号 + installDir = keyValue.ToString().Trim('"'); + if (!Directory.Exists(installDir)) + { + // 可能是文件路径,取目录 + installDir = Path.GetDirectoryName(installDir); + } + return installDir; + } + } + } + } + else + { + foreach (var regAddr in or_install_addrs) + { + var regSubKey = regKey.OpenSubKey(regAddr, false); + + foreach (var name in arr_name) + { + var name_node = regSubKey.OpenSubKey(name); + + if (name_node != null) + { + foreach (var keyName in or_get_key_names) + { + var keyValue = name_node.GetValue(keyName); + + if (keyValue != null) + { + // 值 可能 带双引号,去除双引号 + installDir = keyValue.ToString().Trim('"'); + if (!Directory.Exists(installDir)) + { + // 可能是文件路径,取目录 + installDir = Path.GetDirectoryName(installDir); + } + return installDir; + } + } + } + } + + } + } + } + catch(Exception ex) + { + LogUtils.LogError(nameof(SearchInstallDir), Developer.SysDefault, ex); + } + finally + { + regKey?.Close(); + } + return installDir; + } + + + [DllImport("shell32.dll")] + + public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2); + + + + /// + ///为文件夹添加users,everyone用户组的完全控制权限 + /// + /// + public static void AddSecurityControll2Folder(string dirPath) + { + //获取文件夹信息 + DirectoryInfo dir = new DirectoryInfo(dirPath); + if (dir.Exists) + { + //获得该文件夹的所有访问权限 + System.Security.AccessControl.DirectorySecurity dirSecurity = dir.GetAccessControl(AccessControlSections.All); + //设定文件ACL继承 + InheritanceFlags inherits = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit; + //添加ereryone用户组的访问权限规则 完全控制权限 + FileSystemAccessRule everyoneFileSystemAccessRule = new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow); + //添加Users用户组的访问权限规则 完全控制权限 + FileSystemAccessRule usersFileSystemAccessRule = new FileSystemAccessRule("Users", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow); + bool isModified = false; + dirSecurity.ModifyAccessRule(AccessControlModification.Add, everyoneFileSystemAccessRule, out isModified); + dirSecurity.ModifyAccessRule(AccessControlModification.Add, usersFileSystemAccessRule, out isModified); + //设置访问权限 + dir.SetAccessControl(dirSecurity); + } + } + + /// + /// 添加或修改配置连接 + /// + /// + public static void Save(NameValueCollection dbCHMConfig) + { + db.Save(dbCHMConfig, "DBCHMConfig"); + } + + public static void UpLastModified(int id) + { + db.ExecSql("update DBCHMConfig set Modified='" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "' where id=" + id); + } + + /// + /// 删除连接 + /// + /// + public static void Delete(int id) + { + db.Delete("DBCHMConfig", "Id", id); + } + + /// + /// 查询出所有配置的连接 + /// + /// + public static List SelectAll() + { + return db.GetDataTable("select * from DBCHMConfig order by Modified desc ").ConvertToListObject(); + } + + /// + /// 得到其中1个连接 + /// + /// + /// + public static DBCHMConfig Get(int id) + { + return db.GetDataTable("select * from DBCHMConfig where id = " + id).ConvertToListObject().FirstOrDefault(); + } + + /// + /// 判断配置表是否存在连接字符串 + /// + /// + public static bool HasValue() + { + string strSql = "select count(1) from DBCHMConfig"; + return db.Single(strSql, 0) > 0; + } + + } +} diff --git a/DocTools/DBCHMConfig.cs b/DocTools/DBCHMConfig.cs new file mode 100644 index 0000000..c59a49c --- /dev/null +++ b/DocTools/DBCHMConfig.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools +{ + public class DBCHMConfig + { + [DisplayName("ID")] + public int Id { get; set; } + + [DisplayName("连接名")] + public string Name { get; set; } + + [DisplayName("数据库类型")] + public string DBType { get; set; } + + [DisplayName("主机")] + public string Server { get; set; } + + [DisplayName("端口")] + public int? Port { get; set; } + [DisplayName("数据库")] + public string DBName { get; set; } + + [DisplayName("用户名")] + public string Uid { get; set; } + + [DisplayName("密码")] + public string Pwd { get; set; } + + [DisplayName("连接超时")] + public int? ConnTimeOut { get; set; } + + [DisplayName("连接字符串")] + public string ConnString { get; set; } + + [DisplayName("最后使用时间")] + public DateTime Modified { get; set; } + } +} diff --git a/DocTools/DBDoc/ChmDoc.cs b/DocTools/DBDoc/ChmDoc.cs new file mode 100644 index 0000000..edb5835 --- /dev/null +++ b/DocTools/DBDoc/ChmDoc.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using DocTools.Dtos; +using ZetaLongPaths; + +namespace DocTools.DBDoc +{ + public class ChmDoc : Doc + { + public ChmDoc(DBDto dto, string filter = "chm files (*.chm)|*.chm") : base(dto, filter) + { + + } + + private Encoding CurrEncoding + { + get + { + return Encoding.GetEncoding("gbk"); + } + } + + private string HHCPath + { + get + { + var hhcPath = string.Empty; + var hhwDir = ConfigUtils.SearchInstallDir("HTML Help Workshop", "hhw.exe"); + if (!string.IsNullOrWhiteSpace(hhwDir) && ZlpIOHelper.DirectoryExists(hhwDir)) + { + hhcPath = Path.Combine(hhwDir, "hhc.exe"); + } + return hhcPath; + } + } + + void InitDirFiles() + { + var dirNames = new string[] { + "表结构", + "视图", + "存储过程", + //"函数", + "resources\\js" + }; + + foreach (var name in dirNames) + { + var tmpDir = Path.Combine(this.WorkTmpDir, name); + + if (ZlpIOHelper.DirectoryExists(tmpDir)) + { + ZlpIOHelper.DeleteDirectory(tmpDir, true); + } + ZlpIOHelper.CreateDirectory(tmpDir); + } + + + var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TplFile\\chm\\"); + + var files = Directory.GetFiles(dir, "*.js", SearchOption.AllDirectories); + + foreach (var filePath in files) + { + var fileName = Path.GetFileName(filePath); + ZlpIOHelper.CopyFile(filePath, Path.Combine(this.WorkTmpDir, "resources\\js\\", fileName), true); + } + } + + public override void Build(string filePath) + { + #region 使用 HTML Help Workshop 的 hhc.exe 编译 ,先判断系统中是否已经安装有 HTML Help Workshop + + if (this.HHCPath.IsNullOrWhiteSpace()) + { + string htmlhelpPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "htmlhelp.exe"); + + if (File.Exists(htmlhelpPath)) + { + if (MessageBox.Show("导出CHM文档需安装 HTML Help Workshop ,是否现在安装?","提示", + MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK) + { + var proc = Process.Start(htmlhelpPath); + } + } + return; + } + + #endregion + + this.InitDirFiles(); + + var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TplFile\\chm"); + + var hhc_tpl = File.ReadAllText(Path.Combine(dir, "hhc.cshtml"), CurrEncoding); + var hhk_tpl = File.ReadAllText(Path.Combine(dir, "hhk.cshtml"), CurrEncoding); + var hhp_tpl = File.ReadAllText(Path.Combine(dir, "hhp.cshtml"), CurrEncoding); + var list_tpl = File.ReadAllText(Path.Combine(dir, "list.cshtml"), CurrEncoding); + var table_tpl = File.ReadAllText(Path.Combine(dir, "table.cshtml"), CurrEncoding); + var sqlcode_tpl = File.ReadAllText(Path.Combine(dir, "sqlcode.cshtml"), CurrEncoding); + + var hhc = hhc_tpl.RazorRender(this.Dto).Replace("", ""); + + var hhk = hhk_tpl.RazorRender(this.Dto).Replace("", ""); + + ZlpIOHelper.WriteAllText(Path.Combine(this.WorkTmpDir, "chm.hhc"), hhc, CurrEncoding); + + ZlpIOHelper.WriteAllText(Path.Combine(this.WorkTmpDir, "chm.hhk"), hhk, CurrEncoding); + + ZlpIOHelper.WriteAllText(Path.Combine(this.WorkTmpDir, "数据库目录.html"), list_tpl.RazorRender(this.Dto), CurrEncoding); + + foreach (var tab in this.Dto.Tables) + { + var tab_path = Path.Combine(this.WorkTmpDir, "表结构", $"{tab.TableName} {tab.Comment}.html"); + var content = table_tpl.RazorRender(tab); + ZlpIOHelper.WriteAllText(tab_path, content, CurrEncoding); + } + + + foreach (var item in Dto.Views) + { + var vw_path = Path.Combine(this.WorkTmpDir, "视图", $"{item.Key}.html"); + var content = sqlcode_tpl.RazorRender( + new SqlCode() { DBType = Dto.DBType, CodeName = item.Key, Content = item.Value.Trim() } + ); + ZlpIOHelper.WriteAllText(vw_path, content, CurrEncoding); + } + + + foreach (var item in Dto.Procs) + { + var proc_path = Path.Combine(this.WorkTmpDir, "存储过程", $"{item.Key}.html"); + var content = sqlcode_tpl.RazorRender( + new SqlCode() { DBType = Dto.DBType, CodeName = item.Key, Content = item.Value.Trim() } + ); + ZlpIOHelper.WriteAllText(proc_path, content, CurrEncoding); + } + + var hhp_Path = Path.Combine(this.WorkTmpDir, "chm.hhp"); + + ZlpIOHelper.WriteAllText(hhp_Path, hhp_tpl.RazorRender(new ChmHHP(filePath, this.WorkTmpDir)), CurrEncoding); + + + string res = StartRun(HHCPath, hhp_Path, Encoding.GetEncoding("gbk")); + + ZlpIOHelper.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log", "chm.log"), res); + } + + private string StartRun(string hhcPath, string arguments, Encoding encoding) + { + string str = ""; + ProcessStartInfo startInfo = new ProcessStartInfo() + { + FileName = hhcPath, //调入HHC.EXE文件 + Arguments = arguments, + WindowStyle = ProcessWindowStyle.Hidden, + RedirectStandardOutput = true, + UseShellExecute = false, + RedirectStandardError = true, + CreateNoWindow = true, + StandardErrorEncoding = encoding, + StandardOutputEncoding = encoding + }; + + using (Process process = Process.Start(startInfo)) + { + using (StreamReader reader = process.StandardOutput) + { + str = reader.ReadToEnd(); + } + process.WaitForExit(); + } + return str.Trim(); + } + + } +} diff --git a/DocTools/DBDoc/Doc.cs b/DocTools/DBDoc/Doc.cs new file mode 100644 index 0000000..fd8a78c --- /dev/null +++ b/DocTools/DBDoc/Doc.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using DocTools.Dtos; +using ZetaLongPaths; + +namespace DocTools.DBDoc +{ + public abstract class Doc + { + /// + /// 当前应用程序的名称 => DBCHM + /// + private static string ConfigFileName = Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName).Replace(".vshost", ""); + + /// + /// 定义配置存放的路径 => C:\Users\用户名\AppData\Local\DBCHM + /// + public static string AppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), ConfigFileName); + + + public Doc(DBDto dto, string filter) + { + this.Dto = dto; + this.Filter = filter; + this.WorkTmpDir = Path.Combine(AppPath, dto.DBType + "_" + dto.DBName); + if (ZlpIOHelper.DirectoryExists(this.WorkTmpDir)) + { + ZlpIOHelper.DeleteDirectory(this.WorkTmpDir, true); + } + ZlpIOHelper.CreateDirectory(this.WorkTmpDir); + this.Ext = this.Filter.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()?.Trim('*'); + } + + /// + /// 临时文件的存放目录 + /// + public string WorkTmpDir { get; private set; } + + /// + /// 文件扩展名 + /// + public string Ext { get; set; } + + /// + /// 数据库Dto + /// + public DBDto Dto { get; } + + /// + /// 扩展名过滤字符串 + /// + public string Filter { get; } + + /// + /// 构建生成文档 + /// + public abstract void Build(string filePath); + } +} diff --git a/DocTools/DBDoc/ExcelDoc.cs b/DocTools/DBDoc/ExcelDoc.cs new file mode 100644 index 0000000..6f62a2a --- /dev/null +++ b/DocTools/DBDoc/ExcelDoc.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DocTools.Dtos; + +namespace DocTools.DBDoc +{ + public class ExcelDoc : Doc + { + public ExcelDoc(DBDto dto, string filter = "excel files (.xlsx)|*.xlsx") : base(dto, filter) + { + } + + public override void Build(string filePath) + { + ExcelUtils.ExportExcelByEpplus(filePath, this.Dto); + } + } + + /// + /// Excel处理工具类 + /// + internal static class ExcelUtils + { + + /// + /// 引用EPPlus.dll导出excel数据库字典文档 + /// + /// + /// + /// + public static void ExportExcelByEpplus(string fileName, DBDto dto) + { + var tables = dto.Tables; + + System.IO.FileInfo xlsFileInfo = new System.IO.FileInfo(fileName); + + if (xlsFileInfo.Exists) + { + // 注意此处:存在Excel文档即删除再创建一个 + xlsFileInfo.Delete(); + xlsFileInfo = new System.IO.FileInfo(fileName); + } + // 创建并添加Excel文档信息 + using (OfficeOpenXml.ExcelPackage epck = new OfficeOpenXml.ExcelPackage(xlsFileInfo)) + { + // 创建overview sheet + CreateLogSheet(epck, AppConst.LOG_CHAPTER_NAME, tables); + + // 创建overview sheet + CreateOverviewSheet(epck, AppConst.TABLE_CHAPTER_NAME, tables); + + // 创建tables sheet + CreateTableSheet(epck, AppConst.TABLE_STRUCTURE_CHAPTER_NAME, tables); + + epck.Save(); // 保存excel + epck.Dispose(); + } + } + + /// + /// 创建修订日志sheet + /// + /// + /// + /// + private static void CreateLogSheet(OfficeOpenXml.ExcelPackage epck, string sheetName, List tables) + { + OfficeOpenXml.ExcelWorksheet overviewTbWorksheet = epck.Workbook.Worksheets.Add(sheetName); + + int row = 1; + + overviewTbWorksheet.Cells[row, 1, row, 5].Merge = true; + //overviewTbWorksheet.Cells[row, 1].Value = "总表数量"; + //overviewTbWorksheet.Cells[row, 2].Value = tables.Count + ""; + //overviewTbWorksheet.Cells[row, 4].Value = "密码等级"; + //overviewTbWorksheet.Cells[row, 5].Value = "秘密"; + + row++; // 行号+1 + + overviewTbWorksheet.Cells[row, 1].Value = "版本号"; + overviewTbWorksheet.Cells[row, 2].Value = "修订日期"; + overviewTbWorksheet.Cells[row, 3].Value = "修订内容"; + overviewTbWorksheet.Cells[row, 4].Value = "修订人"; + overviewTbWorksheet.Cells[row, 5].Value = "审核人"; + overviewTbWorksheet.Cells[row, 1, row, 5].Style.Font.Bold = true; + overviewTbWorksheet.Cells[row, 1, row, 5].Style.Font.Size = 10; + overviewTbWorksheet.Row(1).Height = 20; // 行高 + + // 循环日志记录 + row++; // 行号+1 + for (var i = 0; i < 16; i++) + { + // 添加列标题 + overviewTbWorksheet.Cells[row, 1].Value = ""; + overviewTbWorksheet.Cells[row, 2].Value = ""; + overviewTbWorksheet.Cells[row, 3].Value = ""; + overviewTbWorksheet.Cells[row, 4].Value = ""; + overviewTbWorksheet.Cells[row, 5].Value = ""; + + overviewTbWorksheet.Row(row).Height = 20; // 行高 + + row++; // 行号+1 + } + // 水平居中 + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + // 垂直居中 + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center; + // 上下左右边框线 + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 5].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Column(1).Width = 25; + overviewTbWorksheet.Column(2).Width = 25; + overviewTbWorksheet.Column(3).Width = 50; + overviewTbWorksheet.Column(4).Width = 25; + overviewTbWorksheet.Column(5).Width = 25; + } + + /// + /// 创建表目录sheet + /// + /// + /// + /// + private static void CreateOverviewSheet(OfficeOpenXml.ExcelPackage epck, string sheetName, List tables) + { + OfficeOpenXml.ExcelWorksheet overviewTbWorksheet = epck.Workbook.Worksheets.Add(sheetName); + + int row = 1; + overviewTbWorksheet.Cells[row, 1].Value = "序号"; + overviewTbWorksheet.Cells[row, 2].Value = "表名"; + overviewTbWorksheet.Cells[row, 3].Value = "注释/说明"; + overviewTbWorksheet.Cells[row, 1, row, 3].Style.Font.Bold = true; + overviewTbWorksheet.Cells[row, 1, row, 3].Style.Font.Size = 16; + overviewTbWorksheet.Row(1).Height = 30; // 行高 + + // 循环数据库表名 + row++; + foreach (var table in tables) + { + // 数据库名称 + // 添加列标题 + overviewTbWorksheet.Cells[row, 1].Value = table.TableOrder; + overviewTbWorksheet.Cells[row, 2].Value = table.TableName; + overviewTbWorksheet.Cells[row, 3].Value = (!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : ""); + + overviewTbWorksheet.Row(row).Height = 30; // 行高 + + row++; // 行号+1 + } + // 水平居中 + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + // 垂直居中 + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center; + // 上下左右边框线 + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Cells[1, 1, row - 1, 3].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + overviewTbWorksheet.Column(1).Width = 10; + overviewTbWorksheet.Column(2).Width = 50; + overviewTbWorksheet.Column(3).Width = 50; + } + + /// + /// 创建表结构sheet + /// + /// + /// + /// + private static void CreateTableSheet(OfficeOpenXml.ExcelPackage epck, string sheetName, List tables) + { + OfficeOpenXml.ExcelWorksheet tbWorksheet = epck.Workbook.Worksheets.Add(sheetName); + int rowNum = 1, fromRow = 0, count = 0; // 行号计数器 + // 循环数据库表名 + foreach (var table in tables) + { + var lstName = new List + { + "序号","列名","数据类型","长度","小数位","主键","自增","允许空","默认值","列说明" + }; + + //oracle不显示 列是否自增 + if (table.DBType.StartsWith("Oracle")) + { + lstName.Remove("自增"); + } + + var spColCount = lstName.Count; + + // 数据库名称 + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Merge = true; + tbWorksheet.Cells[rowNum, 1].Value = table.TableName + " " + (!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : ""); + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Font.Bold = true; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Font.Size = 16; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center; + + // 注意:保存起始行号 + fromRow = rowNum; + + rowNum++; // 行号+1 + + // tbWorksheet.Cells[int FromRow, int FromCol, int ToRow, int ToCol] + // 列标题字体为粗体 + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Font.Bold = true; + + + + // 添加列标题 + for (int j = 0; j < lstName.Count; j++) + { + tbWorksheet.Cells[rowNum, j + 1].Value = lstName[j]; + } + + rowNum++; // 行号+1 + + // 添加数据行,遍历数据库表字段 + foreach (var column in table.Columns) + { + tbWorksheet.Cells[rowNum, 1].Value = column.ColumnOrder; + tbWorksheet.Cells[rowNum, 2].Value = column.ColumnName; + tbWorksheet.Cells[rowNum, 3].Value = column.ColumnTypeName; + tbWorksheet.Cells[rowNum, 4].Value = column.Length; + tbWorksheet.Cells[rowNum, 5].Value = column.Scale; + tbWorksheet.Cells[rowNum, 6].Value = column.IsPK; + + //oracle不显示 列是否自增 + if (table.DBType.StartsWith("Oracle")) + { + tbWorksheet.Cells[rowNum, 7].Value = column.CanNull; + tbWorksheet.Cells[rowNum, 8].Value = column.DefaultVal; + tbWorksheet.Cells[rowNum, 9].Value = column.Comment; + } + else + { + tbWorksheet.Cells[rowNum, 7].Value = column.IsIdentity; + tbWorksheet.Cells[rowNum, 8].Value = column.CanNull; + tbWorksheet.Cells[rowNum, 9].Value = column.DefaultVal; + tbWorksheet.Cells[rowNum, 10].Value = column.Comment; + } + rowNum++; // 行号+1 + } + + // 水平居中 + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + // 垂直居中 + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center; + // 上下左右边框线 + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[fromRow, 1, rowNum - 1, spColCount].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + + // 处理空白行,分割用 + if (count < tables.Count - 1) + { + //tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + //tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + tbWorksheet.Cells[rowNum, 1, rowNum, spColCount].Merge = true; + tbWorksheet.Cells[rowNum, 1, rowNum, 1].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid; + tbWorksheet.Cells[rowNum, 1, rowNum, 1].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.DodgerBlue); + } + + rowNum++; // 行号+1 + + count++; // 计数器+1 + } + + // 设置表格样式 + tbWorksheet.Cells.Style.WrapText = true; // 自动换行 + tbWorksheet.Cells.Style.ShrinkToFit = true; // 单元格自动适应大小 + } + + } +} diff --git a/DocTools/DBDoc/HtmlDoc.cs b/DocTools/DBDoc/HtmlDoc.cs new file mode 100644 index 0000000..cc25e0c --- /dev/null +++ b/DocTools/DBDoc/HtmlDoc.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DocTools.Dtos; +using ZetaLongPaths; + +namespace DocTools.DBDoc +{ + public class HtmlDoc : Doc + { + public HtmlDoc(DBDto dto, string filter = "html files (*.html)|*.html") : base(dto, filter) + { + } + + public override void Build(string filePath) + { + var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TplFile\\html"); + var html_tpl = File.ReadAllText(Path.Combine(dir, "html.cshtml"), Encoding.UTF8); + var html_doc = html_tpl.RazorRender(this.Dto); + ZlpIOHelper.WriteAllText(filePath, html_doc, Encoding.UTF8); + } + } +} diff --git a/DocTools/DBDoc/MarkDownDoc.cs b/DocTools/DBDoc/MarkDownDoc.cs new file mode 100644 index 0000000..cb7c065 --- /dev/null +++ b/DocTools/DBDoc/MarkDownDoc.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using DocTools.Dtos; +using ZetaLongPaths; + +namespace DocTools.DBDoc +{ + public class MarkDownDoc : Doc + { + public MarkDownDoc(DBDto dto, string filter = "markdown files (.md)|*.md") : base(dto, filter) + { + } + + public override void Build(string filePath) + { + var sb = new StringBuilder(); + sb.AppendLine("# 数据库表目录"); + var dirMD = this.Dto.Tables.MarkDown("Columns", "DBType"); + dirMD = Regex.Replace(dirMD, @"(.+?\|\s+)([a-zA-Z][a-zA-Z0-9_]+)(\s+\|.+\n?)", $"$1[$2](#$2)$3", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + sb.Append(dirMD); + sb.AppendLine(); + + if (this.Dto.Tables.Any()) + { + sb.Append("## 表结构"); + foreach (var dto in this.Dto.Tables) + { + sb.AppendLine(); + sb.AppendLine($"### {dto.TableName} {dto.Comment}"); + + if (dto.DBType.StartsWith("Oracle")) + { + sb.Append(dto.Columns.MarkDown("IsIdentity")); + } + else + { + sb.Append(dto.Columns.MarkDown()); + } + + sb.AppendLine(); + } + } + + if (this.Dto.Views.Any()) + { + sb.Append("## 视图"); + foreach (var item in this.Dto.Views) + { + sb.AppendLine(); + sb.AppendLine($"### {item.Key}"); + sb.AppendLine("``` sql"); + var fmtSql = JS.RunFmtSql(item.Value, this.Dto.DBType); + sb.Append(fmtSql); + sb.AppendLine("```"); + sb.AppendLine(); + } + } + + if (this.Dto.Procs.Any()) + { + sb.Append("## 存储过程"); + foreach (var item in this.Dto.Procs) + { + sb.AppendLine(); + sb.AppendLine($"### {item.Key}"); + sb.AppendLine("``` sql"); + var fmtSql = JS.RunFmtSql(item.Value, this.Dto.DBType); + sb.Append(fmtSql); + sb.AppendLine("```"); + sb.AppendLine(); + } + } + var md = sb.ToString(); + + ZlpIOHelper.WriteAllText(filePath, md, Encoding.UTF8); + } + } +} diff --git a/DocTools/DBDoc/PdfDoc.cs b/DocTools/DBDoc/PdfDoc.cs new file mode 100644 index 0000000..3e255d1 --- /dev/null +++ b/DocTools/DBDoc/PdfDoc.cs @@ -0,0 +1,327 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DocTools.Dtos; +using iTextSharp.text; +using iTextSharp.text.pdf; +using ZetaLongPaths; + +namespace DocTools.DBDoc +{ + public class PdfDoc : Doc + { + public PdfDoc(DBDto dto, string filter = "html files (.pdf)|*.pdf") : base(dto, filter) + { + } + + private static string TTF_Path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TplFile\\pdf\\msyh.ttf"); + + public override void Build(string filePath) + { + PdfUtils.ExportPdfByITextSharp(filePath, TTF_Path, this.Dto); + } + } + + /// + /// Pdf处理工具类 + /// + internal static class PdfUtils + { + + /// + /// 引用iTextSharp.dll导出pdf数据库字典文档 + /// + /// + /// + public static void ExportPdfByITextSharp(string fileName, string fontPath, DBDto dto) + { + var databaseName = dto.DBName; + var tables = dto.Tables; + // 创建并添加文档信息 + Document pdfDocument = new Document(); + pdfDocument.AddTitle(fileName); + + PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, + new System.IO.FileStream(fileName, System.IO.FileMode.Create)); + pdfDocument.Open(); // 打开文档 + + // 标题 + Paragraph title = new Paragraph("数据库字典文档\n\n", BaseFont(fontPath, 30, Font.BOLD)); + title.Alignment = Element.ALIGN_CENTER; + pdfDocument.Add(title); + Paragraph subTitle = new Paragraph(" —— " + databaseName, BaseFont(fontPath, 20, Font.NORMAL)); + subTitle.Alignment = Element.ALIGN_CENTER; + pdfDocument.Add(subTitle); + + // PDF换页 + pdfDocument.NewPage(); + + // 创建添加书签章节 + int chapterNum = 1; + // 全局字体设置,处理iTextSharp中文不识别显示问题 + Font pdfFont = BaseFont(fontPath, 12, Font.NORMAL); + + // log table + Chapter logChapter = new Chapter(new Paragraph(AppConst.LOG_CHAPTER_NAME, pdfFont), chapterNum); + pdfDocument.Add(logChapter); + pdfDocument.Add(new Paragraph("\n", pdfFont)); // 换行 + CreateLogTable(pdfDocument, pdfFont, tables); + // PDF换页 + pdfDocument.NewPage(); + + // overview table + Chapter dirChapter = new Chapter(new Paragraph(AppConst.TABLE_CHAPTER_NAME, pdfFont), (++chapterNum)); + pdfDocument.Add(dirChapter); + pdfDocument.Add(new Paragraph("\n", pdfFont)); // 换行 + CreateOverviewTable(pdfDocument, pdfFont, tables); + // PDF换页 + pdfDocument.NewPage(); + + // table structure + // 添加书签章节 + Chapter tableChapter = new Chapter(new Paragraph(AppConst.TABLE_STRUCTURE_CHAPTER_NAME, pdfFont), (++chapterNum)); + tableChapter.BookmarkOpen = true; + pdfDocument.Add(tableChapter); + pdfDocument.Add(new Paragraph("\n", pdfFont)); // 换行 + + foreach (var table in tables) + { + string docTableName = table.TableName + " " + (!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : ""); + // 添加书签章节 + Section selection = tableChapter.AddSection(20f, new Paragraph(docTableName, pdfFont), chapterNum); + pdfDocument.Add(selection); + pdfDocument.Add(new Paragraph("\n", pdfFont)); // 换行 + + // 遍历数据库表 + // 创建表格 + PdfPTable pdfTable = null; + if (!table.DBType.StartsWith("Oracle")) + { + pdfTable = new PdfPTable(10); + } + else + { + pdfTable = new PdfPTable(9); + } + + // 添加列标题 + pdfTable.AddCell(CreatePdfPCell("序号", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("列名", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("数据类型", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("长度", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("小数位", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("主键", pdfFont)); + + if (!table.DBType.StartsWith("Oracle")) + { + pdfTable.AddCell(CreatePdfPCell("自增", pdfFont)); + } + + pdfTable.AddCell(CreatePdfPCell("允许空", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("默认值", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("列说明", pdfFont)); + // 添加数据行,循环数据库表字段 + foreach (var column in table.Columns) + { + pdfTable.AddCell(CreatePdfPCell(column.ColumnOrder, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.ColumnName, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.ColumnTypeName, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.Length, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.Scale, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.IsPK, pdfFont)); + + if (!table.DBType.StartsWith("Oracle")) + { + pdfTable.AddCell(CreatePdfPCell(column.IsIdentity, pdfFont)); + } + + pdfTable.AddCell(CreatePdfPCell(column.CanNull, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.DefaultVal, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(column.Comment, pdfFont)); + } + + // 设置表格居中 + pdfTable.HorizontalAlignment = Element.ALIGN_CENTER; + pdfTable.TotalWidth = 520F; + pdfTable.LockedWidth = true; + + if (!table.DBType.StartsWith("Oracle")) + { + pdfTable.SetWidths(new float[] { 50F, 60F, 60F, 50F, 50F, 50F, 50F, 50F, 50F, 50F }); + } + else + { + pdfTable.SetWidths(new float[] { 50F, 80F, 70F, 50F, 50F, 50F, 50F, 50F, 70F }); + } + + + // 添加表格 + pdfDocument.Add(pdfTable); + + // PDF换页 + pdfDocument.NewPage(); + } + + + if (dto.Views.Count > 0) + { + Chapter viewChapter = new Chapter(new Paragraph("视图", pdfFont), (++chapterNum)); + viewChapter.BookmarkOpen = true; + pdfDocument.Add(viewChapter); + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + + foreach (var item in dto.Views) + { + Section selection = viewChapter.AddSection(20f, new Paragraph(item.Key, pdfFont), chapterNum); + pdfDocument.Add(selection); + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + + Paragraph pgh = new Paragraph(item.Value.Replace("`", ""), pdfFont); + pdfDocument.Add(pgh); + + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + } + pdfDocument.NewPage(); + } + + + if (dto.Procs.Count > 0) + { + Chapter procChapter = new Chapter(new Paragraph("存储过程", pdfFont), (++chapterNum)); + procChapter.BookmarkOpen = true; + pdfDocument.Add(procChapter); + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + + foreach (var item in dto.Procs) + { + Section selection = procChapter.AddSection(20f, new Paragraph(item.Key, pdfFont), chapterNum); + pdfDocument.Add(selection); + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + + Paragraph pgh = new Paragraph(item.Value.Replace("`",""), pdfFont); + pdfDocument.Add(pgh); + + // 换行 + pdfDocument.Add(new Paragraph("\n", pdfFont)); + + } + pdfDocument.NewPage(); + } + + // 关闭释放PDF文档资源 + pdfDocument.Close(); + } + + /// + /// create log table + /// + /// + /// + /// + private static void CreateLogTable(Document pdfDocument, Font pdfFont, List tables) + { + // 创建表格 + PdfPTable pdfTable = new PdfPTable(5); + + // 添加列标题 + pdfTable.AddCell(CreatePdfPCell("版本号", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("修订日期", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("修订内容", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("修订人", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("审核人", pdfFont)); + for (var i = 0; i < 16; i++) + { + // 添加数据行,循环数据库表字段 + pdfTable.AddCell(CreatePdfPCell("", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("", pdfFont)); + } + + // 设置表格居中 + pdfTable.HorizontalAlignment = Element.ALIGN_CENTER; + pdfTable.TotalWidth = 540F; + pdfTable.LockedWidth = true; + pdfTable.SetWidths(new float[] { 80F, 100F, 200F, 80F, 80F }); + + // 添加表格 + pdfDocument.Add(pdfTable); + } + + /// + /// create overview table + /// + /// + /// + /// + private static void CreateOverviewTable(Document pdfDocument, Font pdfFont, List tables) + { + // 创建表格 + PdfPTable pdfTable = new PdfPTable(3); + + // 添加列标题 + pdfTable.AddCell(CreatePdfPCell("序号", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("表名", pdfFont)); + pdfTable.AddCell(CreatePdfPCell("注释/说明", pdfFont)); + foreach (var table in tables) + { + // 添加数据行,循环数据库表字段 + pdfTable.AddCell(CreatePdfPCell(table.TableOrder, pdfFont)); + pdfTable.AddCell(CreatePdfPCell(table.TableName, pdfFont)); + pdfTable.AddCell(CreatePdfPCell((!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : ""), pdfFont)); + } + + // 设置表格居中 + pdfTable.HorizontalAlignment = Element.ALIGN_CENTER; + pdfTable.TotalWidth = 330F; + pdfTable.LockedWidth = true; + pdfTable.SetWidths(new float[] { 60F, 120F, 150F }); + + // 添加表格 + pdfDocument.Add(pdfTable); + } + + /// + /// 创建pdf表格单元格 + /// + /// + /// + /// + private static PdfPCell CreatePdfPCell(string text, Font pdfFont) + { + Phrase phrase = new Phrase(text, pdfFont); + PdfPCell pdfPCell = new PdfPCell(phrase); + + // 单元格垂直居中显示 + pdfPCell.HorizontalAlignment = Element.ALIGN_CENTER; + pdfPCell.VerticalAlignment = Element.ALIGN_MIDDLE; + + pdfPCell.MinimumHeight = 30; + + return pdfPCell; + } + + /// + /// iTextSharp字体设置 + /// + /// + /// + /// + private static Font BaseFont(string fontPath, float fontSize, int fontStyle) + { + BaseFont chinese = iTextSharp.text.pdf.BaseFont.CreateFont(fontPath, iTextSharp.text.pdf.BaseFont.IDENTITY_H, true); + Font pdfFont = new Font(chinese, fontSize, fontStyle); + return pdfFont; + } + + } +} diff --git a/DocTools/DBDoc/WordDoc.cs b/DocTools/DBDoc/WordDoc.cs new file mode 100644 index 0000000..f3b4c44 --- /dev/null +++ b/DocTools/DBDoc/WordDoc.cs @@ -0,0 +1,511 @@ +using System.Collections.Generic; +using Aspose.Words.Tables; +using DocTools.Dtos; + +namespace DocTools.DBDoc +{ + public class WordDoc : Doc + { + public WordDoc(DBDto dto, string filter = "word files (.doc)|*.doc") : base(dto, filter) + { + } + + public override void Build(string filePath) + { + WordUtils.ExportWordByAsposeWords(filePath, this.Dto); + } + } + + /// + /// Word处理工具类 + /// + internal static class WordUtils + { + private static string asposeBookmark_prefix = "AsposeBookmark"; + private static string asposeBookmarkLog = "asposeBookmarkLog"; + private static string asposeBookmarkOverview = "asposeBookmarkOverview"; + + /// + /// 引用Aspose.Words.dll导出word数据库字典文档 + /// 注意:不依赖微软office办公软件 + /// + /// + /// + public static void ExportWordByAsposeWords(string fileName, DBDto dto) + { + var databaseName = dto.DBName; + var tables = dto.Tables; + + Aspose.Words.Document doc = new Aspose.Words.Document(); + + // TODO document properties + doc.BuiltInDocumentProperties.Subject = "设计文档"; + doc.BuiltInDocumentProperties.ContentType = "数据库字典"; + doc.BuiltInDocumentProperties.Title = "数据库字典文档"; + doc.BuiltInDocumentProperties.Author = doc.BuiltInDocumentProperties.LastSavedBy = doc.BuiltInDocumentProperties.Manager = "trycache"; + doc.BuiltInDocumentProperties.Company = "51Try.Top"; + doc.BuiltInDocumentProperties.Version = doc.BuiltInDocumentProperties.RevisionNumber = 1; + doc.BuiltInDocumentProperties.ContentStatus = "初稿"; + doc.BuiltInDocumentProperties.NameOfApplication = "DBCHM"; + doc.BuiltInDocumentProperties.LastSavedTime = doc.BuiltInDocumentProperties.CreatedTime = System.DateTime.Now; + + // TODO header and footer setting + Aspose.Words.HeaderFooter header = new Aspose.Words.HeaderFooter(doc, Aspose.Words.HeaderFooterType.HeaderPrimary); + doc.FirstSection.HeadersFooters.Add(header); + // Add a paragraph with text to the header. + header.AppendParagraph("数智化合规系统字典").ParagraphFormat.Alignment = + Aspose.Words.ParagraphAlignment.Right; + + Aspose.Words.DocumentBuilder builder = new Aspose.Words.DocumentBuilder(doc); + + // TODO 创建文档标题书签 + CreateBookmark(builder, Aspose.Words.ParagraphAlignment.Center, Aspose.Words.OutlineLevel.Level1, 25, + asposeBookmark_prefix + "0", "数据库字典文档"); + builder.ParagraphFormat.OutlineLevel = Aspose.Words.OutlineLevel.BodyText; + builder.Writeln("—— " + databaseName); + + // TODO 换行 + builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak); + builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak); + builder.InsertBreak(Aspose.Words.BreakType.ParagraphBreak); + + // TODO 数据库字典文档修订日志表 + CreateBookmark(builder, Aspose.Words.ParagraphAlignment.Center, Aspose.Words.OutlineLevel.Level2, 16, + asposeBookmarkLog, AppConst.LOG_CHAPTER_NAME); + CreateLogTable(builder); + builder.InsertBreak(Aspose.Words.BreakType.PageBreak); + + // TODO 创建数据库字典文档数据库概况一览表 + CreateBookmark(builder, Aspose.Words.ParagraphAlignment.Center, Aspose.Words.OutlineLevel.Level2, 16, + asposeBookmarkOverview, AppConst.TABLE_CHAPTER_NAME); + CreateOverviewTable(builder, tables); + builder.InsertBreak(Aspose.Words.BreakType.PageBreak); + + // TODO 创建书签 + CreateBookmark(builder, Aspose.Words.ParagraphAlignment.Left, Aspose.Words.OutlineLevel.Level2, 16, + asposeBookmark_prefix + 1, AppConst.TABLE_STRUCTURE_CHAPTER_NAME); + + int i = 1; // 计数器 + // TODO 遍历数据库表集合 + foreach (var table in tables) + { + string bookmarkName = table.TableName + " " + (!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : ""); + + // TODO 创建书签 + CreateBookmark(builder, Aspose.Words.ParagraphAlignment.Left, Aspose.Words.OutlineLevel.Level3, 16, + asposeBookmark_prefix + i, table.TableOrder + "、" + bookmarkName); + + // TODO 遍历数据库表字段集合 + // TODO 创建表格 + Aspose.Words.Tables.Table asposeTable = builder.StartTable(); + + // 清除段落样式 + builder.ParagraphFormat.ClearFormatting(); + + #region 表格列设置,列标题,列宽,字体等 + // Make the header row. + builder.InsertCell(); + // Set the left indent for the table. Table wide formatting must be applied after + // at least one row is present in the table. + asposeTable.Alignment = Aspose.Words.Tables.TableAlignment.Center; + asposeTable.PreferredWidth = PreferredWidth.FromPercent(100); + asposeTable.AllowAutoFit = false; + // Set height and define the height rule for the header row. + builder.RowFormat.Height = 25.0; + builder.RowFormat.HeightRule = Aspose.Words.HeightRule.AtLeast; + // Some special features for the header row. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.FromArgb(198, 217, 241); + builder.ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center; + builder.Font.Size = 10; + builder.Font.Name = "宋体"; + builder.Font.Bold = true; + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(8); + builder.Write("序号"); + + // We don't need to specify the width of this cell because it's inherited from the previous cell. + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(15); + builder.Write("字段名称"); + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(25); + builder.Write("字段描述"); + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(12); + builder.Write("数据类型"); + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(10); + builder.Write("长度"); + + //builder.InsertCell(); + //builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(8); + //builder.Write("小数位"); + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(10); + builder.Write("主键"); + + //if (!table.DBType.StartsWith("Oracle")) + //{ + // builder.InsertCell(); + // builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(7); + // builder.Write("自增"); + //} + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(10); + builder.Write("允许空"); + + builder.InsertCell(); + builder.CellFormat.PreferredWidth = PreferredWidth.FromPercent(8); + builder.Write("默认值"); + + + builder.EndRow(); + #endregion + + foreach (var column in table.Columns) + { + #region 遍历表格数据行写入 + // Set features for the other rows and cells. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.White; + builder.CellFormat.Width = 100.0; + builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center; + //builder.CellFormat.FitText = true; + // Reset height and define a different height rule for table body + builder.RowFormat.Height = 20.0; + builder.RowFormat.HeightRule = Aspose.Words.HeightRule.AtLeast; + builder.InsertCell(); + // Reset font formatting. + builder.Font.Size = 9; + builder.Font.Bold = false; + builder.Write(column.ColumnOrder); // 序号 + + builder.InsertCell(); + builder.Write(column.ColumnName); // 列名 + + builder.InsertCell(); + builder.Font.Size = 9; + builder.Write(column.Comment); // 列说明 + + builder.InsertCell(); + builder.Write(column.ColumnTypeName); // 数据类型 + + builder.InsertCell(); + builder.Write(column.Length); // 长度 + + //builder.InsertCell(); + //builder.Write(column.Scale); // 小数位 + + builder.InsertCell(); + builder.Write(column.IsPK); // 主键 + + //if (!table.DBType.StartsWith("Oracle")) + //{ + // builder.InsertCell(); + // builder.Write(column.IsIdentity); // 自增 + //} + + builder.InsertCell(); + builder.Write(column.CanNull); // 是否为空 + + builder.InsertCell(); + builder.Font.Size = 9; + builder.Write(column.DefaultVal); // 默认值 + + + + builder.EndRow(); + #endregion + } + + // TODO 表格创建完成,结束 + //asposeTable.PreferredWidth = Aspose.Words.Tables.PreferredWidth.Auto; + //asposeTable.AutoFit(Aspose.Words.Tables.AutoFitBehavior.AutoFitToContents); + builder.EndTable(); + + i++; + + // TODO page breaks + if (i < tables.Count) + { + builder.InsertBreak(Aspose.Words.BreakType.PageBreak); + } + } + + // TODO 生成页码 + AutoGenPageNum(doc, builder); + + // TODO 添加水印 + //InsertWatermarkText(doc, "DBCHM-51Try.Top"); + + doc.Save(fileName); + } + + /// + /// 生成页码 + /// + /// + public static void AutoGenPageNum(Aspose.Words.Document doc, Aspose.Words.DocumentBuilder builder) + { + Aspose.Words.HeaderFooter footer = new Aspose.Words.HeaderFooter(doc, Aspose.Words.HeaderFooterType.FooterPrimary); + doc.FirstSection.HeadersFooters.Add(footer); + // Add a paragraph with text to the footer. + footer.AppendParagraph("").ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center; + // We want to insert a field like this: {PAGE} / {NUMPAGES} + // TODO Go to the primary footer + builder.MoveToHeaderFooter(Aspose.Words.HeaderFooterType.FooterPrimary); + // TODO Add fields for current page number + builder.InsertField("PAGE"); + // TODO Add any custom text formatter + builder.Write(" / "); + // TODO Add field for total page numbers in document + builder.InsertField("NUMPAGES"); + // Finally update the outer field to recalcaluate the final value. + // Doing this will automatically update the inner fields at the same time. + // field.Update(); + } + + /// + /// 创建数据库字典文档修订日志表 + /// + /// + private static void CreateLogTable(Aspose.Words.DocumentBuilder builder) + { + // 清除段落样式 + builder.ParagraphFormat.ClearFormatting(); + + // TODO 创建表格 + Aspose.Words.Tables.Table logTable = builder.StartTable(); + + #region 表格列设置,列标题,列宽,字体等 + // Make the header row. + builder.InsertCell(); + // Set the left indent for the table. Table wide formatting must be applied after + // at least one row is present in the table. + logTable.Alignment = Aspose.Words.Tables.TableAlignment.Center; + logTable.AllowAutoFit = true; + // Set height and define the height rule for the header row. + builder.RowFormat.Height = 40.0; + builder.RowFormat.HeightRule = Aspose.Words.HeightRule.AtLeast; + // Some special features for the header row. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.FromArgb(198, 217, 241); + builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center; + builder.ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center; + builder.Font.Size = 14; + builder.Font.Name = "Arial"; + builder.Font.Bold = true; + builder.CellFormat.Width = 100.0; + builder.Write("版本号"); + + // We don't need to specify the width of this cell because it's inherited from the previous cell. + builder.InsertCell(); + builder.Write("修订日期"); + + builder.InsertCell(); + builder.Write("修订内容"); + + builder.InsertCell(); + builder.Write("修订人"); + + builder.InsertCell(); + builder.Write("审核人"); + + builder.EndRow(); + #endregion + + for (var i = 0; i < 5; i++) + { + #region 遍历表格数据行写入 + // Set features for the other rows and cells. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.White; + builder.CellFormat.Width = 100.0; + builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center; + // Reset height and define a different height rule for table body + builder.RowFormat.Height = 40.0; + builder.InsertCell(); + // Reset font formatting. + builder.Font.Size = 12; + builder.Font.Bold = false; + builder.Write(""); // 版本号 + + builder.InsertCell(); + builder.Write(""); // 修订日期 + + builder.InsertCell(); + builder.Write(""); // 修订内容 + + builder.InsertCell(); + builder.Write(""); // 修订人 + + builder.InsertCell(); + builder.Write(""); // 审核人 + + builder.EndRow(); + #endregion + } + // TODO 表格创建完成,结束 + builder.EndTable(); + } + + /// + /// 创建数据库字典文档数据库概况一览表 + /// + /// + /// + private static void CreateOverviewTable(Aspose.Words.DocumentBuilder builder, List tables) + { + // 清除段落样式 + builder.ParagraphFormat.ClearFormatting(); + + // TODO 创建表格 + Aspose.Words.Tables.Table overviewTable = builder.StartTable(); + + #region 表格列设置,列标题,列宽,字体等 + // Make the header row. + builder.InsertCell(); + // Set the left indent for the table. Table wide formatting must be applied after + // at least one row is present in the table. + overviewTable.Alignment = Aspose.Words.Tables.TableAlignment.Center; + overviewTable.AllowAutoFit = true; + // Set height and define the height rule for the header row. + builder.RowFormat.Height = 40.0; + builder.RowFormat.HeightRule = Aspose.Words.HeightRule.AtLeast; + // Some special features for the header row. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.FromArgb(198, 217, 241); + builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center; + builder.ParagraphFormat.Alignment = Aspose.Words.ParagraphAlignment.Center; + builder.Font.Size = 14; + builder.Font.Name = "Arial"; + builder.Font.Bold = true; + builder.CellFormat.Width = 100.0; + builder.Write("序号"); + + builder.InsertCell(); + builder.Write("表名"); + + builder.InsertCell(); + builder.Write("注释/说明"); + + builder.EndRow(); + #endregion + + // TODO 遍历数据库表集合 + foreach (var table in tables) + { + #region 遍历表格数据行写入 + // Set features for the other rows and cells. + builder.CellFormat.Shading.BackgroundPatternColor = System.Drawing.Color.White; + builder.CellFormat.Width = 100.0; + builder.CellFormat.VerticalAlignment = Aspose.Words.Tables.CellVerticalAlignment.Center; + // Reset height and define a different height rule for table body + builder.RowFormat.Height = 40.0; + builder.InsertCell(); + // Reset font formatting. + builder.Font.Size = 12; + builder.Font.Bold = false; + builder.Write(table.TableOrder); // 序号 + + builder.InsertCell(); + builder.Write(table.TableName); // 表名 + + builder.InsertCell(); + builder.Write((!string.IsNullOrWhiteSpace(table.Comment) ? table.Comment : "")); // 说明 + #endregion + + builder.EndRow(); + } + // TODO 表格创建完成,结束 + builder.EndTable(); + } + + /// + /// 创建书签 + /// + /// + /// + /// + /// + /// + /// + private static void CreateBookmark(Aspose.Words.DocumentBuilder builder, Aspose.Words.ParagraphAlignment alignment, + Aspose.Words.OutlineLevel outlineLevel, double fontSize, string bookmarkName, string bookmarkText) + { + // 清除段落样式 + builder.ParagraphFormat.ClearFormatting(); + + // TODO 创建书签 + builder.StartBookmark(bookmarkName); + builder.ParagraphFormat.Alignment = alignment; + builder.ParagraphFormat.OutlineLevel = outlineLevel; + builder.ParagraphFormat.SpaceBefore = builder.ParagraphFormat.SpaceAfter = 15; + builder.Font.Size = fontSize; + builder.Font.Name = "Arial"; + builder.Font.Bold = true; + builder.Writeln(bookmarkText); + builder.EndBookmark(bookmarkName); + } + + /// + /// Inserts a watermark into a document. + /// + /// The input document. + /// Text of the watermark. + public static void InsertWatermarkText(Aspose.Words.Document doc, string watermarkText) + { + // Create a watermark shape. This will be a WordArt shape. + // You are free to try other shape types as watermarks. + Aspose.Words.Drawing.Shape watermark = new Aspose.Words.Drawing.Shape(doc, Aspose.Words.Drawing.ShapeType.TextPlainText); + // Set up the text of the watermark. + watermark.TextPath.Text = watermarkText; + watermark.TextPath.FontFamily = "Arial"; + watermark.Width = 500; + watermark.Height = 100; + // Text will be directed from the bottom-left to the top-right corner. + watermark.Rotation = -40; + // Remove the following two lines if you need a solid black text. + watermark.Fill.Color = System.Drawing.Color.Gray; // Try LightGray to get more Word-style watermark + watermark.StrokeColor = System.Drawing.Color.Gray; // Try LightGray to get more Word-style watermark + // Place the watermark in the page center. + watermark.RelativeHorizontalPosition = Aspose.Words.Drawing.RelativeHorizontalPosition.Page; + watermark.RelativeVerticalPosition = Aspose.Words.Drawing.RelativeVerticalPosition.Page; + watermark.WrapType = Aspose.Words.Drawing.WrapType.None; + watermark.VerticalAlignment = Aspose.Words.Drawing.VerticalAlignment.Center; + watermark.HorizontalAlignment = Aspose.Words.Drawing.HorizontalAlignment.Center; + // Create a new paragraph and append the watermark to this paragraph. + Aspose.Words.Paragraph watermarkPara = new Aspose.Words.Paragraph(doc); + watermarkPara.AppendChild(watermark); + // Insert the watermark into all headers of each document section. + foreach (Aspose.Words.Section sect in doc.Sections) + { + // There could be up to three different headers in each section, since we want + // the watermark to appear on all pages, insert into all headers. + InsertWatermarkIntoHeader(watermarkPara, sect, Aspose.Words.HeaderFooterType.HeaderPrimary); + InsertWatermarkIntoHeader(watermarkPara, sect, Aspose.Words.HeaderFooterType.HeaderFirst); + InsertWatermarkIntoHeader(watermarkPara, sect, Aspose.Words.HeaderFooterType.HeaderEven); + } + } + + /// + /// Inserts a watermark into a document header. + /// + /// + /// + /// + public static void InsertWatermarkIntoHeader(Aspose.Words.Paragraph watermarkPara, Aspose.Words.Section sect, Aspose.Words.HeaderFooterType headerType) + { + Aspose.Words.HeaderFooter header = sect.HeadersFooters[headerType]; + if (null == header) + { + // There is no header of the specified type in the current section, create it. + header = new Aspose.Words.HeaderFooter(sect.Document, headerType); + sect.HeadersFooters.Add(header); + } + // Insert a clone of the watermark into the header. + header.AppendChild(watermarkPara.Clone(true)); + } + + } +} diff --git a/DocTools/DBDoc/XmlDoc.cs b/DocTools/DBDoc/XmlDoc.cs new file mode 100644 index 0000000..c6ba1d4 --- /dev/null +++ b/DocTools/DBDoc/XmlDoc.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using DocTools.Dtos; + +namespace DocTools.DBDoc +{ + public class XmlDoc : Doc + { + public XmlDoc(DBDto dto, string filter = "xml files (.xml)|*.xml") : base(dto, filter) + { + } + + public override void Build(string filePath) + { + string xmlContent = this.Dto.Tables.SerializeXml(); + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(xmlContent); + + var root = xmlDoc.DocumentElement; + root.SetAttribute("databaseName", this.Dto.DBName); + root.SetAttribute("tableNum", this.Dto.Tables.Count + ""); + xmlDoc.Save(filePath); + } + } +} diff --git a/DocTools/DocFactory.cs b/DocTools/DocFactory.cs new file mode 100644 index 0000000..d89e6c6 --- /dev/null +++ b/DocTools/DocFactory.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DocTools.DBDoc; +using DocTools.Dtos; + +namespace DocTools +{ + public class DocFactory + { + public static Doc CreateInstance(DocType type, DBDto dto) + { + switch (type) + { + case DocType.chm: + return new ChmDoc(dto); + case DocType.html: + return new HtmlDoc(dto); + case DocType.word: + return new WordDoc(dto); + case DocType.excel: + return new ExcelDoc(dto); + case DocType.pdf: + return new PdfDoc(dto); + case DocType.markdown: + return new MarkDownDoc(dto); + case DocType.xml: + return new XmlDoc(dto); + default: + return new ChmDoc(dto); + } + } + } +} diff --git a/DocTools/DocTools.csproj b/DocTools/DocTools.csproj new file mode 100644 index 0000000..656ec6e --- /dev/null +++ b/DocTools/DocTools.csproj @@ -0,0 +1,171 @@ + + + + + Debug + AnyCPU + {130A8861-0C39-4933-9DE8-AA9525488211} + Library + Properties + DocTools + DocTools + v4.7.2 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + lib\Aspose.Words.dll + + + ..\packages\BouncyCastle.1.8.6.1\lib\BouncyCastle.Crypto.dll + + + False + lib\EPPlus.dll + + + ..\packages\Esprima.2.1.3\lib\net461\Esprima.dll + + + ..\packages\iTextSharp.5.5.13.2\lib\itextsharp.dll + + + ..\packages\Jint.3.0.0-beta-2038\lib\net461\Jint.dll + + + ..\packages\Microsoft.IO.RecyclableMemoryStream.1.4.1\lib\net46\Microsoft.IO.RecyclableMemoryStream.dll + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + ..\packages\RazorEngine.3.10.0\lib\net45\RazorEngine.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.ComponentModel.Annotations.4.7.0\lib\net461\System.ComponentModel.Annotations.dll + + + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\Microsoft.AspNet.Razor.3.0.0\lib\net45\System.Web.Razor.dll + + + + + + + + + + ..\packages\ZetaLongPaths.1.0.0.38\lib\net452-full\ZetaLongPaths.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + {3d36cdc9-e989-465b-a9f1-ad85dc42f242} + MJTop.Data + + + + + PreserveNewest + + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/DocTools/Dtos/ChmHHP.cs b/DocTools/Dtos/ChmHHP.cs new file mode 100644 index 0000000..2f6112f --- /dev/null +++ b/DocTools/Dtos/ChmHHP.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools.Dtos +{ + public class ChmHHP + { + public ChmHHP() { } + + public ChmHHP(string chmFile, string workTmpDir) + { + this.ChmFile = chmFile; + this.WorkTmpDir = workTmpDir; + + if (!string.IsNullOrWhiteSpace(this.WorkTmpDir)) + { + this.Files = Directory.GetFiles(this.WorkTmpDir, "*.html", SearchOption.AllDirectories).ToList(); + } + } + + public string ChmFile { get; set; } + + public string WorkTmpDir { get; set; } + + public string DefaultFile { get; set; } = "数据库目录.html"; + + public string Title + { + get + { + if (string.IsNullOrWhiteSpace(this.ChmFile)) + { + return string.Empty; + } + return Path.GetFileName(this.ChmFile); + } + } + + public List Files { get; private set; } + } +} diff --git a/DocTools/Dtos/ColumnDto.cs b/DocTools/Dtos/ColumnDto.cs new file mode 100644 index 0000000..5c83ba8 --- /dev/null +++ b/DocTools/Dtos/ColumnDto.cs @@ -0,0 +1,74 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace DocTools.Dtos +{ + /// + /// 数据库表字段dto + /// + [Serializable] + public class ColumnDto + { + + /// + /// 序号 + /// + [Display(Name = "序号")] + public string ColumnOrder { get; set; } + + /// + /// 列名 + /// + [Display(Name = "列名")] + public string ColumnName { get; set; } + + /// + /// 数据类型 + /// + [Display(Name = "数据类型")] + public string ColumnTypeName { get; set; } + + /// + /// 长度 + /// + [Display(Name = "长度")] + public string Length { get; set; } + + /// + /// 小数位 + /// + [Display(Name = "小数位数")] + public string Scale { get; set; } + + /// + /// 主键 + /// + [Display(Name = "主键")] + public string IsPK { get; set; } + + /// + /// 自增 + /// + [Display(Name = "自增")] + public string IsIdentity { get; set; } + + /// + /// 允许空 + /// + [Display(Name = "允许空")] + public string CanNull { get; set; } + + /// + /// 默认值 + /// + [Display(Name = "默认值")] + public string DefaultVal { get; set; } + + /// + /// 注释 + /// + [Display(Name = "列说明")] + public string Comment { get; set; } + + } +} diff --git a/DocTools/Dtos/DBDto.cs b/DocTools/Dtos/DBDto.cs new file mode 100644 index 0000000..89139af --- /dev/null +++ b/DocTools/Dtos/DBDto.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Text; + +namespace DocTools.Dtos +{ + /// + /// 数据库Dto + /// + public class DBDto + { + public DBDto() { } + + public DBDto(string dbName, object tag = null) + { + this.DBName = dbName; + this.Tag = tag; + } + + /// + /// 数据库名称 + /// + public string DBName { get; set; } + + /// + /// 数据库类型 + /// + public string DBType { get; set; } + + + private List _Tables = null; + /// + /// 表结构信息 + /// + public List Tables + { + get + { + if (_Tables == null) + { + return new List(); + } + else + { + _Tables.ForEach(t => + { + t.Comment = FilterIllegalDir(t.Comment); + }); + return _Tables; + } + } + set + { + _Tables = value; + } + } + + /// + /// 数据库视图 + /// + public Dictionary Views { get; set; } + + /// + /// 数据库存储过程 + /// + public Dictionary Procs { get; set; } + + /// + /// 其他一些参数数据,用法如 winform 控件的 Tag属性 + /// + public object Tag { get; set; } + + + /// + /// 处理非法字符路径 + /// + /// + /// + private string FilterIllegalDir(string str) + { + if (str.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + str = string.Join(" ", str.Split(Path.GetInvalidFileNameChars())); + } + return str; + } + + } +} diff --git a/DocTools/Dtos/SqlCode.cs b/DocTools/Dtos/SqlCode.cs new file mode 100644 index 0000000..d2b3cd8 --- /dev/null +++ b/DocTools/Dtos/SqlCode.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools.Dtos +{ + public class SqlCode + { + public string DBType { get; set; } + + public string CodeName { get; set; } + + public string Content { get; set; } + + public string StyleContent + { + get + { + return JS.RunStyleSql(Content?.Trim(), DBType); + } + } + } +} diff --git a/DocTools/Dtos/TableDto.cs b/DocTools/Dtos/TableDto.cs new file mode 100644 index 0000000..9769c80 --- /dev/null +++ b/DocTools/Dtos/TableDto.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace DocTools.Dtos +{ + /// + /// 数据库表dto + /// + [Serializable] + public class TableDto + { + + /// + /// 序号 + /// + [Display(Name = "序号")] + public string TableOrder { get; set; } + + /// + /// 表名 + /// + [Display(Name = "表名")] + public string TableName { get; set; } + + /// + /// 注释 + /// + [Display(Name = "表说明")] + public string Comment { get; set; } + + /// + /// 数据库类型 + /// + [Display(Name = "数据库类型")] + public string DBType { get; set; } + + /// + /// 表格列集合 + /// + [Display(Name = "列数据")] + public List Columns { get; set; } + + } +} diff --git a/DocTools/Enums.cs b/DocTools/Enums.cs new file mode 100644 index 0000000..1fb88a5 --- /dev/null +++ b/DocTools/Enums.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools +{ + public enum DocType + { + chm, + html, + word, + excel, + pdf, + markdown, + xml + } +} diff --git a/DocTools/Extensions.cs b/DocTools/Extensions.cs new file mode 100644 index 0000000..c140945 --- /dev/null +++ b/DocTools/Extensions.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using ZetaLongPaths; +using System.ComponentModel.DataAnnotations; +using System.Collections.Specialized; + +namespace DocTools +{ + public static class Extensions + { + public static void WriteAllText(StringBuilder builder, string filePath, string enc = "gbk") + { + ZlpIOHelper.WriteAllText(filePath, builder.ToString(), Encoding.GetEncoding(enc)); + } + + public static void WriteAllText(StringBuilder builder, string filePath, Encoding encoding) + { + ZlpIOHelper.WriteAllText(filePath, builder.ToString(), encoding); + } + + public static string GetResourceContent(this Assembly assembly, string name) + { + var buffer = assembly.GetResourceBuffer(name); + return System.Text.Encoding.UTF8.GetString(buffer); + } + + public static byte[] GetResourceBuffer(this Assembly assembly, string name) + { + var stream = assembly.GetManifestResourceStream(name); + var buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + stream.Dispose(); + return buffer; + } + + public static EM GetEnum(this string enumName) + where EM : struct, Enum + { + if (!Enum.TryParse(enumName, out EM em)) + { + throw new ArgumentException("枚举转换失败!", nameof(enumName)); + } + return em; + } + + public static Dictionary ToDictionary(this NameValueCollection nvc) + { + var dict = new Dictionary(); + foreach (var key in nvc.AllKeys) + { + dict.Add(key, nvc[key]); + } + return dict; + } + + #region MarkDown + + public static string MarkDown(this IEnumerable objs, params string[] excludePropNames) + { + if (objs == null) + { + return string.Empty; + } + StringBuilder sb = new StringBuilder(); + var minus = 0; + var type = typeof(T); + var props = type.GetProperties(); + var lstTmp = new List(); + + sb.Append(" | "); + foreach (var prop in props) + { + if (excludePropNames != null && excludePropNames.Contains(prop.Name, StringComparer.OrdinalIgnoreCase)) + { + minus++; + continue; + } + var headName = ((prop.GetCustomAttributes(typeof(DisplayAttribute), false)?.FirstOrDefault() as DisplayAttribute)?.Name) ?? prop.Name; + lstTmp.Add(headName); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + sb.AppendLine(); + + lstTmp = new List(); + sb.Append(" | "); + for (int j = 0; j < props.Length - minus; j++) + { + lstTmp.Add(":---:"); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + + foreach (var obj in objs) + { + if (obj == null) + { + continue; + } + sb.AppendLine(); + sb.Append(" | "); + lstTmp = new List(); + foreach (var prop in props) + { + if (excludePropNames != null && excludePropNames.Contains(prop.Name, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + var value = (prop.GetValue(obj, null) ?? string.Empty).ToString(); + lstTmp.Add(value); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + } + var md = sb.ToString(); + return md; + } + + public static string MarkDown(this DataTable data, params string[] excludeColNames) + { + if (data == null) + { + return string.Empty; + } + StringBuilder sb = new StringBuilder(); + var minus = 0; + var lstTmp = new List(); + + sb.Append(" | "); + foreach (DataColumn dc in data.Columns) + { + if (excludeColNames != null && excludeColNames.Contains(dc.ColumnName, StringComparer.OrdinalIgnoreCase)) + { + minus++; + continue; + } + lstTmp.Add(dc.ColumnName); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + sb.AppendLine(); + + lstTmp = new List(); + sb.Append(" | "); + for (int j = 0; j < data.Columns.Count - minus; j++) + { + lstTmp.Add(":---:"); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + + foreach (DataRow dr in data.Rows) + { + sb.AppendLine(); + sb.Append(" | "); + lstTmp = new List(); + foreach (DataColumn dc in data.Columns) + { + if (excludeColNames != null && excludeColNames.Contains(dc.ColumnName, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + var value = (dr[dc] ?? string.Empty).ToString(); + lstTmp.Add(value); + } + sb.Append(string.Join(" | ", lstTmp)); + sb.Append(" | "); + } + var md = sb.ToString(); + return md; + } + + #endregion + + #region Xml序列化/反序列化 + + /// + /// 反序列化 + /// + /// 类型 + /// XML字符串 + /// + public static object DeserializeXml(this Type type, string xml) + { + using (StringReader sr = new StringReader(xml)) + { + XmlSerializer xmldes = new XmlSerializer(type); + return xmldes.Deserialize(sr); + } + } + + /// + /// 反序列化 + /// + /// + /// + /// + public static object DeserializeXml(this Type type, Stream stream) + { + XmlSerializer xmldes = new XmlSerializer(type); + return xmldes.Deserialize(stream); + } + + + + /// + /// 序列化 + /// + /// 类型 + /// 对象 + /// + public static string SerializeXml(this T obj) + where T : new() + { + MemoryStream Stream = new MemoryStream(); + XmlSerializer xml = new XmlSerializer(obj.GetType()); + //序列化对象 + xml.Serialize(Stream, obj); + Stream.Position = 0; + StreamReader sr = new StreamReader(Stream); + string str = sr.ReadToEnd(); + + sr.Dispose(); + Stream.Dispose(); + + return str; + } + + #endregion + } +} diff --git a/DocTools/JS.cs b/DocTools/JS.cs new file mode 100644 index 0000000..36f5a64 --- /dev/null +++ b/DocTools/JS.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocTools +{ + public class JS + { + private static Jint.Engine jt = null; + static JS() + { + //jt = new Jint.Engine(cfg => + //{ + // //cfg.LimitRecursion(); + // cfg.Strict(); + //}); + jt = new Jint.Engine(); + jt.SetValue("log", new Action(Console.WriteLine)); + + var text = string.Empty; + var names = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames(); + foreach (var name in names) + { + if (Path.GetExtension(name) == ".js") + { + text = System.Reflection.Assembly.GetExecutingAssembly().GetResourceContent(name); + try + { + jt.Execute(text); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } + } + + /// summary> + /// https://zeroturnaround.github.io/sql-formatter/ + /// 格式化SQL语句 + /// + /// SQL语句 + /// 数据库类型 + /// 格式化后的SQL语句 + public static string RunFmtSql(string sql, string dbtype) + { + var supportLang = new List() + { + "sql", + "redshift", + "db2", + "mariadb", + "mysql", + "n1ql", + "plsql", + "postgresql", + "spark", + "tsql" + }; + + var lang = dbtype.ToLower(); + + if (!supportLang.Contains(lang)) + { + if (lang.StartsWith("oracle")) + { + lang = "plsql"; + } + else if (lang == "sqlserver") + { + lang = "tsql"; + } + else + { + lang = "sql"; + } + } + + sql = sql.Replace("`", ""); + var code = $@"var fmtSql = sqlFormatter.format(`{ sql }`, {{language: ""{ lang }"",uppercase: true }});"; + var res = sql; + try + { + jt.Execute(code); + res = jt.GetValue("fmtSql").ToString(); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + return res; + } + + /// + /// https://highlightjs.org/static/demo/ + /// 9.9版本 + /// 返回带有css样式的高亮SQL代码 + /// + /// 格式化后的SQL代码 + /// 数据库类型 + /// + public static string RunHighlightHtml(string sql, string dbtype) + { + dbtype = dbtype.ToLower(); + var lang = "sql"; + + //if (dbtype.StartsWith("oracle")) + //{ + // lang = "ruleslanguage"; + //} + //else if (dbtype == "postgresql") + //{ + // lang = "pgsql"; + //} + + sql = sql.Replace("`", ""); + var code = $@"var hlhtml = hljs.highlight(""{ lang }"",`{ sql }`,true).value;"; + var res = sql; + try + { + jt.Execute(code); + res = jt.GetValue("hlhtml").ToString(); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + return res; + } + + + public static string RunStyleSql(string sql, string dbtype) + { + var fmtSql = RunFmtSql(sql, dbtype); + + var reSql = RunHighlightHtml(fmtSql, dbtype); + + return reSql; + } + } +} + diff --git a/DocTools/LogUtils.cs b/DocTools/LogUtils.cs new file mode 100644 index 0000000..ae6cb33 --- /dev/null +++ b/DocTools/LogUtils.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Web; +using System.Text.RegularExpressions; +using System.Linq; +using System.Collections; +using System.Reflection; +using System.Collections.Specialized; +using ZetaLongPaths; + +/// +/// 日志操作类 +/// +public class LogUtils +{ + + private static object locker = new object(); + + + /// + /// 写入日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志级别 + /// 日志详情 + /// 记录时间 + public static void Write(string logName, Developer developer, LogLevel level, List details, DateTime createtime) + { + Log log = new Log(); + log.LogName = logName; + log.Level = level; + log.Developer = developer; + log.Added = createtime; + log.Details = details; + + string logText = log.ToString() + "\r\n----------------------------------------------------------------------------------------------------\r\n"; + string fileName = DateTime.Now.ToString("yyyyMMdd") + ".log"; + string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log"); + if (!ZetaLongPaths.ZlpIOHelper.DirectoryExists(dir)) + { + ZetaLongPaths.ZlpIOHelper.CreateDirectory(dir); + } + fileName = Path.Combine(dir, fileName); + ZlpIOHelper.AppendText(fileName, logText, Encoding.GetEncoding("GBK")); + } + + /// + /// 写入Info 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogInfo(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + Write(logName, developer, LogLevel.Info, Info_objs?.ToList(), DateTime.Now); + } + } + + + /// s + /// 写入带 堆栈执行 的Info 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogWrite(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true); + System.Diagnostics.StackFrame frame = stack.GetFrame(0); + string execFile = frame.GetFileName(); + string fullName = frame.GetMethod().DeclaringType.FullName; + string methodName = frame.GetMethod().Name; + int execLine = frame.GetFileLineNumber(); + lstDetails.Add("文件路径:" + execFile + "\r\n"); + lstDetails.Add("类全命名:" + fullName + "\r\n"); + lstDetails.Add("执行方法:" + methodName + "\r\n"); + lstDetails.Add("当前行号:" + execLine + "\r\n"); + + if (Info_objs != null && Info_objs.Length > 0) + { + lstDetails.AddRange(Info_objs); + } + Write(logName, developer, LogLevel.Info, lstDetails, DateTime.Now); + } + } + + /// + /// 写入Warn 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogWarn(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true); + System.Diagnostics.StackFrame frame = stack.GetFrame(0); + string execFile = frame.GetFileName(); + string fullName = frame.GetMethod().DeclaringType.FullName; + string methodName = frame.GetMethod().Name; + int execLine = frame.GetFileLineNumber(); + lstDetails.Add("文件路径:" + execFile + "\r\n"); + lstDetails.Add("类全命名:" + fullName + "\r\n"); + lstDetails.Add("执行方法:" + methodName + "\r\n"); + lstDetails.Add("当前行号:" + execLine + "\r\n"); + + if (Info_objs != null && Info_objs.Length > 0) + { + lstDetails.AddRange(Info_objs); + } + Write(logName, developer, LogLevel.Warn, lstDetails, DateTime.Now); + } + } + + /// + /// 写入 Errorr日志 + /// + /// 日志名称 + /// 开发记录者 + /// 异常对象(可为null) + /// 日志内容 + public static void LogError(string logName, Developer developer, Exception ex, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true); + System.Diagnostics.StackFrame frame = stack.GetFrame(0); + string execFile = frame.GetFileName(); + string fullName = frame.GetMethod().DeclaringType.FullName; + string methodName = frame.GetMethod().Name; + int execLine = frame.GetFileLineNumber(); + lstDetails.Add("文件路径:" + execFile + "\r\n"); + lstDetails.Add("类全命名:" + fullName + "\r\n"); + lstDetails.Add("执行方法:" + methodName + "\r\n"); + lstDetails.Add("当前行号:" + execLine + "\r\n"); + lstDetails.Add(ex); + if (ex.InnerException != null) + { + lstDetails.Add(ex.InnerException); + } + if (Info_objs != null && Info_objs.Length > 0) + { + lstDetails.AddRange(Info_objs); + } + Write(logName, developer, LogLevel.Error, lstDetails, DateTime.Now); + } + } +} + + +/// +/// 程序日志 +/// +public class Log +{ + public Guid Id { get { return Guid.NewGuid(); } } + + /// + /// 日志名称 + /// + public string LogName { get; set; } + + /// + /// 日志级别 + /// + public LogLevel Level { get; set; } + + /// + /// 当前记录日志者 + /// + public Developer Developer { get; set; } + + /// + /// 日志详细内容 + /// + public List Details { get; set; } + + /// + /// 日志时间 + /// + public DateTime Added { get; set; } + + + public override string ToString() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this ?? default(Log)); + } + + + #region 枚举 处理 + /// + /// 根据枚举对象得到 枚举键值对 + /// + /// 枚举 + /// + public static Dictionary GetAllEnums() + { + Dictionary dict = null; + Type type = typeof(T); + string[] enums = Enum.GetNames(type); + if (enums != null && enums.Length > 0) + { + dict = new Dictionary(); + foreach (string item in enums) + { + string str = Enum.Parse(typeof(T), item).ToString(); + T deve = (T)Enum.Parse(typeof(T), item); + string uid = Convert.ToInt32(deve).ToString(); + dict.Add(str, uid); + } + } + return dict; + } + + + /// + /// 根据枚举val获取枚举name + /// + /// 枚举类型 + /// 枚举val + /// 枚举name + public static T GetEnumName(int enumVal) + { + T t = (T)Enum.Parse(typeof(T), enumVal.ToString()); + return t; + } + #endregion +} + +/// +/// 日志级别 +/// +public enum LogLevel +{ + Info = 0, + Warn = 1, + Error = 2 +} + +/// +/// 日志记录开发者 +/// +public enum Developer +{ + /// + /// 系统默认 + /// + SysDefault = 0, + + /// + /// 其他用户 + /// + + MJ = 1, +} \ No newline at end of file diff --git a/DocTools/Properties/AssemblyInfo.cs b/DocTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..498e6a5 --- /dev/null +++ b/DocTools/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("DocTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DocTools")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("130a8861-0c39-4933-9de8-aa9525488211")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DocTools/RazorTpl.cs b/DocTools/RazorTpl.cs new file mode 100644 index 0000000..58edf89 --- /dev/null +++ b/DocTools/RazorTpl.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using RazorEngine; +using RazorEngine.Configuration; +using RazorEngine.Templating; +using RazorEngine.Text; + +namespace DocTools +{ + public static class RazorTpl + { + static RazorTpl() + { + var config = new TemplateServiceConfiguration(); + config.Language = Language.CSharp; + config.EncodedStringFactory = new RawStringFactory(); + config.DisableTempFileLocking = true; + //config.EncodedStringFactory = new HtmlEncodedStringFactory(); + var service = RazorEngineService.Create(config); + Engine.Razor = service; + } + + public static string RazorRender(this FileInfo tpl_file, object model, string encoding = "utf-8") + { + try + { + var tpl_text = File.ReadAllText(tpl_file.FullName, System.Text.Encoding.GetEncoding(encoding)); + + return Engine.Razor.RunCompile(tpl_text, Md5(tpl_text), null, model); + } + catch (Exception ex) + { + throw ex; + } + } + + public static string RazorRender(this string tpl_text, object model) + { + try + { + return Engine.Razor.RunCompile(tpl_text, Md5(tpl_text), null, model); + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 字符串的Md5值 + /// + private static string Md5(string value) + { + if (value == null) + return null; + MD5 md5Hash = MD5.Create(); + byte[] data = md5Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value)); + StringBuilder sBuilder = new StringBuilder(); + for (int i = 0; i < data.Length; i++) + { + sBuilder.Append(data[i].ToString("x2")); + } + return sBuilder.ToString(); + } + } +} diff --git a/DocTools/TplFile/chm/embed/highlight.js b/DocTools/TplFile/chm/embed/highlight.js new file mode 100644 index 0000000..3dde6b9 --- /dev/null +++ b/DocTools/TplFile/chm/embed/highlight.js @@ -0,0 +1,964 @@ + +var hljs = {}; +// Convenience variables for build-in objects +var ArrayProto = [], + objectKeys = Object.keys; + +// Global internal variables used within the highlight.js library. +var languages = {}, + aliases = {}; + +// Regular expressions used throughout the highlight.js library. +var noHighlightRe = /^(no-?highlight|plain|text)$/i, + languagePrefixRe = /\blang(?:uage)?-([\w-]+)\b/i, + fixMarkupRe = /((^(<[^>]+>|\t|)+|(?:\n)))/gm; + +var spanEndTag = ''; + +// Global options used when within external APIs. This is modified when +// calling the `hljs.configure` function. +var options = { + classPrefix: 'hljs-', + tabReplace: null, + useBR: false, + languages: undefined +}; + +// Object map that is used to escape some common HTML characters. +var escapeRegexMap = { + '&': '&', + '<': '<', + '>': '>' +}; + +/* Utility functions */ + +function escape(value) { + return value.replace(/[&<>]/gm, function (character) { + return escapeRegexMap[character]; + }); +} + +function tag(node) { + return node.nodeName.toLowerCase(); +} + +function testRe(re, lexeme) { + var match = re && re.exec(lexeme); + return match && match.index === 0; +} + +function isNotHighlighted(language) { + return noHighlightRe.test(language); +} + +function blockLanguage(block) { + var i, match, length, _class; + var classes = block.className + ' '; + + classes += block.parentNode ? block.parentNode.className : ''; + + // language-* takes precedence over non-prefixed class names. + match = languagePrefixRe.exec(classes); + if (match) { + return getLanguage(match[1]) ? match[1] : 'no-highlight'; + } + + classes = classes.split(/\s+/); + + for (i = 0, length = classes.length; i < length; i++) { + _class = classes[i] + + if (isNotHighlighted(_class) || getLanguage(_class)) { + return _class; + } + } +} + +function inherit(parent, obj) { + var key; + var result = {}; + + for (key in parent) + result[key] = parent[key]; + if (obj) + for (key in obj) + result[key] = obj[key]; + return result; +} + +/* Stream merging */ + +function nodeStream(node) { + var result = []; + (function _nodeStream(node, offset) { + for (var child = node.firstChild; child; child = child.nextSibling) { + if (child.nodeType === 3) + offset += child.nodeValue.length; + else if (child.nodeType === 1) { + result.push({ + event: 'start', + offset: offset, + node: child + }); + offset = _nodeStream(child, offset); + // Prevent void elements from having an end tag that would actually + // double them in the output. There are more void elements in HTML + // but we list only those realistically expected in code display. + if (!tag(child).match(/br|hr|img|input/)) { + result.push({ + event: 'stop', + offset: offset, + node: child + }); + } + } + } + return offset; + })(node, 0); + return result; +} + +function mergeStreams(original, highlighted, value) { + var processed = 0; + var result = ''; + var nodeStack = []; + + function selectStream() { + if (!original.length || !highlighted.length) { + return original.length ? original : highlighted; + } + if (original[0].offset !== highlighted[0].offset) { + return (original[0].offset < highlighted[0].offset) ? original : highlighted; + } + + /* + To avoid starting the stream just before it should stop the order is + ensured that original always starts first and closes last: + + if (event1 == 'start' && event2 == 'start') + return original; + if (event1 == 'start' && event2 == 'stop') + return highlighted; + if (event1 == 'stop' && event2 == 'start') + return original; + if (event1 == 'stop' && event2 == 'stop') + return highlighted; + + ... which is collapsed to: + */ + return highlighted[0].event === 'start' ? original : highlighted; + } + + function open(node) { + function attr_str(a) { return ' ' + a.nodeName + '="' + escape(a.value) + '"'; } + result += '<' + tag(node) + ArrayProto.map.call(node.attributes, attr_str).join('') + '>'; + } + + function close(node) { + result += ''; + } + + function render(event) { + (event.event === 'start' ? open : close)(event.node); + } + + while (original.length || highlighted.length) { + var stream = selectStream(); + result += escape(value.substring(processed, stream[0].offset)); + processed = stream[0].offset; + if (stream === original) { + /* + On any opening or closing tag of the original markup we first close + the entire highlighted node stack, then render the original tag along + with all the following original tags at the same offset and then + reopen all the tags on the highlighted stack. + */ + nodeStack.reverse().forEach(close); + do { + render(stream.splice(0, 1)[0]); + stream = selectStream(); + } while (stream === original && stream.length && stream[0].offset === processed); + nodeStack.reverse().forEach(open); + } else { + if (stream[0].event === 'start') { + nodeStack.push(stream[0].node); + } else { + nodeStack.pop(); + } + render(stream.splice(0, 1)[0]); + } + } + return result + escape(value.substr(processed)); +} + +/* Initialization */ + + +function compileLanguage(language) { + + function reStr(re) { + return (re && re.source) || re; + } + + function langRe(value, global) { + return new RegExp( + reStr(value), + 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '') + ); + } + + let cnt = 0; + function compileMode(mode, parent) { + cnt++; + if (cnt > 5) { + return; + } + if (mode.compiled) + return; + mode.compiled = true; + + mode.keywords = mode.keywords || mode.beginKeywords; + if (mode.keywords) { + var compiled_keywords = {}; + + var flatten = function (className, str) { + if (language.case_insensitive) { + str = str.toLowerCase(); + } + str.split(' ').forEach(function (kw) { + var pair = kw.split('|'); + compiled_keywords[pair[0]] = [className, pair[1] ? Number(pair[1]) : 1]; + }); + }; + + if (typeof mode.keywords === 'string') { // string + flatten('keyword', mode.keywords); + } else { + objectKeys(mode.keywords).forEach(function (className) { + flatten(className, mode.keywords[className]); + }); + } + mode.keywords = compiled_keywords; + } + mode.lexemesRe = langRe(mode.lexemes || /\w+/, true); + + if (parent) { + if (mode.beginKeywords) { + mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')\\b'; + } + if (!mode.begin) + mode.begin = /\B|\b/; + mode.beginRe = langRe(mode.begin); + if (!mode.end && !mode.endsWithParent) + mode.end = /\B|\b/; + if (mode.end) + mode.endRe = langRe(mode.end); + mode.terminator_end = reStr(mode.end) || ''; + if (mode.endsWithParent && parent.terminator_end) + mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end; + } + if (mode.illegal) + mode.illegalRe = langRe(mode.illegal); + if (mode.relevance == null) + mode.relevance = 1; + if (!mode.contains) { + mode.contains = []; + } + var expanded_contains = []; + log("mode.contains.length:" + mode.contains.length); + mode.contains.forEach(function (c) { + if (c.variants) { + c.variants.forEach(function (v) { expanded_contains.push(inherit(c, v)); }); + } else { + expanded_contains.push(c === 'self' ? mode : c); + } + }); + mode.contains = expanded_contains; + mode.contains.forEach(function (c) { + compileMode(c, mode); + }); + + if (mode.starts) { + compileMode(mode.starts, parent); + } + + var terminators = + mode.contains.map(function (c) { + return c.beginKeywords ? '\\.?(' + c.begin + ')\\.?' : c.begin; + }) + .concat([mode.terminator_end, mode.illegal]) + .map(reStr) + .filter(Boolean); + mode.terminators = terminators.length ? langRe(terminators.join('|'), true) : { exec: function (/*s*/) { return null; } }; + } + + compileMode(language); +} + +/* +Core highlighting function. Accepts a language name, or an alias, and a +string with the code to highlight. Returns an object with the following +properties: + +- relevance (int) +- value (an HTML string with highlighting markup) + +*/ +function highlight(name, value, ignore_illegals, continuation) { + + function subMode(lexeme, mode) { + var i, length; + + for (i = 0, length = mode.contains.length; i < length; i++) { + if (testRe(mode.contains[i].beginRe, lexeme)) { + return mode.contains[i]; + } + } + } + + function endOfMode(mode, lexeme) { + if (testRe(mode.endRe, lexeme)) { + while (mode.endsParent && mode.parent) { + mode = mode.parent; + } + return mode; + } + if (mode.endsWithParent) { + return endOfMode(mode.parent, lexeme); + } + } + + function isIllegal(lexeme, mode) { + return !ignore_illegals && testRe(mode.illegalRe, lexeme); + } + + function keywordMatch(mode, match) { + var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0]; + return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str]; + } + + function buildSpan(classname, insideSpan, leaveOpen, noPrefix) { + var classPrefix = noPrefix ? '' : options.classPrefix, + openSpan = ''; + + return openSpan + insideSpan + closeSpan; + } + + function processKeywords() { + var keyword_match, last_index, match, result; + + if (!top.keywords) + return escape(mode_buffer); + + result = ''; + last_index = 0; + top.lexemesRe.lastIndex = 0; + match = top.lexemesRe.exec(mode_buffer); + + while (match) { + result += escape(mode_buffer.substring(last_index, match.index)); + keyword_match = keywordMatch(top, match); + if (keyword_match) { + relevance += keyword_match[1]; + result += buildSpan(keyword_match[0], escape(match[0])); + } else { + result += escape(match[0]); + } + last_index = top.lexemesRe.lastIndex; + match = top.lexemesRe.exec(mode_buffer); + } + return result + escape(mode_buffer.substr(last_index)); + } + + function processSubLanguage() { + var explicit = typeof top.subLanguage === 'string'; + if (explicit && !languages[top.subLanguage]) { + return escape(mode_buffer); + } + + var result = explicit ? + highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) : + highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : undefined); + + // Counting embedded language score towards the host language may be disabled + // with zeroing the containing mode relevance. Usecase in point is Markdown that + // allows XML everywhere and makes every XML snippet to have a much larger Markdown + // score. + if (top.relevance > 0) { + relevance += result.relevance; + } + if (explicit) { + continuations[top.subLanguage] = result.top; + } + return buildSpan(result.language, result.value, false, true); + } + + function processBuffer() { + result += (top.subLanguage != null ? processSubLanguage() : processKeywords()); + mode_buffer = ''; + } + + function startNewMode(mode) { + result += mode.className ? buildSpan(mode.className, '', true) : ''; + top = Object.create(mode, { parent: { value: top } }); + } + + function processLexeme(buffer, lexeme) { + + mode_buffer += buffer; + + if (lexeme == null) { + processBuffer(); + return 0; + } + + var new_mode = subMode(lexeme, top); + if (new_mode) { + if (new_mode.skip) { + mode_buffer += lexeme; + } else { + if (new_mode.excludeBegin) { + mode_buffer += lexeme; + } + processBuffer(); + if (!new_mode.returnBegin && !new_mode.excludeBegin) { + mode_buffer = lexeme; + } + } + startNewMode(new_mode, lexeme); + return new_mode.returnBegin ? 0 : lexeme.length; + } + + var end_mode = endOfMode(top, lexeme); + if (end_mode) { + var origin = top; + if (origin.skip) { + mode_buffer += lexeme; + } else { + if (!(origin.returnEnd || origin.excludeEnd)) { + mode_buffer += lexeme; + } + processBuffer(); + if (origin.excludeEnd) { + mode_buffer = lexeme; + } + } + do { + if (top.className) { + result += spanEndTag; + } + if (!top.skip) { + relevance += top.relevance; + } + top = top.parent; + } while (top !== end_mode.parent); + if (end_mode.starts) { + startNewMode(end_mode.starts, ''); + } + return origin.returnEnd ? 0 : lexeme.length; + } + + if (isIllegal(lexeme, top)) + throw new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '') + '"'); + + /* + Parser should not reach this point as all types of lexemes should be caught + earlier, but if it does due to some bug make sure it advances at least one + character forward to prevent infinite looping. + */ + mode_buffer += lexeme; + return lexeme.length || 1; + } + + var language = getLanguage(name); + log(name + " " + language); + if (!language) { + throw new Error('Unknown language: "' + name + '"'); + } + + log("language:" + language); + compileLanguage(language); + var top = continuation || language; + + + var continuations = {}; // keep continuations for sub-languages + var result = '', current; + for (current = top; current !== language; current = current.parent) { + if (current.className) { + result = buildSpan(current.className, '', true) + result; + } + } + log("name:" + name); + var mode_buffer = ''; + var relevance = 0; + try { + var match, count, index = 0; + while (true) { + log("index:"+index); + top.terminators.lastIndex = index; + match = top.terminators.exec(value); + if (!match) + break; + count = processLexeme(value.substring(index, match.index), match[0]); + index = match.index + count; + } + processLexeme(value.substr(index)); + for (current = top; current.parent; current = current.parent) { // close dangling modes + if (current.className) { + result += spanEndTag; + } + } + return { + relevance: relevance, + value: result, + language: name, + top: top + }; + } catch (e) { + if (e.message && e.message.indexOf('Illegal') !== -1) { + return { + relevance: 0, + value: escape(value) + }; + } else { + throw e; + } + } +} + +/* +Highlighting with language detection. Accepts a string with the code to +highlight. Returns an object with the following properties: + +- language (detected language) +- relevance (int) +- value (an HTML string with highlighting markup) +- second_best (object with the same structure for second-best heuristically + detected language, may be absent) + +*/ +function highlightAuto(text, languageSubset) { + languageSubset = languageSubset || options.languages || objectKeys(languages); + var result = { + relevance: 0, + value: escape(text) + }; + var second_best = result; + languageSubset.filter(getLanguage).forEach(function (name) { + var current = highlight(name, text, false); + current.language = name; + if (current.relevance > second_best.relevance) { + second_best = current; + } + if (current.relevance > result.relevance) { + second_best = result; + result = current; + } + }); + if (second_best.language) { + result.second_best = second_best; + } + return result; +} + +/* +Post-processing of the highlighted markup: + +- replace TABs with something more useful +- replace real line-breaks with '
' for non-pre containers + +*/ +function fixMarkup(value) { + return !(options.tabReplace || options.useBR) + ? value + : value.replace(fixMarkupRe, function (match, p1) { + if (options.useBR && match === '\n') { + return '
'; + } else if (options.tabReplace) { + return p1.replace(/\t/g, options.tabReplace); + } + }); +} + +function buildClassName(prevClassName, currentLang, resultLang) { + var language = currentLang ? aliases[currentLang] : resultLang, + result = [prevClassName.trim()]; + + if (!prevClassName.match(/\bhljs\b/)) { + result.push('hljs'); + } + + if (prevClassName.indexOf(language) === -1) { + result.push(language); + } + + return result.join(' ').trim(); +} + +/* +Applies highlighting to a DOM node containing code. Accepts a DOM node and +two optional parameters for fixMarkup. +*/ +function highlightBlock(block) { + var node, originalStream, result, resultNode, text; + var language = blockLanguage(block); + + if (isNotHighlighted(language)) + return; + + if (options.useBR) { + node = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); + node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(//g, '\n'); + } else { + node = block; + } + text = node.textContent; + result = language ? highlight(language, text, true) : highlightAuto(text); + + originalStream = nodeStream(node); + if (originalStream.length) { + resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); + resultNode.innerHTML = result.value; + result.value = mergeStreams(originalStream, nodeStream(resultNode), text); + } + result.value = fixMarkup(result.value); + + block.innerHTML = result.value; + block.className = buildClassName(block.className, language, result.language); + block.result = { + language: result.language, + re: result.relevance + }; + if (result.second_best) { + block.second_best = { + language: result.second_best.language, + re: result.second_best.relevance + }; + } +} + +/* +Updates highlight.js global options with values passed in the form of an object. +*/ +function configure(user_options) { + options = inherit(options, user_options); +} + +/* +Applies highlighting to all
..
blocks on a page. +*/ +function initHighlighting() { + if (initHighlighting.called) + return; + initHighlighting.called = true; + + var blocks = document.querySelectorAll('pre code'); + ArrayProto.forEach.call(blocks, highlightBlock); +} + +/* +Attaches highlighting to the page load event. +*/ +function initHighlightingOnLoad() { + addEventListener('DOMContentLoaded', initHighlighting, false); + addEventListener('load', initHighlighting, false); +} + +function registerLanguage(name, language) { + //log(name + " " + language); + var lang = languages[name] = language(hljs); + if (lang.aliases) { + lang.aliases.forEach(function (alias) { aliases[alias] = name; }); + } +} + +function listLanguages() { + return objectKeys(languages); +} + +function getLanguage(name) { + name = (name || '').toLowerCase(); + return languages[name] || languages[aliases[name]]; +} + +/* Interface definition */ + +hljs.highlight = highlight; +hljs.highlightAuto = highlightAuto; +hljs.fixMarkup = fixMarkup; +hljs.highlightBlock = highlightBlock; +hljs.configure = configure; +hljs.initHighlighting = initHighlighting; +hljs.initHighlightingOnLoad = initHighlightingOnLoad; +hljs.registerLanguage = registerLanguage; +hljs.listLanguages = listLanguages; +hljs.getLanguage = getLanguage; +hljs.inherit = inherit; + +// Common regexps +hljs.IDENT_RE = '[a-zA-Z]\\w*'; +hljs.UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; +hljs.NUMBER_RE = '\\b\\d+(\\.\\d+)?'; +hljs.C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float +hljs.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... +hljs.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; + +// Common modes +hljs.BACKSLASH_ESCAPE = { + begin: '\\\\[\\s\\S]', relevance: 0 +}; +hljs.APOS_STRING_MODE = { + className: 'string', + begin: '\'', end: '\'', + illegal: '\\n', + contains: [hljs.BACKSLASH_ESCAPE] +}; +hljs.QUOTE_STRING_MODE = { + className: 'string', + begin: '"', end: '"', + illegal: '\\n', + contains: [hljs.BACKSLASH_ESCAPE] +}; +hljs.PHRASAL_WORDS_MODE = { + begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/ +}; +hljs.COMMENT = function (begin, end, inherits) { + var mode = hljs.inherit( + { + className: 'comment', + begin: begin, end: end, + contains: [] + }, + inherits || {} + ); + mode.contains.push(hljs.PHRASAL_WORDS_MODE); + mode.contains.push({ + className: 'doctag', + begin: '(?:TODO|FIXME|NOTE|BUG|XXX):', + relevance: 0 + }); + return mode; +}; +hljs.C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$'); +hljs.C_BLOCK_COMMENT_MODE = hljs.COMMENT('/\\*', '\\*/'); +hljs.HASH_COMMENT_MODE = hljs.COMMENT('#', '$'); +hljs.NUMBER_MODE = { + className: 'number', + begin: hljs.NUMBER_RE, + relevance: 0 +}; +hljs.C_NUMBER_MODE = { + className: 'number', + begin: hljs.C_NUMBER_RE, + relevance: 0 +}; +hljs.BINARY_NUMBER_MODE = { + className: 'number', + begin: hljs.BINARY_NUMBER_RE, + relevance: 0 +}; +hljs.CSS_NUMBER_MODE = { + className: 'number', + begin: hljs.NUMBER_RE + '(' + + '%|em|ex|ch|rem' + + '|vw|vh|vmin|vmax' + + '|cm|mm|in|pt|pc|px' + + '|deg|grad|rad|turn' + + '|s|ms' + + '|Hz|kHz' + + '|dpi|dpcm|dppx' + + ')?', + relevance: 0 +}; +hljs.REGEXP_MODE = { + className: 'regexp', + begin: /\//, end: /\/[gimuy]*/, + illegal: /\n/, + contains: [ + hljs.BACKSLASH_ESCAPE, + { + begin: /\[/, end: /\]/, + relevance: 0, + contains: [hljs.BACKSLASH_ESCAPE] + } + ] +}; +hljs.TITLE_MODE = { + className: 'title', + begin: hljs.IDENT_RE, + relevance: 0 +}; +hljs.UNDERSCORE_TITLE_MODE = { + className: 'title', + begin: hljs.UNDERSCORE_IDENT_RE, + relevance: 0 +}; +hljs.METHOD_GUARD = { + // excludes method names from keyword processing + begin: '\\.\\s*' + hljs.UNDERSCORE_IDENT_RE, + relevance: 0 +}; + +hljs.registerLanguage("sql", function (hljs) { + var COMMENT_MODE = hljs.COMMENT('--', '$'); + return { + case_insensitive: true, + illegal: /[<>{}*#]/, + contains: [ + { + beginKeywords: + 'begin end start commit rollback savepoint lock alter create drop rename call ' + + 'delete do handler insert load replace select truncate update set show pragma grant ' + + 'merge describe use explain help declare prepare execute deallocate release ' + + 'unlock purge reset change stop analyze cache flush optimize repair kill ' + + 'install uninstall checksum restore check backup revoke comment', + end: /;/, endsWithParent: true, + lexemes: /[\w\.]+/, + keywords: { + keyword: + 'abort abs absolute acc acce accep accept access accessed accessible account acos action activate add ' + + 'addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias ' + + 'allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply ' + + 'archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan ' + + 'atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid ' + + 'authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile ' + + 'before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float ' + + 'binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound ' + + 'buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel ' + + 'capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base ' + + 'char_length character_length characters characterset charindex charset charsetform charsetid check ' + + 'checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close ' + + 'cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation ' + + 'collect colu colum column column_value columns columns_updated comment commit compact compatibility ' + + 'compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn ' + + 'connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection ' + + 'consider consistent constant constraint constraints constructor container content contents context ' + + 'contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost ' + + 'count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation ' + + 'critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user ' + + 'cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add ' + + 'date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts ' + + 'day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate ' + + 'declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults ' + + 'deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank ' + + 'depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor ' + + 'deterministic diagnostics difference dimension direct_load directory disable disable_all ' + + 'disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div ' + + 'do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable ' + + 'editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt ' + + 'end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors ' + + 'escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding ' + + 'execu execut execute exempt exists exit exp expire explain export export_set extended extent external ' + + 'external_1 external_2 externally extract failed failed_login_attempts failover failure far fast ' + + 'feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final ' + + 'finish first first_value fixed flash_cache flashback floor flush following follows for forall force ' + + 'form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ' + + 'ftp full function general generated get get_format get_lock getdate getutcdate global global_name ' + + 'globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups ' + + 'gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex ' + + 'hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified ' + + 'identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment ' + + 'index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile ' + + 'initial initialized initially initrans inmemory inner innodb input insert install instance instantiable ' + + 'instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat ' + + 'is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists ' + + 'keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase ' + + 'lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit ' + + 'lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate ' + + 'locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call ' + + 'logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime ' + + 'managed management manual map mapping mask master master_pos_wait match matched materialized max ' + + 'maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans ' + + 'md5 measures median medium member memcompress memory merge microsecond mid migration min minextents ' + + 'minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month ' + + 'months mount move movement multiset mutex name name_const names nan national native natural nav nchar ' + + 'nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile ' + + 'nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile ' + + 'nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder ' + + 'nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck ' + + 'noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe ' + + 'nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ' + + 'ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old ' + + 'on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date ' + + 'oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary ' + + 'out outer outfile outline output over overflow overriding package pad parallel parallel_enable ' + + 'parameters parent parse partial partition partitions pascal passing password password_grace_time ' + + 'password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex ' + + 'pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc ' + + 'performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin ' + + 'policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction ' + + 'prediction_cost prediction_details prediction_probability prediction_set prepare present preserve ' + + 'prior priority private private_sga privileges procedural procedure procedure_analyze processlist ' + + 'profiles project prompt protection public publishingservername purge quarter query quick quiesce quota ' + + 'quotename radians raise rand range rank raw read reads readsize rebuild record records ' + + 'recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh ' + + 'regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy ' + + 'reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename ' + + 'repair repeat replace replicate replication required reset resetlogs resize resource respect restore ' + + 'restricted result result_cache resumable resume retention return returning returns reuse reverse revoke ' + + 'right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows ' + + 'rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll ' + + 'sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select ' + + 'self sequence sequential serializable server servererror session session_user sessions_per_user set ' + + 'sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor ' + + 'si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin ' + + 'size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex ' + + 'source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows ' + + 'sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone ' + + 'standby start starting startup statement static statistics stats_binomial_test stats_crosstab ' + + 'stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep ' + + 'stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev ' + + 'stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate ' + + 'subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum ' + + 'suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate ' + + 'sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo ' + + 'template temporary terminated tertiary_weights test than then thread through tier ties time time_format ' + + 'time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr ' + + 'timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking ' + + 'transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate ' + + 'try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress ' + + 'under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot ' + + 'unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert ' + + 'url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date ' + + 'utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var ' + + 'var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray ' + + 'verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear ' + + 'wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped ' + + 'xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces ' + + 'xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek', + literal: + 'true false null', + built_in: + 'array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number ' + + 'numeric real record serial serial8 smallint text varchar varying void' + }, + contains: [ + { + className: 'string', + begin: '\'', end: '\'', + contains: [hljs.BACKSLASH_ESCAPE, { begin: '\'\'' }] + }, + { + className: 'string', + begin: '"', end: '"', + contains: [hljs.BACKSLASH_ESCAPE, { begin: '""' }] + }, + { + className: 'string', + begin: '`', end: '`', + contains: [hljs.BACKSLASH_ESCAPE] + }, + hljs.C_NUMBER_MODE, + hljs.C_BLOCK_COMMENT_MODE, + COMMENT_MODE + ] + }, + hljs.C_BLOCK_COMMENT_MODE, + COMMENT_MODE + ] + }; +}); \ No newline at end of file diff --git a/DocTools/TplFile/chm/embed/sql-formatter.js b/DocTools/TplFile/chm/embed/sql-formatter.js new file mode 100644 index 0000000..76d0c78 --- /dev/null +++ b/DocTools/TplFile/chm/embed/sql-formatter.js @@ -0,0 +1,25 @@ +!function (t, n) { "object" == typeof exports && "object" == typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define([], n) : "object" == typeof exports ? exports.sqlFormatter = n() : t.sqlFormatter = n() } + (this, function () { + return function (t) { function n(r) { if (e[r]) return e[r].exports; var u = e[r] = { exports: {}, id: r, loaded: !1 }; return t[r].call(u.exports, u, u.exports, n), u.loaded = !0, u.exports } var e = {}; return n.m = t, n.c = e, n.p = "", n(0) }([function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(9), i = r(u), o = e(10), a = r(o), f = e(11), c = r(f); n.default = { format: function (t, n) { switch (n = n || {}, n.language) { case "db2": return new i.default(n).format(t); case "n1ql": return new a.default(n).format(t); default: return new c.default(n).format(t) } } }, t.exports = n.default }, function (t, n) { "use strict"; n.__esModule = !0, n.default = function (t, n) { if (!(t instanceof n)) throw new TypeError("Cannot call a class as a function") } }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(5), a = r(o), f = e(4), c = r(f), l = e(6), s = r(l), E = e(7), p = r(E), h = e(8), R = r(h), _ = function () { function t(n, e) { (0, i.default)(this, t), this.cfg = n || {}, this.indentation = new s.default(this.cfg.indent), this.inlineBlock = new p.default, this.params = new R.default(this.cfg.params), this.tokenizer = e, this.previousReservedWord = {} } return t.prototype.format = function (t) { var n = this.tokenizer.tokenize(t), e = this.getFormattedQueryFromTokens(n); return e.trim() + "\n" }, t.prototype.getFormattedQueryFromTokens = function (t) { var n = this, e = ""; return t.forEach(function (r, u) { r.type !== c.default.WHITESPACE && (r.type === c.default.LINE_COMMENT ? e = n.formatLineComment(r, e) : r.type === c.default.BLOCK_COMMENT ? e = n.formatBlockComment(r, e) : r.type === c.default.RESERVED_TOPLEVEL ? (e = n.formatToplevelReservedWord(r, e), n.previousReservedWord = r) : r.type === c.default.RESERVED_NEWLINE ? (e = n.formatNewlineReservedWord(r, e), n.previousReservedWord = r) : r.type === c.default.RESERVED ? (e = n.formatWithSpaces(r, e), n.previousReservedWord = r) : e = r.type === c.default.OPEN_PAREN ? n.formatOpeningParentheses(t, u, e) : r.type === c.default.CLOSE_PAREN ? n.formatClosingParentheses(r, e) : r.type === c.default.PLACEHOLDER ? n.formatPlaceholder(r, e) : "," === r.value ? n.formatComma(r, e) : ":" === r.value ? n.formatWithSpaceAfter(r, e) : "." === r.value || ";" === r.value ? n.formatWithoutSpaces(r, e) : n.formatWithSpaces(r, e)) }), e }, t.prototype.formatLineComment = function (t, n) { return this.addNewline(n + t.value) }, t.prototype.formatBlockComment = function (t, n) { return this.addNewline(this.addNewline(n) + this.indentComment(t.value)) }, t.prototype.indentComment = function (t) { return t.replace(/\n/g, "\n" + this.indentation.getIndent()) }, t.prototype.formatToplevelReservedWord = function (t, n) { return this.indentation.decreaseTopLevel(), n = this.addNewline(n), this.indentation.increaseToplevel(), n += this.equalizeWhitespace(t.value), this.addNewline(n) }, t.prototype.formatNewlineReservedWord = function (t, n) { return this.addNewline(n) + this.equalizeWhitespace(t.value) + " " }, t.prototype.equalizeWhitespace = function (t) { return t.replace(/\s+/g, " ") }, t.prototype.formatOpeningParentheses = function (t, n, e) { var r = t[n - 1]; return r && r.type !== c.default.WHITESPACE && r.type !== c.default.OPEN_PAREN && (e = a.default.trimEnd(e)), e += t[n].value, this.inlineBlock.beginIfPossible(t, n), this.inlineBlock.isActive() || (this.indentation.increaseBlockLevel(), e = this.addNewline(e)), e }, t.prototype.formatClosingParentheses = function (t, n) { return this.inlineBlock.isActive() ? (this.inlineBlock.end(), this.formatWithSpaceAfter(t, n)) : (this.indentation.decreaseBlockLevel(), this.formatWithSpaces(t, this.addNewline(n))) }, t.prototype.formatPlaceholder = function (t, n) { return n + this.params.get(t) + " " }, t.prototype.formatComma = function (t, n) { return n = a.default.trimEnd(n) + t.value + " ", this.inlineBlock.isActive() ? n : /^LIMIT$/i.test(this.previousReservedWord.value) ? n : this.addNewline(n) }, t.prototype.formatWithSpaceAfter = function (t, n) { return a.default.trimEnd(n) + t.value + " " }, t.prototype.formatWithoutSpaces = function (t, n) { return a.default.trimEnd(n) + t.value }, t.prototype.formatWithSpaces = function (t, n) { return n + t.value + " " }, t.prototype.addNewline = function (t) { return a.default.trimEnd(t) + "\n" + this.indentation.getIndent() }, t }(); n.default = _, t.exports = n.default }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(5), a = r(o), f = e(4), c = r(f), l = function () { function t(n) { (0, i.default)(this, t), this.WHITESPACE_REGEX = /^(\s+)/, this.NUMBER_REGEX = /^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/, this.OPERATOR_REGEX = /^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|.)/, this.BLOCK_COMMENT_REGEX = /^(\/\*\*?(?:\*\/|$))/u, this.LINE_COMMENT_REGEX = this.createLineCommentRegex(n.lineCommentTypes), this.RESERVED_TOPLEVEL_REGEX = this.createReservedWordRegex(n.reservedToplevelWords), this.RESERVED_NEWLINE_REGEX = this.createReservedWordRegex(n.reservedNewlineWords), this.RESERVED_PLAIN_REGEX = this.createReservedWordRegex(n.reservedWords), this.WORD_REGEX = this.createWordRegex(n.specialWordChars), this.STRING_REGEX = this.createStringRegex(n.stringTypes), this.OPEN_PAREN_REGEX = this.createParenRegex(n.openParens), this.CLOSE_PAREN_REGEX = this.createParenRegex(n.closeParens), this.INDEXED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(n.indexedPlaceholderTypes, "[0-9]*"), this.IDENT_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(n.namedPlaceholderTypes, "[a-zA-Z0-9._$]+"), this.STRING_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(n.namedPlaceholderTypes, this.createStringPattern(n.stringTypes)) } return t.prototype.createLineCommentRegex = function (t) { return RegExp("^((?:" + t.map(function (t) { return a.default.escapeRegExp(t) }).join("|") + ").*?(?:\n|$))") }, t.prototype.createReservedWordRegex = function (t) { var n = t.join("|").replace(/ /g, "\\s+"); return RegExp("^(" + n + ")\\b", "i") }, t.prototype.createWordRegex = function () { var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : []; return RegExp("^([\\w" + t.join("") + "]+)") }, t.prototype.createStringRegex = function (t) { return RegExp("^(" + this.createStringPattern(t) + ")") }, t.prototype.createStringPattern = function (t) { var n = { "``": "((`[^`]*($|`))+)", "[]": "((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)", '""': '(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)', "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", "N''": "((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)" }; return t.map(function (t) { return n[t] }).join("|") }, t.prototype.createParenRegex = function (t) { return RegExp("^(" + t.map(function (t) { return a.default.escapeRegExp(t) }).join("|") + ")") }, t.prototype.createPlaceholderRegex = function (t, n) { if (a.default.isEmpty(t)) return !1; var e = t.map(a.default.escapeRegExp).join("|"); return RegExp("^((?:" + e + ")(?:" + n + "))") }, t.prototype.tokenize = function (t) { for (var n = [], e = void 0; t.length;)e = this.getNextToken(t, e), t = t.substring(e.value.length), n.push(e); return n }, t.prototype.getNextToken = function (t, n) { return this.getWhitespaceToken(t) || this.getCommentToken(t) || this.getStringToken(t) || this.getOpenParenToken(t) || this.getCloseParenToken(t) || this.getPlaceholderToken(t) || this.getNumberToken(t) || this.getReservedWordToken(t, n) || this.getWordToken(t) || this.getOperatorToken(t) }, t.prototype.getWhitespaceToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.WHITESPACE, regex: this.WHITESPACE_REGEX }) }, t.prototype.getCommentToken = function (t) { return this.getLineCommentToken(t) || this.getBlockCommentToken(t) }, t.prototype.getLineCommentToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.LINE_COMMENT, regex: this.LINE_COMMENT_REGEX }) }, t.prototype.getBlockCommentToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.BLOCK_COMMENT, regex: this.BLOCK_COMMENT_REGEX }) }, t.prototype.getStringToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.STRING, regex: this.STRING_REGEX }) }, t.prototype.getOpenParenToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.OPEN_PAREN, regex: this.OPEN_PAREN_REGEX }) }, t.prototype.getCloseParenToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.CLOSE_PAREN, regex: this.CLOSE_PAREN_REGEX }) }, t.prototype.getPlaceholderToken = function (t) { return this.getIdentNamedPlaceholderToken(t) || this.getStringNamedPlaceholderToken(t) || this.getIndexedPlaceholderToken(t) }, t.prototype.getIdentNamedPlaceholderToken = function (t) { return this.getPlaceholderTokenWithKey({ input: t, regex: this.IDENT_NAMED_PLACEHOLDER_REGEX, parseKey: function (t) { return t.slice(1) } }) }, t.prototype.getStringNamedPlaceholderToken = function (t) { var n = this; return this.getPlaceholderTokenWithKey({ input: t, regex: this.STRING_NAMED_PLACEHOLDER_REGEX, parseKey: function (t) { return n.getEscapedPlaceholderKey({ key: t.slice(2, -1), quoteChar: t.slice(-1) }) } }) }, t.prototype.getIndexedPlaceholderToken = function (t) { return this.getPlaceholderTokenWithKey({ input: t, regex: this.INDEXED_PLACEHOLDER_REGEX, parseKey: function (t) { return t.slice(1) } }) }, t.prototype.getPlaceholderTokenWithKey = function (t) { var n = t.input, e = t.regex, r = t.parseKey, u = this.getTokenOnFirstMatch({ input: n, regex: e, type: c.default.PLACEHOLDER }); return u && (u.key = r(u.value)), u }, t.prototype.getEscapedPlaceholderKey = function (t) { var n = t.key, e = t.quoteChar; return n.replace(RegExp(a.default.escapeRegExp("\\") + e, "g"), e) }, t.prototype.getNumberToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.NUMBER, regex: this.NUMBER_REGEX }) }, t.prototype.getOperatorToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.OPERATOR, regex: this.OPERATOR_REGEX }) }, t.prototype.getReservedWordToken = function (t, n) { if (!n || !n.value || "." !== n.value) return this.getToplevelReservedToken(t) || this.getNewlineReservedToken(t) || this.getPlainReservedToken(t) }, t.prototype.getToplevelReservedToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.RESERVED_TOPLEVEL, regex: this.RESERVED_TOPLEVEL_REGEX }) }, t.prototype.getNewlineReservedToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.RESERVED_NEWLINE, regex: this.RESERVED_NEWLINE_REGEX }) }, t.prototype.getPlainReservedToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.RESERVED, regex: this.RESERVED_PLAIN_REGEX }) }, t.prototype.getWordToken = function (t) { return this.getTokenOnFirstMatch({ input: t, type: c.default.WORD, regex: this.WORD_REGEX }) }, t.prototype.getTokenOnFirstMatch = function (t) { var n = t.input, e = t.type, r = t.regex, u = n.match(r); if (u) return { type: e, value: u[1] } }, t }(); n.default = l, t.exports = n.default }, function (t, n) { "use strict"; n.__esModule = !0, n.default = { WHITESPACE: "whitespace", WORD: "word", STRING: "string", RESERVED: "reserved", RESERVED_TOPLEVEL: "reserved-toplevel", RESERVED_NEWLINE: "reserved-newline", OPERATOR: "operator", OPEN_PAREN: "open-paren", CLOSE_PAREN: "close-paren", LINE_COMMENT: "line-comment", BLOCK_COMMENT: "block-comment", NUMBER: "number", PLACEHOLDER: "placeholder" }, t.exports = n.default }, function (t, n, e) { + var r; (function (t, u) { + (function () { + function i(t, n) { return t.set(n[0], n[1]), t } function o(t, n) { return t.add(n), t } function a(t, n, e) { switch (e.length) { case 0: return t.call(n); case 1: return t.call(n, e[0]); case 2: return t.call(n, e[0], e[1]); case 3: return t.call(n, e[0], e[1], e[2]) }return t.apply(n, e) } function f(t, n, e, r) { for (var u = -1, i = null == t ? 0 : t.length; ++u < i;) { var o = t[u]; n(r, o, e(o), t) } return r } function c(t, n) { for (var e = -1, r = null == t ? 0 : t.length; ++e < r && n(t[e], e, t) !== !1;); return t } function l(t, n) { for (var e = null == t ? 0 : t.length; e-- && n(t[e], e, t) !== !1;); return t } function s(t, n) { for (var e = -1, r = null == t ? 0 : t.length; ++e < r;)if (!n(t[e], e, t)) return !1; return !0 } function E(t, n) { for (var e = -1, r = null == t ? 0 : t.length, u = 0, i = []; ++e < r;) { var o = t[e]; n(o, e, t) && (i[u++] = o) } return i } function p(t, n) { var e = null == t ? 0 : t.length; return !!e && g(t, n, 0) > -1 } function h(t, n, e) { for (var r = -1, u = null == t ? 0 : t.length; ++r < u;)if (e(n, t[r])) return !0; return !1 } function R(t, n) { for (var e = -1, r = null == t ? 0 : t.length, u = Array(r); ++e < r;)u[e] = n(t[e], e, t); return u } function _(t, n) { for (var e = -1, r = n.length, u = t.length; ++e < r;)t[u + e] = n[e]; return t } function T(t, n, e, r) { var u = -1, i = null == t ? 0 : t.length; for (r && i && (e = t[++u]); ++u < i;)e = n(e, t[u], u, t); return e } function v(t, n, e, r) { var u = null == t ? 0 : t.length; for (r && u && (e = t[--u]); u--;)e = n(e, t[u], u, t); return e } function N(t, n) { for (var e = -1, r = null == t ? 0 : t.length; ++e < r;)if (n(t[e], e, t)) return !0; return !1 } function A(t) { return t.split("") } function I(t) { return t.match(Yn) || [] } function O(t, n, e) { var r; return e(t, function (t, e, u) { if (n(t, e, u)) return r = e, !1 }), r } function d(t, n, e, r) { for (var u = t.length, i = e + (r ? 1 : -1); r ? i-- : ++i < u;)if (n(t[i], i, t)) return i; return -1 } function g(t, n, e) { return n === n ? J(t, n, e) : d(t, L, e) } function S(t, n, e, r) { for (var u = e - 1, i = t.length; ++u < i;)if (r(t[u], n)) return u; return -1 } function L(t) { return t !== t } function y(t, n) { var e = null == t ? 0 : t.length; return e ? M(t, n) / e : xt } function C(t) { return function (n) { return null == n ? ut : n[t] } } function P(t) { return function (n) { return null == t ? ut : t[n] } } function D(t, n, e, r, u) { return u(t, function (t, u, i) { e = r ? (r = !1, t) : n(e, t, u, i) }), e } function U(t, n) { var e = t.length; for (t.sort(n); e--;)t[e] = t[e].value; return t } function M(t, n) { for (var e, r = -1, u = t.length; ++r < u;) { var i = n(t[r]); i !== ut && (e = e === ut ? i : e + i) } return e } function m(t, n) { for (var e = -1, r = Array(t); ++e < t;)r[e] = n(e); return r } function w(t, n) { return R(n, function (n) { return [n, t[n]] }) } function b(t) { return function (n) { return t(n) } } function G(t, n) { return R(n, function (n) { return t[n] }) } function x(t, n) { return t.has(n) } function W(t, n) { for (var e = -1, r = t.length; ++e < r && g(n, t[e], 0) > -1;); return e } function F(t, n) { for (var e = t.length; e-- && g(n, t[e], 0) > -1;); return e } function B(t, n) { for (var e = t.length, r = 0; e--;)t[e] === n && ++r; return r } function k(t) { return "\\" + er[t] } function H(t, n) { return null == t ? ut : t[n] } function V(t) { return Ke.test(t) } function Y(t) { return ze.test(t) } function j(t) { for (var n, e = []; !(n = t.next()).done;)e.push(n.value); return e } function X(t) { var n = -1, e = Array(t.size); return t.forEach(function (t, r) { e[++n] = [r, t] }), e } function K(t, n) { return function (e) { return t(n(e)) } } function z(t, n) { for (var e = -1, r = t.length, u = 0, i = []; ++e < r;) { var o = t[e]; o !== n && o !== st || (t[e] = st, i[u++] = e) } return i } function Q(t) { var n = -1, e = Array(t.size); return t.forEach(function (t) { e[++n] = t }), e } function $(t) { var n = -1, e = Array(t.size); return t.forEach(function (t) { e[++n] = [t, t] }), e } function J(t, n, e) { for (var r = e - 1, u = t.length; ++r < u;)if (t[r] === n) return r; return -1 } function Z(t, n, e) { for (var r = e + 1; r--;)if (t[r] === n) return r; return r } function q(t) { return V(t) ? nt(t) : Nr(t) } function tt(t) { return V(t) ? et(t) : A(t) } function nt(t) { for (var n = je.lastIndex = 0; je.test(t);)++n; return n } function et(t) { return t.match(je) || [] } function rt(t) { return t.match(Xe) || [] } var ut, it = "4.17.4", ot = 200, at = "Unsupported core-js use. Try https://npms.io/search?q=ponyfill.", ft = "Expected a function", ct = "__lodash_hash_undefined__", lt = 500, st = "__lodash_placeholder__", Et = 1, pt = 2, ht = 4, Rt = 1, _t = 2, Tt = 1, vt = 2, Nt = 4, At = 8, It = 16, Ot = 32, dt = 64, gt = 128, St = 256, Lt = 512, yt = 30, Ct = "...", Pt = 800, Dt = 16, Ut = 1, Mt = 2, mt = 3, wt = 1 / 0, bt = 9007199254740991, Gt = 1.7976931348623157e308, xt = NaN, Wt = 4294967295, Ft = Wt - 1, Bt = Wt >>> 1, kt = [["ary", gt], ["bind", Tt], ["bindKey", vt], ["curry", At], ["curryRight", It], ["flip", Lt], ["partial", Ot], ["partialRight", dt], ["rearg", St]], Ht = "[object Arguments]", Vt = "[object Array]", Yt = "[object AsyncFunction]", jt = "[object Boolean]", Xt = "[object Date]", Kt = "[object DOMException]", zt = "[object Error]", Qt = "[object Function]", $t = "[object GeneratorFunction]", Jt = "[object Map]", Zt = "[object Number]", qt = "[object Null]", tn = "[object Object]", nn = "[object Promise]", en = "[object Proxy]", rn = "[object RegExp]", un = "[object Set]", on = "[object String]", an = "[object Symbol]", fn = "[object Undefined]", cn = "[object WeakMap]", ln = "[object WeakSet]", sn = "[object ArrayBuffer]", En = "[object DataView]", pn = "[object Float32Array]", hn = "[object Float64Array]", Rn = "[object Int8Array]", _n = "[object Int16Array]", Tn = "[object Int32Array]", vn = "[object Uint8Array]", Nn = "[object Uint8ClampedArray]", An = "[object Uint16Array]", In = "[object Uint32Array]", On = /\b__p \+= '';/g, dn = /\b(__p \+=) '' \+/g, gn = /(__e\(.*?\)|\b__t\)) \+\n'';/g, Sn = /&(?:amp|lt|gt|quot|#39);/g, Ln = /[&<>"']/g, yn = RegExp(Sn.source), Cn = RegExp(Ln.source), Pn = /<%-([\s\S]+?)%>/g, Dn = /<%([\s\S]+?)%>/g, Un = /<%=([\s\S]+?)%>/g, Mn = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, mn = /^\w*$/, wn = /^\./, bn = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g, Gn = /[\\^$.*+?()[\]{}|]/g, xn = RegExp(Gn.source), Wn = /^\s+|\s+$/g, Fn = /^\s+/, Bn = /\s+$/, kn = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, Hn = /\{\n\/\* \[wrapped with (.+)\] \*/, Vn = /,? & /, Yn = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g, jn = /\\(\\)?/g, Xn = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g, Kn = /\w*$/, zn = /^[-+]0x[0-9a-f]+$/i, Qn = /^0b[01]+$/i, $n = /^\[object .+?Constructor\]$/, Jn = /^0o[0-7]+$/i, Zn = /^(?:0|[1-9]\d*)$/, qn = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g, te = /($^)/, ne = /['\n\r\u2028\u2029\\]/g, ee = "\\ud800-\\udfff", re = "\\u0300-\\u036f", ue = "\\ufe20-\\ufe2f", ie = "\\u20d0-\\u20ff", oe = re + ue + ie, ae = "\\u2700-\\u27bf", fe = "a-z\\xdf-\\xf6\\xf8-\\xff", ce = "\\xac\\xb1\\xd7\\xf7", le = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf", se = "\\u2000-\\u206f", Ee = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000", pe = "A-Z\\xc0-\\xd6\\xd8-\\xde", he = "\\ufe0e\\ufe0f", Re = ce + le + se + Ee, _e = "['’]", Te = "[" + ee + "]", ve = "[" + Re + "]", Ne = "[" + oe + "]", Ae = "\\d+", Ie = "[" + ae + "]", Oe = "[" + fe + "]", de = "[^" + ee + Re + Ae + ae + fe + pe + "]", ge = "\\ud83c[\\udffb-\\udfff]", Se = "(?:" + Ne + "|" + ge + ")", Le = "[^" + ee + "]", ye = "(?:\\ud83c[\\udde6-\\uddff]){2}", Ce = "[\\ud800-\\udbff][\\udc00-\\udfff]", Pe = "[" + pe + "]", De = "\\u200d", Ue = "(?:" + Oe + "|" + de + ")", Me = "(?:" + Pe + "|" + de + ")", me = "(?:" + _e + "(?:d|ll|m|re|s|t|ve))?", we = "(?:" + _e + "(?:D|LL|M|RE|S|T|VE))?", be = Se + "?", Ge = "[" + he + "]?", xe = "(?:" + De + "(?:" + [Le, ye, Ce].join("|") + ")" + Ge + be + ")*", We = "\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)", Fe = "\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)", Be = Ge + be + xe, ke = "(?:" + [Ie, ye, Ce].join("|") + ")" + Be, He = "(?:" + [Le + Ne + "?", Ne, ye, Ce, Te].join("|") + ")", Ve = RegExp(_e, "g"), Ye = RegExp(Ne, "g"), je = RegExp(ge + "(?=" + ge + ")|" + He + Be, "g"), Xe = RegExp([Pe + "?" + Oe + "+" + me + "(?=" + [ve, Pe, "$"].join("|") + ")", Me + "+" + we + "(?=" + [ve, Pe + Ue, "$"].join("|") + ")", Pe + "?" + Ue + "+" + me, Pe + "+" + we, Fe, We, Ae, ke].join("|"), "g"), Ke = RegExp("[" + De + ee + oe + he + "]"), ze = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/, Qe = ["Array", "Buffer", "DataView", "Date", "Error", "Float32Array", "Float64Array", "Function", "Int8Array", "Int16Array", "Int32Array", "Map", "Math", "Object", "Promise", "RegExp", "Set", "String", "Symbol", "TypeError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "WeakMap", "_", "clearTimeout", "isFinite", "parseInt", "setTimeout"], $e = -1, Je = {}; Je[pn] = Je[hn] = Je[Rn] = Je[_n] = Je[Tn] = Je[vn] = Je[Nn] = Je[An] = Je[In] = !0, Je[Ht] = Je[Vt] = Je[sn] = Je[jt] = Je[En] = Je[Xt] = Je[zt] = Je[Qt] = Je[Jt] = Je[Zt] = Je[tn] = Je[rn] = Je[un] = Je[on] = Je[cn] = !1; var Ze = {}; Ze[Ht] = Ze[Vt] = Ze[sn] = Ze[En] = Ze[jt] = Ze[Xt] = Ze[pn] = Ze[hn] = Ze[Rn] = Ze[_n] = Ze[Tn] = Ze[Jt] = Ze[Zt] = Ze[tn] = Ze[rn] = Ze[un] = Ze[on] = Ze[an] = Ze[vn] = Ze[Nn] = Ze[An] = Ze[In] = !0, Ze[zt] = Ze[Qt] = Ze[cn] = !1; var qe = { "À": "A", "Á": "A", "Â": "A", "Ã": "A", "Ä": "A", "Å": "A", "à": "a", "á": "a", "â": "a", "ã": "a", "ä": "a", "å": "a", "Ç": "C", "ç": "c", "Ð": "D", "ð": "d", "È": "E", "É": "E", "Ê": "E", "Ë": "E", "è": "e", "é": "e", "ê": "e", "ë": "e", "Ì": "I", "Í": "I", "Î": "I", "Ï": "I", "ì": "i", "í": "i", "î": "i", "ï": "i", "Ñ": "N", "ñ": "n", "Ò": "O", "Ó": "O", "Ô": "O", "Õ": "O", "Ö": "O", "Ø": "O", "ò": "o", "ó": "o", "ô": "o", "õ": "o", "ö": "o", "ø": "o", "Ù": "U", "Ú": "U", "Û": "U", "Ü": "U", "ù": "u", "ú": "u", "û": "u", "ü": "u", "Ý": "Y", "ý": "y", "ÿ": "y", "Æ": "Ae", "æ": "ae", "Þ": "Th", "þ": "th", "ß": "ss", "Ā": "A", "Ă": "A", "Ą": "A", "ā": "a", "ă": "a", "ą": "a", "Ć": "C", "Ĉ": "C", "Ċ": "C", "Č": "C", "ć": "c", "ĉ": "c", "ċ": "c", "č": "c", "Ď": "D", "Đ": "D", "ď": "d", "đ": "d", "Ē": "E", "Ĕ": "E", "Ė": "E", "Ę": "E", "Ě": "E", "ē": "e", "ĕ": "e", "ė": "e", "ę": "e", "ě": "e", "Ĝ": "G", "Ğ": "G", "Ġ": "G", "Ģ": "G", "ĝ": "g", "ğ": "g", "ġ": "g", "ģ": "g", "Ĥ": "H", "Ħ": "H", "ĥ": "h", "ħ": "h", "Ĩ": "I", "Ī": "I", "Ĭ": "I", "Į": "I", "İ": "I", "ĩ": "i", "ī": "i", "ĭ": "i", "į": "i", "ı": "i", "Ĵ": "J", "ĵ": "j", "Ķ": "K", "ķ": "k", "ĸ": "k", "Ĺ": "L", "Ļ": "L", "Ľ": "L", "Ŀ": "L", "Ł": "L", "ĺ": "l", "ļ": "l", "ľ": "l", "ŀ": "l", "ł": "l", "Ń": "N", "Ņ": "N", "Ň": "N", "Ŋ": "N", "ń": "n", "ņ": "n", "ň": "n", "ŋ": "n", "Ō": "O", "Ŏ": "O", "Ő": "O", "ō": "o", "ŏ": "o", "ő": "o", "Ŕ": "R", "Ŗ": "R", "Ř": "R", "ŕ": "r", "ŗ": "r", "ř": "r", "Ś": "S", "Ŝ": "S", "Ş": "S", "Š": "S", "ś": "s", "ŝ": "s", "ş": "s", "š": "s", "Ţ": "T", "Ť": "T", "Ŧ": "T", "ţ": "t", "ť": "t", "ŧ": "t", "Ũ": "U", "Ū": "U", "Ŭ": "U", "Ů": "U", "Ű": "U", "Ų": "U", "ũ": "u", "ū": "u", "ŭ": "u", "ů": "u", "ű": "u", "ų": "u", "Ŵ": "W", "ŵ": "w", "Ŷ": "Y", "ŷ": "y", "Ÿ": "Y", "Ź": "Z", "Ż": "Z", "Ž": "Z", "ź": "z", "ż": "z", "ž": "z", "IJ": "IJ", "ij": "ij", "Œ": "Oe", "œ": "oe", "ʼn": "'n", "ſ": "s" }, tr = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, nr = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" }, er = { "\\": "\\", "'": "'", "\n": "n", "\r": "r", "\u2028": "u2028", "\u2029": "u2029" }, rr = parseFloat, ur = parseInt, ir = "object" == typeof t && t && t.Object === Object && t, or = "object" == typeof self && self && self.Object === Object && self, ar = ir || or || Function("return this")(), fr = "object" == typeof n && n && !n.nodeType && n, cr = fr && "object" == typeof u && u && !u.nodeType && u, lr = cr && cr.exports === fr, sr = lr && ir.process, Er = function () { try { return sr && sr.binding && sr.binding("util") } catch (t) { } }(), pr = Er && Er.isArrayBuffer, hr = Er && Er.isDate, Rr = Er && Er.isMap, _r = Er && Er.isRegExp, Tr = Er && Er.isSet, vr = Er && Er.isTypedArray, Nr = C("length"), Ar = P(qe), Ir = P(tr), Or = P(nr), dr = function t(n) { + function e(t) { if (lf(t) && !IE(t) && !(t instanceof A)) { if (t instanceof u) return t; if (Il.call(t, "__wrapped__")) return io(t) } return new u(t) } function r() { } function u(t, n) { this.__wrapped__ = t, this.__actions__ = [], this.__chain__ = !!n, this.__index__ = 0, this.__values__ = ut } function A(t) { this.__wrapped__ = t, this.__actions__ = [], this.__dir__ = 1, this.__filtered__ = !1, this.__iteratees__ = [], this.__takeCount__ = Wt, this.__views__ = [] } function P() { var t = new A(this.__wrapped__); return t.__actions__ = Bu(this.__actions__), t.__dir__ = this.__dir__, t.__filtered__ = this.__filtered__, t.__iteratees__ = Bu(this.__iteratees__), t.__takeCount__ = this.__takeCount__, t.__views__ = Bu(this.__views__), t } function J() { if (this.__filtered__) { var t = new A(this); t.__dir__ = -1, t.__filtered__ = !0 } else t = this.clone(), t.__dir__ *= -1; return t } function nt() { var t = this.__wrapped__.value(), n = this.__dir__, e = IE(t), r = 0 > n, u = e ? t.length : 0, i = Pi(0, u, this.__views__), o = i.start, a = i.end, f = a - o, c = r ? a : o - 1, l = this.__iteratees__, s = l.length, E = 0, p = Jl(f, this.__takeCount__); if (!e || !r && u == f && p == f) return Iu(t, this.__actions__); var h = []; t: for (; f-- && p > E;) { c += n; for (var R = -1, _ = t[c]; ++R < s;) { var T = l[R], v = T.iteratee, N = T.type, A = v(_); if (N == Mt) _ = A; else if (!A) { if (N == Ut) continue t; break t } } h[E++] = _ } return h } function et(t) { var n = -1, e = null == t ? 0 : t.length; for (this.clear(); ++n < e;) { var r = t[n]; this.set(r[0], r[1]) } } function Yn() { this.__data__ = as ? as(null) : {}, this.size = 0 } function ee(t) { var n = this.has(t) && delete this.__data__[t]; return this.size -= n ? 1 : 0, n } function re(t) { var n = this.__data__; if (as) { var e = n[t]; return e === ct ? ut : e } return Il.call(n, t) ? n[t] : ut } function ue(t) { var n = this.__data__; return as ? n[t] !== ut : Il.call(n, t) } function ie(t, n) { var e = this.__data__; return this.size += this.has(t) ? 0 : 1, e[t] = as && n === ut ? ct : n, this } function oe(t) { var n = -1, e = null == t ? 0 : t.length; for (this.clear(); ++n < e;) { var r = t[n]; this.set(r[0], r[1]) } } function ae() { this.__data__ = [], this.size = 0 } function fe(t) { var n = this.__data__, e = me(n, t); if (0 > e) return !1; var r = n.length - 1; return e == r ? n.pop() : bl.call(n, e, 1), --this.size, !0 } function ce(t) { var n = this.__data__, e = me(n, t); return 0 > e ? ut : n[e][1] } function le(t) { return me(this.__data__, t) > -1 } function se(t, n) { var e = this.__data__, r = me(e, t); return 0 > r ? (++this.size, e.push([t, n])) : e[r][1] = n, this } function Ee(t) { var n = -1, e = null == t ? 0 : t.length; for (this.clear(); ++n < e;) { var r = t[n]; this.set(r[0], r[1]) } } function pe() { this.size = 0, this.__data__ = { hash: new et, map: new (rs || oe), string: new et } } function he(t) { var n = Si(this, t).delete(t); return this.size -= n ? 1 : 0, n } function Re(t) { return Si(this, t).get(t) } function _e(t) { return Si(this, t).has(t) } function Te(t, n) { var e = Si(this, t), r = e.size; return e.set(t, n), this.size += e.size == r ? 0 : 1, this } function ve(t) { var n = -1, e = null == t ? 0 : t.length; for (this.__data__ = new Ee; ++n < e;)this.add(t[n]) } function Ne(t) { return this.__data__.set(t, ct), this } function Ae(t) { return this.__data__.has(t) } function Ie(t) { var n = this.__data__ = new oe(t); this.size = n.size } function Oe() { this.__data__ = new oe, this.size = 0 } function de(t) { var n = this.__data__, e = n.delete(t); return this.size = n.size, e } function ge(t) { return this.__data__.get(t) } function Se(t) { return this.__data__.has(t) } function Le(t, n) { var e = this.__data__; if (e instanceof oe) { var r = e.__data__; if (!rs || ot - 1 > r.length) return r.push([t, n]), this.size = ++e.size, this; e = this.__data__ = new Ee(r) } return e.set(t, n), this.size = e.size, this } function ye(t, n) { var e = IE(t), r = !e && AE(t), u = !e && !r && dE(t), i = !e && !r && !u && CE(t), o = e || r || u || i, a = o ? m(t.length, hl) : [], f = a.length; for (var c in t) !n && !Il.call(t, c) || o && ("length" == c || u && ("offset" == c || "parent" == c) || i && ("buffer" == c || "byteLength" == c || "byteOffset" == c) || xi(c, f)) || a.push(c); return a } function Ce(t) { var n = t.length; return n ? t[eu(0, n - 1)] : ut } function Pe(t, n) { return no(Bu(t), Fe(n, 0, t.length)) } function De(t) { return no(Bu(t)) } function Ue(t, n, e) { (e === ut || Qa(t[n], e)) && (e !== ut || n in t) || xe(t, n, e) } function Me(t, n, e) { var r = t[n]; Il.call(t, n) && Qa(r, e) && (e !== ut || n in t) || xe(t, n, e) } function me(t, n) { for (var e = t.length; e--;)if (Qa(t[e][0], n)) return e; return -1 } function we(t, n, e, r) { return Ns(t, function (t, u, i) { n(r, t, e(t), i) }), r } function be(t, n) { return t && ku(n, jf(n), t) } function Ge(t, n) { return t && ku(n, Xf(n), t) } function xe(t, n, e) { "__proto__" == n && Fl ? Fl(t, n, { configurable: !0, enumerable: !0, value: e, writable: !0 }) : t[n] = e } function We(t, n) { for (var e = -1, r = n.length, u = al(r), i = null == t; ++e < r;)u[e] = i ? ut : Hf(t, n[e]); return u } function Fe(t, n, e) { return t === t && (e !== ut && (t = t > e ? e : t), n !== ut && (t = n > t ? n : t)), t } function Be(t, n, e, r, u, i) { var o, a = n & Et, f = n & pt, l = n & ht; if (e && (o = u ? e(t, r, u, i) : e(t)), o !== ut) return o; if (!cf(t)) return t; var s = IE(t); if (s) { if (o = Mi(t), !a) return Bu(t, o) } else { var E = Us(t), p = E == Qt || E == $t; if (dE(t)) return Cu(t, a); if (E == tn || E == Ht || p && !u) { if (o = f || p ? {} : mi(t), !a) return f ? Vu(t, Ge(o, t)) : Hu(t, be(o, t)) } else { if (!Ze[E]) return u ? t : {}; o = wi(t, E, Be, a) } } i || (i = new Ie); var h = i.get(t); if (h) return h; i.set(t, o); var R = l ? f ? Ii : Ai : f ? Xf : jf, _ = s ? ut : R(t); return c(_ || t, function (r, u) { _ && (u = r, r = t[u]), Me(o, u, Be(r, n, e, u, t, i)) }), o } function ke(t) { var n = jf(t); return function (e) { return He(e, t, n) } } function He(t, n, e) { var r = e.length; if (null == t) return !r; for (t = El(t); r--;) { var u = e[r], i = n[u], o = t[u]; if (o === ut && !(u in t) || !i(o)) return !1 } return !0 } function je(t, n, e) { if ("function" != typeof t) throw new Rl(ft); return ws(function () { t.apply(ut, e) }, n) } function Xe(t, n, e, r) { var u = -1, i = p, o = !0, a = t.length, f = [], c = n.length; if (!a) return f; e && (n = R(n, b(e))), r ? (i = h, o = !1) : ot > n.length || (i = x, o = !1, n = new ve(n)); t: for (; ++u < a;) { var l = t[u], s = null == e ? l : e(l); if (l = r || 0 !== l ? l : 0, o && s === s) { for (var E = c; E--;)if (n[E] === s) continue t; f.push(l) } else i(n, s, r) || f.push(l) } return f } function Ke(t, n) { var e = !0; return Ns(t, function (t, r, u) { return e = !!n(t, r, u) }), e } function ze(t, n, e) { for (var r = -1, u = t.length; ++r < u;) { var i = t[r], o = n(i); if (null != o && (a === ut ? o === o && !If(o) : e(o, a))) var a = o, f = i } return f } function qe(t, n, e, r) { var u = t.length; for (e = yf(e), 0 > e && (e = -e > u ? 0 : u + e), r = r === ut || r > u ? u : yf(r), 0 > r && (r += u), r = e > r ? 0 : Cf(r); r > e;)t[e++] = n; return t } function tr(t, n) { var e = []; return Ns(t, function (t, r, u) { n(t, r, u) && e.push(t) }), e } function nr(t, n, e, r, u) { var i = -1, o = t.length; for (e || (e = Gi), u || (u = []); ++i < o;) { var a = t[i]; n > 0 && e(a) ? n > 1 ? nr(a, n - 1, e, r, u) : _(u, a) : r || (u[u.length] = a) } return u } function er(t, n) { return t && Is(t, n, jf) } function ir(t, n) { return t && Os(t, n, jf) } function or(t, n) { return E(n, function (n) { return of(t[n]) }) } function fr(t, n) { n = Lu(n, t); for (var e = 0, r = n.length; null != t && r > e;)t = t[eo(n[e++])]; return e && e == r ? t : ut } function cr(t, n, e) { var r = n(t); return IE(t) ? r : _(r, e(t)) } function sr(t) { return null == t ? t === ut ? fn : qt : Wl && Wl in El(t) ? Ci(t) : Qi(t) } function Er(t, n) { return t > n } function Nr(t, n) { return null != t && Il.call(t, n) } function dr(t, n) { return null != t && n in El(t) } function Sr(t, n, e) { return t >= Jl(n, e) && t < $l(n, e) } function Lr(t, n, e) { for (var r = e ? h : p, u = t[0].length, i = t.length, o = i, a = al(i), f = 1 / 0, c = []; o--;) { var l = t[o]; o && n && (l = R(l, b(n))), f = Jl(l.length, f), a[o] = e || !n && (120 > u || 120 > l.length) ? ut : new ve(o && l) } l = t[0]; var s = -1, E = a[0]; t: for (; ++s < u && f > c.length;) { var _ = l[s], T = n ? n(_) : _; if (_ = e || 0 !== _ ? _ : 0, !(E ? x(E, T) : r(c, T, e))) { for (o = i; --o;) { var v = a[o]; if (!(v ? x(v, T) : r(t[o], T, e))) continue t } E && E.push(T), c.push(_) } } return c } function yr(t, n, e, r) { return er(t, function (t, u, i) { n(r, e(t), u, i) }), r } function Cr(t, n, e) { n = Lu(n, t), t = Ji(t, n); var r = null == t ? t : t[eo(So(n))]; return null == r ? ut : a(r, t, e) } function Pr(t) { return lf(t) && sr(t) == Ht } function Dr(t) { return lf(t) && sr(t) == sn } function Ur(t) { return lf(t) && sr(t) == Xt } function Mr(t, n, e, r, u) { return t === n || (null == t || null == n || !lf(t) && !lf(n) ? t !== t && n !== n : mr(t, n, e, r, Mr, u)) } function mr(t, n, e, r, u, i) { var o = IE(t), a = IE(n), f = o ? Vt : Us(t), c = a ? Vt : Us(n); f = f == Ht ? tn : f, c = c == Ht ? tn : c; var l = f == tn, s = c == tn, E = f == c; if (E && dE(t)) { if (!dE(n)) return !1; o = !0, l = !1 } if (E && !l) return i || (i = new Ie), o || CE(t) ? _i(t, n, e, r, u, i) : Ti(t, n, f, e, r, u, i); if (!(e & Rt)) { var p = l && Il.call(t, "__wrapped__"), h = s && Il.call(n, "__wrapped__"); if (p || h) { var R = p ? t.value() : t, _ = h ? n.value() : n; return i || (i = new Ie), u(R, _, e, r, i) } } return !!E && (i || (i = new Ie), vi(t, n, e, r, u, i)) } function wr(t) { return lf(t) && Us(t) == Jt } function br(t, n, e, r) { var u = e.length, i = u, o = !r; if (null == t) return !i; for (t = El(t); u--;) { var a = e[u]; if (o && a[2] ? a[1] !== t[a[0]] : !(a[0] in t)) return !1 } for (; ++u < i;) { a = e[u]; var f = a[0], c = t[f], l = a[1]; if (o && a[2]) { if (c === ut && !(f in t)) return !1 } else { var s = new Ie; if (r) var E = r(c, l, f, t, n, s); if (!(E === ut ? Mr(l, c, Rt | _t, r, s) : E)) return !1 } } return !0 } function Gr(t) { if (!cf(t) || Hi(t)) return !1; var n = of(t) ? yl : $n; return n.test(ro(t)) } function xr(t) { return lf(t) && sr(t) == rn } function Wr(t) { return lf(t) && Us(t) == un } function Fr(t) { return lf(t) && ff(t.length) && !!Je[sr(t)] } function Br(t) { return "function" == typeof t ? t : null == t ? bc : "object" == typeof t ? IE(t) ? Xr(t[0], t[1]) : jr(t) : Vc(t) } function kr(t) { if (!Vi(t)) return Ql(t); var n = []; for (var e in El(t)) Il.call(t, e) && "constructor" != e && n.push(e); return n } function Hr(t) { if (!cf(t)) return zi(t); var n = Vi(t), e = []; for (var r in t) ("constructor" != r || !n && Il.call(t, r)) && e.push(r); return e } function Vr(t, n) { return n > t } function Yr(t, n) { var e = -1, r = $a(t) ? al(t.length) : []; return Ns(t, function (t, u, i) { r[++e] = n(t, u, i) }), r } function jr(t) { var n = Li(t); return 1 == n.length && n[0][2] ? ji(n[0][0], n[0][1]) : function (e) { return e === t || br(e, t, n) } } function Xr(t, n) { return Fi(t) && Yi(n) ? ji(eo(t), n) : function (e) { var r = Hf(e, t); return r === ut && r === n ? Yf(e, t) : Mr(n, r, Rt | _t) } } function Kr(t, n, e, r, u) { t !== n && Is(n, function (i, o) { if (cf(i)) u || (u = new Ie), zr(t, n, o, e, Kr, r, u); else { var a = r ? r(t[o], i, o + "", t, n, u) : ut; a === ut && (a = i), Ue(t, o, a) } }, Xf) } function zr(t, n, e, r, u, i, o) { var a = t[e], f = n[e], c = o.get(f); if (c) return Ue(t, e, c), ut; var l = i ? i(a, f, e + "", t, n, o) : ut, s = l === ut; if (s) { var E = IE(f), p = !E && dE(f), h = !E && !p && CE(f); l = f, E || p || h ? IE(a) ? l = a : Ja(a) ? l = Bu(a) : p ? (s = !1, l = Cu(f, !0)) : h ? (s = !1, l = bu(f, !0)) : l = [] : vf(f) || AE(f) ? (l = a, AE(a) ? l = Df(a) : (!cf(a) || r && of(a)) && (l = mi(f))) : s = !1 } s && (o.set(f, l), u(l, f, r, i, o), o.delete(f)), Ue(t, e, l) } function Qr(t, n) { var e = t.length; if (e) return n += 0 > n ? e : 0, xi(n, e) ? t[n] : ut } function $r(t, n, e) { var r = -1; n = R(n.length ? n : [bc], b(gi())); var u = Yr(t, function (t, e, u) { var i = R(n, function (n) { return n(t) }); return { criteria: i, index: ++r, value: t } }); return U(u, function (t, n) { return xu(t, n, e) }) } function Jr(t, n) { return Zr(t, n, function (n, e) { return Yf(t, e) }) } function Zr(t, n, e) { for (var r = -1, u = n.length, i = {}; ++r < u;) { var o = n[r], a = fr(t, o); e(a, o) && fu(i, Lu(o, t), a) } return i } function qr(t) { return function (n) { return fr(n, t) } } function tu(t, n, e, r) { var u = r ? S : g, i = -1, o = n.length, a = t; for (t === n && (n = Bu(n)), e && (a = R(t, b(e))); ++i < o;)for (var f = 0, c = n[i], l = e ? e(c) : c; (f = u(a, l, f, r)) > -1;)a !== t && bl.call(a, f, 1), bl.call(t, f, 1); return t } function nu(t, n) { for (var e = t ? n.length : 0, r = e - 1; e--;) { var u = n[e]; if (e == r || u !== i) { var i = u; xi(u) ? bl.call(t, u, 1) : vu(t, u) } } return t } function eu(t, n) { return t + Yl(ts() * (n - t + 1)) } function ru(t, n, e, r) { for (var u = -1, i = $l(Vl((n - t) / (e || 1)), 0), o = al(i); i--;)o[r ? i : ++u] = t, t += e; return o } function uu(t, n) { var e = ""; if (!t || 1 > n || n > bt) return e; do n % 2 && (e += t), n = Yl(n / 2), n && (t += t); while (n); return e } function iu(t, n) { return bs($i(t, n, bc), t + "") } function ou(t) { return Ce(uc(t)) } function au(t, n) { var e = uc(t); return no(e, Fe(n, 0, e.length)) } function fu(t, n, e, r) { + if (!cf(t)) return t; n = Lu(n, t); for (var u = -1, i = n.length, o = i - 1, a = t; null != a && ++u < i;) { + var f = eo(n[u]), c = e; if (u != o) { var l = a[f]; c = r ? r(l, f, a) : ut, c === ut && (c = cf(l) ? l : xi(n[u + 1]) ? [] : {}) } Me(a, f, c), a = a[f] + } return t + } function cu(t) { return no(uc(t)) } function lu(t, n, e) { var r = -1, u = t.length; 0 > n && (n = -n > u ? 0 : u + n), e = e > u ? u : e, 0 > e && (e += u), u = n > e ? 0 : e - n >>> 0, n >>>= 0; for (var i = al(u); ++r < u;)i[r] = t[r + n]; return i } function su(t, n) { var e; return Ns(t, function (t, r, u) { return e = n(t, r, u), !e }), !!e } function Eu(t, n, e) { var r = 0, u = null == t ? r : t.length; if ("number" == typeof n && n === n && Bt >= u) { for (; u > r;) { var i = r + u >>> 1, o = t[i]; null === o || If(o) || (e ? o > n : o >= n) ? u = i : r = i + 1 } return u } return pu(t, n, bc, e) } function pu(t, n, e, r) { n = e(n); for (var u = 0, i = null == t ? 0 : t.length, o = n !== n, a = null === n, f = If(n), c = n === ut; i > u;) { var l = Yl((u + i) / 2), s = e(t[l]), E = s !== ut, p = null === s, h = s === s, R = If(s); if (o) var _ = r || h; else _ = c ? h && (r || E) : a ? h && E && (r || !p) : f ? h && E && !p && (r || !R) : !p && !R && (r ? n >= s : n > s); _ ? u = l + 1 : i = l } return Jl(i, Ft) } function hu(t, n) { for (var e = -1, r = t.length, u = 0, i = []; ++e < r;) { var o = t[e], a = n ? n(o) : o; if (!e || !Qa(a, f)) { var f = a; i[u++] = 0 === o ? 0 : o } } return i } function Ru(t) { return "number" == typeof t ? t : If(t) ? xt : +t } function _u(t) { if ("string" == typeof t) return t; if (IE(t)) return R(t, _u) + ""; if (If(t)) return Ts ? Ts.call(t) : ""; var n = t + ""; return "0" == n && 1 / t == -wt ? "-0" : n } function Tu(t, n, e) { var r = -1, u = p, i = t.length, o = !0, a = [], f = a; if (e) o = !1, u = h; else if (i < ot) f = n ? [] : a; else { var c = n ? null : ys(t); if (c) return Q(c); o = !1, u = x, f = new ve } t: for (; ++r < i;) { var l = t[r], s = n ? n(l) : l; if (l = e || 0 !== l ? l : 0, o && s === s) { for (var E = f.length; E--;)if (f[E] === s) continue t; n && f.push(s), a.push(l) } else u(f, s, e) || (f !== a && f.push(s), a.push(l)) } return a } function vu(t, n) { return n = Lu(n, t), t = Ji(t, n), null == t || delete t[eo(So(n))] } function Nu(t, n, e, r) { return fu(t, n, e(fr(t, n)), r) } function Au(t, n, e, r) { for (var u = t.length, i = r ? u : -1; (r ? i-- : ++i < u) && n(t[i], i, t);); return e ? lu(t, r ? 0 : i, r ? i + 1 : u) : lu(t, r ? i + 1 : 0, r ? u : i) } function Iu(t, n) { var e = t; return e instanceof A && (e = e.value()), T(n, function (t, n) { return n.func.apply(n.thisArg, _([t], n.args)) }, e) } function Ou(t, n, e) { var r = t.length; if (2 > r) return r ? Tu(t[0]) : []; for (var u = -1, i = al(r); ++u < r;)for (var o = t[u], a = -1; ++a < r;)a != u && (i[u] = Xe(i[u] || o, t[a], n, e)); return Tu(nr(i, 1), n, e) } function du(t, n, e) { for (var r = -1, u = t.length, i = n.length, o = {}; ++r < u;) { var a = i > r ? n[r] : ut; e(o, t[r], a) } return o } function gu(t) { return Ja(t) ? t : [] } function Su(t) { return "function" == typeof t ? t : bc } function Lu(t, n) { return IE(t) ? t : Fi(t, n) ? [t] : Gs(Mf(t)) } function yu(t, n, e) { var r = t.length; return e = e === ut ? r : e, n || r > e ? lu(t, n, e) : t } function Cu(t, n) { if (n) return t.slice(); var e = t.length, r = Ul ? Ul(e) : new t.constructor(e); return t.copy(r), r } function Pu(t) { var n = new t.constructor(t.byteLength); return new Dl(n).set(new Dl(t)), n } function Du(t, n) { var e = n ? Pu(t.buffer) : t.buffer; return new t.constructor(e, t.byteOffset, t.byteLength) } function Uu(t, n, e) { var r = n ? e(X(t), Et) : X(t); return T(r, i, new t.constructor) } function Mu(t) { var n = new t.constructor(t.source, Kn.exec(t)); return n.lastIndex = t.lastIndex, n } function mu(t, n, e) { var r = n ? e(Q(t), Et) : Q(t); return T(r, o, new t.constructor) } function wu(t) { return _s ? El(_s.call(t)) : {} } function bu(t, n) { var e = n ? Pu(t.buffer) : t.buffer; return new t.constructor(e, t.byteOffset, t.length) } function Gu(t, n) { if (t !== n) { var e = t !== ut, r = null === t, u = t === t, i = If(t), o = n !== ut, a = null === n, f = n === n, c = If(n); if (!a && !c && !i && t > n || i && o && f && !a && !c || r && o && f || !e && f || !u) return 1; if (!r && !i && !c && n > t || c && e && u && !r && !i || a && e && u || !o && u || !f) return -1 } return 0 } function xu(t, n, e) { for (var r = -1, u = t.criteria, i = n.criteria, o = u.length, a = e.length; ++r < o;) { var f = Gu(u[r], i[r]); if (f) { if (r >= a) return f; var c = e[r]; return f * ("desc" == c ? -1 : 1) } } return t.index - n.index } function Wu(t, n, e, r) { for (var u = -1, i = t.length, o = e.length, a = -1, f = n.length, c = $l(i - o, 0), l = al(f + c), s = !r; ++a < f;)l[a] = n[a]; for (; ++u < o;)(s || i > u) && (l[e[u]] = t[u]); for (; c--;)l[a++] = t[u++]; return l } function Fu(t, n, e, r) { for (var u = -1, i = t.length, o = -1, a = e.length, f = -1, c = n.length, l = $l(i - a, 0), s = al(l + c), E = !r; ++u < l;)s[u] = t[u]; for (var p = u; ++f < c;)s[p + f] = n[f]; for (; ++o < a;)(E || i > u) && (s[p + e[o]] = t[u++]); return s } function Bu(t, n) { var e = -1, r = t.length; for (n || (n = al(r)); ++e < r;)n[e] = t[e]; return n } function ku(t, n, e, r) { var u = !e; e || (e = {}); for (var i = -1, o = n.length; ++i < o;) { var a = n[i], f = r ? r(e[a], t[a], a, e, t) : ut; f === ut && (f = t[a]), u ? xe(e, a, f) : Me(e, a, f) } return e } function Hu(t, n) { return ku(t, Ps(t), n) } function Vu(t, n) { return ku(t, Ds(t), n) } function Yu(t, n) { return function (e, r) { var u = IE(e) ? f : we, i = n ? n() : {}; return u(e, t, gi(r, 2), i) } } function ju(t) { return iu(function (n, e) { var r = -1, u = e.length, i = u > 1 ? e[u - 1] : ut, o = u > 2 ? e[2] : ut; for (i = t.length > 3 && "function" == typeof i ? (u--, i) : ut, o && Wi(e[0], e[1], o) && (i = 3 > u ? ut : i, u = 1), n = El(n); ++r < u;) { var a = e[r]; a && t(n, a, r, i) } return n }) } function Xu(t, n) { return function (e, r) { if (null == e) return e; if (!$a(e)) return t(e, r); for (var u = e.length, i = n ? u : -1, o = El(e); (n ? i-- : ++i < u) && r(o[i], i, o) !== !1;); return e } } function Ku(t) { return function (n, e, r) { for (var u = -1, i = El(n), o = r(n), a = o.length; a--;) { var f = o[t ? a : ++u]; if (e(i[f], f, i) === !1) break } return n } } function zu(t, n, e) { function r() { var n = this && this !== ar && this instanceof r ? i : t; return n.apply(u ? e : this, arguments) } var u = n & Tt, i = Ju(t); return r } function Qu(t) { return function (n) { n = Mf(n); var e = V(n) ? tt(n) : ut, r = e ? e[0] : n.charAt(0), u = e ? yu(e, 1).join("") : n.slice(1); return r[t]() + u } } function $u(t) { return function (n) { return T(Dc(lc(n).replace(Ve, "")), t, "") } } function Ju(t) { return function () { var n = arguments; switch (n.length) { case 0: return new t; case 1: return new t(n[0]); case 2: return new t(n[0], n[1]); case 3: return new t(n[0], n[1], n[2]); case 4: return new t(n[0], n[1], n[2], n[3]); case 5: return new t(n[0], n[1], n[2], n[3], n[4]); case 6: return new t(n[0], n[1], n[2], n[3], n[4], n[5]); case 7: return new t(n[0], n[1], n[2], n[3], n[4], n[5], n[6]) }var e = vs(t.prototype), r = t.apply(e, n); return cf(r) ? r : e } } function Zu(t, n, e) { function r() { for (var i = arguments.length, o = al(i), f = i, c = di(r); f--;)o[f] = arguments[f]; var l = 3 > i && o[0] !== c && o[i - 1] !== c ? [] : z(o, c); if (i -= l.length, e > i) return ci(t, n, ni, r.placeholder, ut, o, l, ut, ut, e - i); var s = this && this !== ar && this instanceof r ? u : t; return a(s, this, o) } var u = Ju(t); return r } function qu(t) { return function (n, e, r) { var u = El(n); if (!$a(n)) { var i = gi(e, 3); n = jf(n), e = function (t) { return i(u[t], t, u) } } var o = t(n, e, r); return o > -1 ? u[i ? n[o] : o] : ut } } function ti(t) { return Ni(function (n) { var e = n.length, r = e, i = u.prototype.thru; for (t && n.reverse(); r--;) { var o = n[r]; if ("function" != typeof o) throw new Rl(ft); if (i && !a && "wrapper" == Oi(o)) var a = new u([], !0) } for (r = a ? r : e; ++r < e;) { o = n[r]; var f = Oi(o), c = "wrapper" == f ? Cs(o) : ut; a = c && ki(c[0]) && c[1] == (gt | At | Ot | St) && !c[4].length && 1 == c[9] ? a[Oi(c[0])].apply(a, c[3]) : 1 == o.length && ki(o) ? a[f]() : a.thru(o) } return function () { var t = arguments, r = t[0]; if (a && 1 == t.length && IE(r)) return a.plant(r).value(); for (var u = 0, i = e ? n[u].apply(this, t) : r; ++u < e;)i = n[u].call(this, i); return i } }) } function ni(t, n, e, r, u, i, o, a, f, c) { function l() { for (var T = arguments.length, v = al(T), N = T; N--;)v[N] = arguments[N]; if (h) var A = di(l), I = B(v, A); if (r && (v = Wu(v, r, u, h)), i && (v = Fu(v, i, o, h)), T -= I, h && c > T) { var O = z(v, A); return ci(t, n, ni, l.placeholder, e, v, O, a, f, c - T) } var d = E ? e : this, g = p ? d[t] : t; return T = v.length, a ? v = Zi(v, a) : R && T > 1 && v.reverse(), s && T > f && (v.length = f), this && this !== ar && this instanceof l && (g = _ || Ju(g)), g.apply(d, v) } var s = n & gt, E = n & Tt, p = n & vt, h = n & (At | It), R = n & Lt, _ = p ? ut : Ju(t); return l } function ei(t, n) { return function (e, r) { return yr(e, t, n(r), {}) } } function ri(t, n) { return function (e, r) { var u; if (e === ut && r === ut) return n; if (e !== ut && (u = e), r !== ut) { if (u === ut) return r; "string" == typeof e || "string" == typeof r ? (e = _u(e), r = _u(r)) : (e = Ru(e), r = Ru(r)), u = t(e, r) } return u } } function ui(t) { return Ni(function (n) { return n = R(n, b(gi())), iu(function (e) { var r = this; return t(n, function (t) { return a(t, r, e) }) }) }) } function ii(t, n) { n = n === ut ? " " : _u(n); var e = n.length; if (2 > e) return e ? uu(n, t) : n; var r = uu(n, Vl(t / q(n))); return V(n) ? yu(tt(r), 0, t).join("") : r.slice(0, t) } function oi(t, n, e, r) { function u() { for (var n = -1, f = arguments.length, c = -1, l = r.length, s = al(l + f), E = this && this !== ar && this instanceof u ? o : t; ++c < l;)s[c] = r[c]; for (; f--;)s[c++] = arguments[++n]; return a(E, i ? e : this, s) } var i = n & Tt, o = Ju(t); return u } function ai(t) { return function (n, e, r) { return r && "number" != typeof r && Wi(n, e, r) && (e = r = ut), n = Lf(n), e === ut ? (e = n, n = 0) : e = Lf(e), r = r === ut ? e > n ? 1 : -1 : Lf(r), ru(n, e, r, t) } } function fi(t) { return function (n, e) { return "string" == typeof n && "string" == typeof e || (n = Pf(n), e = Pf(e)), t(n, e) } } function ci(t, n, e, r, u, i, o, a, f, c) { var l = n & At, s = l ? o : ut, E = l ? ut : o, p = l ? i : ut, h = l ? ut : i; n |= l ? Ot : dt, n &= ~(l ? dt : Ot), n & Nt || (n &= ~(Tt | vt)); var R = [t, n, u, p, s, h, E, a, f, c], _ = e.apply(ut, R); return ki(t) && ms(_, R), _.placeholder = r, qi(_, t, n) } function li(t) { var n = sl[t]; return function (t, e) { if (t = Pf(t), e = null == e ? 0 : Jl(yf(e), 292)) { var r = (Mf(t) + "e").split("e"), u = n(r[0] + "e" + (+r[1] + e)); return r = (Mf(u) + "e").split("e"), +(r[0] + "e" + (+r[1] - e)) } return n(t) } } function si(t) { return function (n) { var e = Us(n); return e == Jt ? X(n) : e == un ? $(n) : w(n, t(n)) } } function Ei(t, n, e, r, u, i, o, a) { var f = n & vt; if (!f && "function" != typeof t) throw new Rl(ft); var c = r ? r.length : 0; if (c || (n &= ~(Ot | dt), r = u = ut), o = o === ut ? o : $l(yf(o), 0), a = a === ut ? a : yf(a), c -= u ? u.length : 0, n & dt) { var l = r, s = u; r = u = ut } var E = f ? ut : Cs(t), p = [t, n, e, r, u, l, s, i, o, a]; if (E && Ki(p, E), t = p[0], n = p[1], e = p[2], r = p[3], u = p[4], a = p[9] = p[9] === ut ? f ? 0 : t.length : $l(p[9] - c, 0), !a && n & (At | It) && (n &= ~(At | It)), n && n != Tt) h = n == At || n == It ? Zu(t, n, a) : n != Ot && n != (Tt | Ot) || u.length ? ni.apply(ut, p) : oi(t, n, e, r); else var h = zu(t, n, e); var R = E ? ds : ms; return qi(R(h, p), t, n) } function pi(t, n, e, r) { return t === ut || Qa(t, vl[e]) && !Il.call(r, e) ? n : t } function hi(t, n, e, r, u, i) { return cf(t) && cf(n) && (i.set(n, t), Kr(t, n, ut, hi, i), i.delete(n)), t } function Ri(t) { return vf(t) ? ut : t } function _i(t, n, e, r, u, i) { var o = e & Rt, a = t.length, f = n.length; if (!(a == f || o && f > a)) return !1; var c = i.get(t); if (c && i.get(n)) return c == n; var l = -1, s = !0, E = e & _t ? new ve : ut; for (i.set(t, n), i.set(n, t); ++l < a;) { var p = t[l], h = n[l]; if (r) var R = o ? r(h, p, l, n, t, i) : r(p, h, l, t, n, i); if (R !== ut) { if (R) continue; s = !1; break } if (E) { if (!N(n, function (t, n) { if (!x(E, n) && (p === t || u(p, t, e, r, i))) return E.push(n) })) { s = !1; break } } else if (p !== h && !u(p, h, e, r, i)) { s = !1; break } } return i.delete(t), i.delete(n), s } function Ti(t, n, e, r, u, i, o) { switch (e) { case En: if (t.byteLength != n.byteLength || t.byteOffset != n.byteOffset) return !1; t = t.buffer, n = n.buffer; case sn: return !(t.byteLength != n.byteLength || !i(new Dl(t), new Dl(n))); case jt: case Xt: case Zt: return Qa(+t, +n); case zt: return t.name == n.name && t.message == n.message; case rn: case on: return t == n + ""; case Jt: var a = X; case un: var f = r & Rt; if (a || (a = Q), t.size != n.size && !f) return !1; var c = o.get(t); if (c) return c == n; r |= _t, o.set(t, n); var l = _i(a(t), a(n), r, u, i, o); return o.delete(t), l; case an: if (_s) return _s.call(t) == _s.call(n) }return !1 } function vi(t, n, e, r, u, i) { var o = e & Rt, a = Ai(t), f = a.length, c = Ai(n), l = c.length; if (f != l && !o) return !1; for (var s = f; s--;) { var E = a[s]; if (!(o ? E in n : Il.call(n, E))) return !1 } var p = i.get(t); if (p && i.get(n)) return p == n; var h = !0; i.set(t, n), i.set(n, t); for (var R = o; ++s < f;) { E = a[s]; var _ = t[E], T = n[E]; if (r) var v = o ? r(T, _, E, n, t, i) : r(_, T, E, t, n, i); if (!(v === ut ? _ === T || u(_, T, e, r, i) : v)) { h = !1; break } R || (R = "constructor" == E) } if (h && !R) { var N = t.constructor, A = n.constructor; N != A && "constructor" in t && "constructor" in n && !("function" == typeof N && N instanceof N && "function" == typeof A && A instanceof A) && (h = !1) } return i.delete(t), i.delete(n), h } function Ni(t) { return bs($i(t, ut, _o), t + "") } function Ai(t) { return cr(t, jf, Ps) } function Ii(t) { return cr(t, Xf, Ds) } function Oi(t) { for (var n = t.name + "", e = cs[n], r = Il.call(cs, n) ? e.length : 0; r--;) { var u = e[r], i = u.func; if (null == i || i == t) return u.name } return n } function di(t) { var n = Il.call(e, "placeholder") ? e : t; return n.placeholder } function gi() { var t = e.iteratee || Gc; return t = t === Gc ? Br : t, arguments.length ? t(arguments[0], arguments[1]) : t } function Si(t, n) { var e = t.__data__; return Bi(n) ? e["string" == typeof n ? "string" : "hash"] : e.map } function Li(t) { for (var n = jf(t), e = n.length; e--;) { var r = n[e], u = t[r]; n[e] = [r, u, Yi(u)] } return n } function yi(t, n) { var e = H(t, n); return Gr(e) ? e : ut } function Ci(t) { var n = Il.call(t, Wl), e = t[Wl]; try { t[Wl] = ut; var r = !0 } catch (t) { } var u = gl.call(t); return r && (n ? t[Wl] = e : delete t[Wl]), u } function Pi(t, n, e) { for (var r = -1, u = e.length; ++r < u;) { var i = e[r], o = i.size; switch (i.type) { case "drop": t += o; break; case "dropRight": n -= o; break; case "take": n = Jl(n, t + o); break; case "takeRight": t = $l(t, n - o) } } return { start: t, end: n } } function Di(t) { var n = t.match(Hn); return n ? n[1].split(Vn) : [] } function Ui(t, n, e) { n = Lu(n, t); for (var r = -1, u = n.length, i = !1; ++r < u;) { var o = eo(n[r]); if (!(i = null != t && e(t, o))) break; t = t[o] } return i || ++r != u ? i : (u = null == t ? 0 : t.length, !!u && ff(u) && xi(o, u) && (IE(t) || AE(t))) } function Mi(t) { var n = t.length, e = t.constructor(n); return n && "string" == typeof t[0] && Il.call(t, "index") && (e.index = t.index, e.input = t.input), e } function mi(t) { return "function" != typeof t.constructor || Vi(t) ? {} : vs(Ml(t)) } function wi(t, n, e, r) { var u = t.constructor; switch (n) { case sn: return Pu(t); case jt: case Xt: return new u(+t); case En: return Du(t, r); case pn: case hn: case Rn: case _n: case Tn: case vn: case Nn: case An: case In: return bu(t, r); case Jt: return Uu(t, r, e); case Zt: case on: return new u(t); case rn: return Mu(t); case un: return mu(t, r, e); case an: return wu(t) } } function bi(t, n) { var e = n.length; if (!e) return t; var r = e - 1; return n[r] = (e > 1 ? "& " : "") + n[r], n = n.join(e > 2 ? ", " : " "), t.replace(kn, "{\n/* [wrapped with " + n + "] */\n") } function Gi(t) { return IE(t) || AE(t) || !!(Gl && t && t[Gl]) } function xi(t, n) { return n = null == n ? bt : n, !!n && ("number" == typeof t || Zn.test(t)) && t > -1 && t % 1 == 0 && n > t } function Wi(t, n, e) { if (!cf(e)) return !1; var r = typeof n; return !!("number" == r ? $a(e) && xi(n, e.length) : "string" == r && n in e) && Qa(e[n], t) } function Fi(t, n) { if (IE(t)) return !1; var e = typeof t; return !("number" != e && "symbol" != e && "boolean" != e && null != t && !If(t)) || (mn.test(t) || !Mn.test(t) || null != n && t in El(n)) } function Bi(t) { var n = typeof t; return "string" == n || "number" == n || "symbol" == n || "boolean" == n ? "__proto__" !== t : null === t } function ki(t) { var n = Oi(t), r = e[n]; if ("function" != typeof r || !(n in A.prototype)) return !1; if (t === r) return !0; var u = Cs(r); return !!u && t === u[0] } function Hi(t) { return !!dl && dl in t } function Vi(t) { var n = t && t.constructor, e = "function" == typeof n && n.prototype || vl; return t === e } function Yi(t) { return t === t && !cf(t) } function ji(t, n) { return function (e) { return null != e && (e[t] === n && (n !== ut || t in El(e))) } } function Xi(t) { var n = ba(t, function (t) { return e.size === lt && e.clear(), t }), e = n.cache; return n } function Ki(t, n) { var e = t[1], r = n[1], u = e | r, i = (Tt | vt | gt) > u, o = r == gt && e == At || r == gt && e == St && n[8] >= t[7].length || r == (gt | St) && n[8] >= n[7].length && e == At; if (!i && !o) return t; r & Tt && (t[2] = n[2], u |= e & Tt ? 0 : Nt); var a = n[3]; if (a) { var f = t[3]; t[3] = f ? Wu(f, a, n[4]) : a, t[4] = f ? z(t[3], st) : n[4] } return a = n[5], a && (f = t[5], t[5] = f ? Fu(f, a, n[6]) : a, t[6] = f ? z(t[5], st) : n[6]), a = n[7], a && (t[7] = a), r & gt && (t[8] = null == t[8] ? n[8] : Jl(t[8], n[8])), null == t[9] && (t[9] = n[9]), t[0] = n[0], t[1] = u, t } function zi(t) { var n = []; if (null != t) for (var e in El(t)) n.push(e); return n } function Qi(t) { return gl.call(t) } function $i(t, n, e) { return n = $l(n === ut ? t.length - 1 : n, 0), function () { for (var r = arguments, u = -1, i = $l(r.length - n, 0), o = al(i); ++u < i;)o[u] = r[n + u]; u = -1; for (var f = al(n + 1); ++u < n;)f[u] = r[u]; return f[n] = e(o), a(t, this, f) } } function Ji(t, n) { return 2 > n.length ? t : fr(t, lu(n, 0, -1)) } function Zi(t, n) { for (var e = t.length, r = Jl(n.length, e), u = Bu(t); r--;) { var i = n[r]; t[r] = xi(i, e) ? u[i] : ut } return t } function qi(t, n, e) { var r = n + ""; return bs(t, bi(r, uo(Di(r), e))) } function to(t) { var n = 0, e = 0; return function () { var r = Zl(), u = Dt - (r - e); if (e = r, u > 0) { if (++n >= Pt) return arguments[0] } else n = 0; return t.apply(ut, arguments) } } function no(t, n) { var e = -1, r = t.length, u = r - 1; for (n = n === ut ? r : n; ++e < n;) { var i = eu(e, u), o = t[i]; t[i] = t[e], t[e] = o } return t.length = n, t } function eo(t) { if ("string" == typeof t || If(t)) return t; var n = t + ""; return "0" == n && 1 / t == -wt ? "-0" : n } function ro(t) { if (null != t) { try { return Al.call(t) } catch (t) { } try { return t + "" } catch (t) { } } return "" } function uo(t, n) { return c(kt, function (e) { var r = "_." + e[0]; n & e[1] && !p(t, r) && t.push(r) }), t.sort() } function io(t) { if (t instanceof A) return t.clone(); var n = new u(t.__wrapped__, t.__chain__); return n.__actions__ = Bu(t.__actions__), n.__index__ = t.__index__, n.__values__ = t.__values__, n } function oo(t, n, e) { n = (e ? Wi(t, n, e) : n === ut) ? 1 : $l(yf(n), 0); var r = null == t ? 0 : t.length; if (!r || 1 > n) return []; for (var u = 0, i = 0, o = al(Vl(r / n)); r > u;)o[i++] = lu(t, u, u += n); return o } function ao(t) { for (var n = -1, e = null == t ? 0 : t.length, r = 0, u = []; ++n < e;) { var i = t[n]; i && (u[r++] = i) } return u } function fo() { var t = arguments.length; if (!t) return []; for (var n = al(t - 1), e = arguments[0], r = t; r--;)n[r - 1] = arguments[r]; return _(IE(e) ? Bu(e) : [e], nr(n, 1)) } function co(t, n, e) { var r = null == t ? 0 : t.length; return r ? (n = e || n === ut ? 1 : yf(n), lu(t, 0 > n ? 0 : n, r)) : [] } function lo(t, n, e) { var r = null == t ? 0 : t.length; return r ? (n = e || n === ut ? 1 : yf(n), n = r - n, lu(t, 0, 0 > n ? 0 : n)) : [] } function so(t, n) { return t && t.length ? Au(t, gi(n, 3), !0, !0) : [] } function Eo(t, n) { return t && t.length ? Au(t, gi(n, 3), !0) : [] } function po(t, n, e, r) { var u = null == t ? 0 : t.length; return u ? (e && "number" != typeof e && Wi(t, n, e) && (e = 0, r = u), qe(t, n, e, r)) : [] } function ho(t, n, e) { var r = null == t ? 0 : t.length; if (!r) return -1; var u = null == e ? 0 : yf(e); return 0 > u && (u = $l(r + u, 0)), d(t, gi(n, 3), u) } function Ro(t, n, e) { var r = null == t ? 0 : t.length; if (!r) return -1; var u = r - 1; return e !== ut && (u = yf(e), u = 0 > e ? $l(r + u, 0) : Jl(u, r - 1)), d(t, gi(n, 3), u, !0) } function _o(t) { var n = null == t ? 0 : t.length; return n ? nr(t, 1) : [] } function To(t) { var n = null == t ? 0 : t.length; return n ? nr(t, wt) : [] } function vo(t, n) { var e = null == t ? 0 : t.length; return e ? (n = n === ut ? 1 : yf(n), nr(t, n)) : [] } function No(t) { for (var n = -1, e = null == t ? 0 : t.length, r = {}; ++n < e;) { var u = t[n]; r[u[0]] = u[1] } return r } function Ao(t) { return t && t.length ? t[0] : ut } function Io(t, n, e) { var r = null == t ? 0 : t.length; if (!r) return -1; var u = null == e ? 0 : yf(e); return 0 > u && (u = $l(r + u, 0)), g(t, n, u) } function Oo(t) { var n = null == t ? 0 : t.length; return n ? lu(t, 0, -1) : [] } function go(t, n) { return null == t ? "" : zl.call(t, n) } function So(t) { var n = null == t ? 0 : t.length; return n ? t[n - 1] : ut } function Lo(t, n, e) { var r = null == t ? 0 : t.length; if (!r) return -1; var u = r; return e !== ut && (u = yf(e), u = 0 > u ? $l(r + u, 0) : Jl(u, r - 1)), n === n ? Z(t, n, u) : d(t, L, u, !0) } function yo(t, n) { return t && t.length ? Qr(t, yf(n)) : ut } function Co(t, n) { return t && t.length && n && n.length ? tu(t, n) : t } function Po(t, n, e) { return t && t.length && n && n.length ? tu(t, n, gi(e, 2)) : t } function Do(t, n, e) { return t && t.length && n && n.length ? tu(t, n, ut, e) : t } function Uo(t, n) { var e = []; if (!t || !t.length) return e; var r = -1, u = [], i = t.length; for (n = gi(n, 3); ++r < i;) { var o = t[r]; n(o, r, t) && (e.push(o), u.push(r)) } return nu(t, u), e } function Mo(t) { return null == t ? t : ns.call(t) } function mo(t, n, e) { var r = null == t ? 0 : t.length; return r ? (e && "number" != typeof e && Wi(t, n, e) ? (n = 0, e = r) : (n = null == n ? 0 : yf(n), e = e === ut ? r : yf(e)), lu(t, n, e)) : [] } function wo(t, n) { return Eu(t, n) } function bo(t, n, e) { return pu(t, n, gi(e, 2)) } function Go(t, n) { var e = null == t ? 0 : t.length; if (e) { var r = Eu(t, n); if (e > r && Qa(t[r], n)) return r } return -1 } function xo(t, n) { return Eu(t, n, !0) } function Wo(t, n, e) { return pu(t, n, gi(e, 2), !0) } function Fo(t, n) { var e = null == t ? 0 : t.length; if (e) { var r = Eu(t, n, !0) - 1; if (Qa(t[r], n)) return r } return -1 } function Bo(t) { return t && t.length ? hu(t) : [] } function ko(t, n) { return t && t.length ? hu(t, gi(n, 2)) : [] } function Ho(t) { var n = null == t ? 0 : t.length; return n ? lu(t, 1, n) : [] } function Vo(t, n, e) { return t && t.length ? (n = e || n === ut ? 1 : yf(n), lu(t, 0, 0 > n ? 0 : n)) : [] } function Yo(t, n, e) { var r = null == t ? 0 : t.length; return r ? (n = e || n === ut ? 1 : yf(n), n = r - n, lu(t, 0 > n ? 0 : n, r)) : [] } function jo(t, n) { return t && t.length ? Au(t, gi(n, 3), !1, !0) : [] } function Xo(t, n) { return t && t.length ? Au(t, gi(n, 3)) : [] } function Ko(t) { return t && t.length ? Tu(t) : [] } function zo(t, n) { return t && t.length ? Tu(t, gi(n, 2)) : [] } function Qo(t, n) { return n = "function" == typeof n ? n : ut, t && t.length ? Tu(t, ut, n) : [] } function $o(t) { if (!t || !t.length) return []; var n = 0; return t = E(t, function (t) { if (Ja(t)) return n = $l(t.length, n), !0 }), m(n, function (n) { return R(t, C(n)) }) } function Jo(t, n) { if (!t || !t.length) return []; var e = $o(t); return null == n ? e : R(e, function (t) { return a(n, ut, t) }) } function Zo(t, n) { return du(t || [], n || [], Me) } function qo(t, n) { return du(t || [], n || [], fu) } function ta(t) { var n = e(t); return n.__chain__ = !0, n } function na(t, n) { return n(t), t } function ea(t, n) { return n(t) } function ra() { return ta(this) } function ua() { return new u(this.value(), this.__chain__) } function ia() { this.__values__ === ut && (this.__values__ = Sf(this.value())); var t = this.__index__ >= this.__values__.length, n = t ? ut : this.__values__[this.__index__++]; return { done: t, value: n } } function oa() { return this } function aa(t) { for (var n, e = this; e instanceof r;) { var u = io(e); u.__index__ = 0, u.__values__ = ut, n ? i.__wrapped__ = u : n = u; var i = u; e = e.__wrapped__ } return i.__wrapped__ = t, n } function fa() { var t = this.__wrapped__; if (t instanceof A) { var n = t; return this.__actions__.length && (n = new A(this)), n = n.reverse(), n.__actions__.push({ func: ea, args: [Mo], thisArg: ut }), new u(n, this.__chain__) } return this.thru(Mo) } function ca() { return Iu(this.__wrapped__, this.__actions__) } function la(t, n, e) { var r = IE(t) ? s : Ke; return e && Wi(t, n, e) && (n = ut), r(t, gi(n, 3)) } function sa(t, n) { var e = IE(t) ? E : tr; return e(t, gi(n, 3)) } function Ea(t, n) { return nr(va(t, n), 1) } function pa(t, n) { return nr(va(t, n), wt) } function ha(t, n, e) { return e = e === ut ? 1 : yf(e), nr(va(t, n), e) } function Ra(t, n) { var e = IE(t) ? c : Ns; return e(t, gi(n, 3)) } function _a(t, n) { var e = IE(t) ? l : As; return e(t, gi(n, 3)) } function Ta(t, n, e, r) { t = $a(t) ? t : uc(t), e = e && !r ? yf(e) : 0; var u = t.length; return 0 > e && (e = $l(u + e, 0)), Af(t) ? u >= e && t.indexOf(n, e) > -1 : !!u && g(t, n, e) > -1 } function va(t, n) { var e = IE(t) ? R : Yr; return e(t, gi(n, 3)) } function Na(t, n, e, r) { return null == t ? [] : (IE(n) || (n = null == n ? [] : [n]), e = r ? ut : e, IE(e) || (e = null == e ? [] : [e]), $r(t, n, e)) } function Aa(t, n, e) { var r = IE(t) ? T : D, u = 3 > arguments.length; return r(t, gi(n, 4), e, u, Ns) } function Ia(t, n, e) { var r = IE(t) ? v : D, u = 3 > arguments.length; return r(t, gi(n, 4), e, u, As) } function Oa(t, n) { var e = IE(t) ? E : tr; return e(t, Ga(gi(n, 3))) } function da(t) { var n = IE(t) ? Ce : ou; return n(t) } function ga(t, n, e) { n = (e ? Wi(t, n, e) : n === ut) ? 1 : yf(n); var r = IE(t) ? Pe : au; return r(t, n) } function Sa(t) { var n = IE(t) ? De : cu; return n(t) } function La(t) { if (null == t) return 0; if ($a(t)) return Af(t) ? q(t) : t.length; var n = Us(t); return n == Jt || n == un ? t.size : kr(t).length } function ya(t, n, e) { var r = IE(t) ? N : su; return e && Wi(t, n, e) && (n = ut), r(t, gi(n, 3)) } function Ca(t, n) { if ("function" != typeof n) throw new Rl(ft); return t = yf(t), function () { if (--t < 1) return n.apply(this, arguments) } } function Pa(t, n, e) { return n = e ? ut : n, n = t && null == n ? t.length : n, Ei(t, gt, ut, ut, ut, ut, n) } function Da(t, n) { var e; if ("function" != typeof n) throw new Rl(ft); return t = yf(t), function () { return --t > 0 && (e = n.apply(this, arguments)), t > 1 || (n = ut), e } } function Ua(t, n, e) { n = e ? ut : n; var r = Ei(t, At, ut, ut, ut, ut, ut, n); return r.placeholder = Ua.placeholder, r } function Ma(t, n, e) { n = e ? ut : n; var r = Ei(t, It, ut, ut, ut, ut, ut, n); return r.placeholder = Ma.placeholder, r } function ma(t, n, e) { function r(n) { var e = E, r = p; return E = p = ut, v = n, R = t.apply(r, e) } function u(t) { return v = t, _ = ws(a, n), N ? r(t) : R } function i(t) { var e = t - T, r = t - v, u = n - e; return A ? Jl(u, h - r) : u } function o(t) { var e = t - T, r = t - v; return T === ut || e >= n || 0 > e || A && r >= h } function a() { var t = cE(); return o(t) ? f(t) : (_ = ws(a, i(t)), ut) } function f(t) { return _ = ut, I && E ? r(t) : (E = p = ut, R) } function c() { _ !== ut && Ls(_), v = 0, E = T = p = _ = ut } function l() { return _ === ut ? R : f(cE()) } function s() { var t = cE(), e = o(t); if (E = arguments, p = this, T = t, e) { if (_ === ut) return u(T); if (A) return _ = ws(a, n), r(T) } return _ === ut && (_ = ws(a, n)), R } var E, p, h, R, _, T, v = 0, N = !1, A = !1, I = !0; if ("function" != typeof t) throw new Rl(ft); return n = Pf(n) || 0, cf(e) && (N = !!e.leading, A = "maxWait" in e, h = A ? $l(Pf(e.maxWait) || 0, n) : h, I = "trailing" in e ? !!e.trailing : I), s.cancel = c, s.flush = l, s } function wa(t) { return Ei(t, Lt) } function ba(t, n) { if ("function" != typeof t || null != n && "function" != typeof n) throw new Rl(ft); var e = function () { var r = arguments, u = n ? n.apply(this, r) : r[0], i = e.cache; if (i.has(u)) return i.get(u); var o = t.apply(this, r); return e.cache = i.set(u, o) || i, o }; return e.cache = new (ba.Cache || Ee), e } function Ga(t) { if ("function" != typeof t) throw new Rl(ft); return function () { var n = arguments; switch (n.length) { case 0: return !t.call(this); case 1: return !t.call(this, n[0]); case 2: return !t.call(this, n[0], n[1]); case 3: return !t.call(this, n[0], n[1], n[2]) }return !t.apply(this, n) } } function xa(t) { return Da(2, t) } function Wa(t, n) { if ("function" != typeof t) throw new Rl(ft); return n = n === ut ? n : yf(n), iu(t, n) } function Fa(t, n) { if ("function" != typeof t) throw new Rl(ft); return n = null == n ? 0 : $l(yf(n), 0), iu(function (e) { var r = e[n], u = yu(e, 0, n); return r && _(u, r), a(t, this, u) }) } function Ba(t, n, e) { var r = !0, u = !0; if ("function" != typeof t) throw new Rl(ft); return cf(e) && (r = "leading" in e ? !!e.leading : r, u = "trailing" in e ? !!e.trailing : u), ma(t, n, { leading: r, maxWait: n, trailing: u }) } function ka(t) { return Pa(t, 1) } function Ha(t, n) { return RE(Su(n), t) } function Va() { if (!arguments.length) return []; var t = arguments[0]; return IE(t) ? t : [t] } function Ya(t) { return Be(t, ht) } function ja(t, n) { return n = "function" == typeof n ? n : ut, Be(t, ht, n) } function Xa(t) { return Be(t, Et | ht) } function Ka(t, n) { return n = "function" == typeof n ? n : ut, Be(t, Et | ht, n) } function za(t, n) { return null == n || He(t, n, jf(n)) } function Qa(t, n) { return t === n || t !== t && n !== n } function $a(t) { return null != t && ff(t.length) && !of(t) } function Ja(t) { return lf(t) && $a(t) } function Za(t) { return t === !0 || t === !1 || lf(t) && sr(t) == jt } function qa(t) { return lf(t) && 1 === t.nodeType && !vf(t) } function tf(t) { if (null == t) return !0; if ($a(t) && (IE(t) || "string" == typeof t || "function" == typeof t.splice || dE(t) || CE(t) || AE(t))) return !t.length; var n = Us(t); if (n == Jt || n == un) return !t.size; if (Vi(t)) return !kr(t).length; for (var e in t) if (Il.call(t, e)) return !1; return !0 } function nf(t, n) { return Mr(t, n) } function ef(t, n, e) { e = "function" == typeof e ? e : ut; var r = e ? e(t, n) : ut; return r === ut ? Mr(t, n, ut, e) : !!r } function rf(t) { if (!lf(t)) return !1; var n = sr(t); return n == zt || n == Kt || "string" == typeof t.message && "string" == typeof t.name && !vf(t) } function uf(t) { return "number" == typeof t && Kl(t) } function of(t) { if (!cf(t)) return !1; var n = sr(t); return n == Qt || n == $t || n == Yt || n == en } function af(t) { return "number" == typeof t && t == yf(t) } function ff(t) { return "number" == typeof t && t > -1 && t % 1 == 0 && bt >= t } function cf(t) { var n = typeof t; return null != t && ("object" == n || "function" == n) } function lf(t) { return null != t && "object" == typeof t } function sf(t, n) { return t === n || br(t, n, Li(n)) } function Ef(t, n, e) { return e = "function" == typeof e ? e : ut, br(t, n, Li(n), e) } function pf(t) { return Tf(t) && t != +t } function hf(t) { if (Ms(t)) throw new cl(at); return Gr(t) } function Rf(t) { return null === t } function _f(t) { return null == t } function Tf(t) { return "number" == typeof t || lf(t) && sr(t) == Zt } function vf(t) { if (!lf(t) || sr(t) != tn) return !1; var n = Ml(t); if (null === n) return !0; var e = Il.call(n, "constructor") && n.constructor; return "function" == typeof e && e instanceof e && Al.call(e) == Sl } function Nf(t) { return af(t) && t >= -bt && bt >= t } function Af(t) { return "string" == typeof t || !IE(t) && lf(t) && sr(t) == on } function If(t) { return "symbol" == typeof t || lf(t) && sr(t) == an } function Of(t) { return t === ut } function df(t) { return lf(t) && Us(t) == cn } function gf(t) { return lf(t) && sr(t) == ln } function Sf(t) { if (!t) return []; if ($a(t)) return Af(t) ? tt(t) : Bu(t); if (xl && t[xl]) return j(t[xl]()); var n = Us(t), e = n == Jt ? X : n == un ? Q : uc; return e(t) } function Lf(t) { if (!t) return 0 === t ? t : 0; if (t = Pf(t), t === wt || t === -wt) { var n = 0 > t ? -1 : 1; return n * Gt } return t === t ? t : 0 } function yf(t) { var n = Lf(t), e = n % 1; return n === n ? e ? n - e : n : 0 } function Cf(t) { return t ? Fe(yf(t), 0, Wt) : 0 } function Pf(t) { if ("number" == typeof t) return t; if (If(t)) return xt; if (cf(t)) { var n = "function" == typeof t.valueOf ? t.valueOf() : t; t = cf(n) ? n + "" : n } if ("string" != typeof t) return 0 === t ? t : +t; t = t.replace(Wn, ""); var e = Qn.test(t); return e || Jn.test(t) ? ur(t.slice(2), e ? 2 : 8) : zn.test(t) ? xt : +t } function Df(t) { return ku(t, Xf(t)) } function Uf(t) { return t ? Fe(yf(t), -bt, bt) : 0 === t ? t : 0 } function Mf(t) { return null == t ? "" : _u(t) } function mf(t, n) { var e = vs(t); return null == n ? e : be(e, n) } function wf(t, n) { return O(t, gi(n, 3), er) } function bf(t, n) { return O(t, gi(n, 3), ir) } function Gf(t, n) { return null == t ? t : Is(t, gi(n, 3), Xf) } function xf(t, n) { return null == t ? t : Os(t, gi(n, 3), Xf) } function Wf(t, n) { return t && er(t, gi(n, 3)) } function Ff(t, n) { return t && ir(t, gi(n, 3)) } function Bf(t) { return null == t ? [] : or(t, jf(t)) } function kf(t) { return null == t ? [] : or(t, Xf(t)) } function Hf(t, n, e) { var r = null == t ? ut : fr(t, n); return r === ut ? e : r } function Vf(t, n) { return null != t && Ui(t, n, Nr) } function Yf(t, n) { return null != t && Ui(t, n, dr) } function jf(t) { return $a(t) ? ye(t) : kr(t) } function Xf(t) { return $a(t) ? ye(t, !0) : Hr(t) } function Kf(t, n) { var e = {}; return n = gi(n, 3), er(t, function (t, r, u) { xe(e, n(t, r, u), t) }), e } function zf(t, n) { var e = {}; return n = gi(n, 3), er(t, function (t, r, u) { xe(e, r, n(t, r, u)) }), e } function Qf(t, n) { return $f(t, Ga(gi(n))) } function $f(t, n) { if (null == t) return {}; var e = R(Ii(t), function (t) { return [t] }); return n = gi(n), Zr(t, e, function (t, e) { return n(t, e[0]) }) } function Jf(t, n, e) { n = Lu(n, t); var r = -1, u = n.length; for (u || (u = 1, t = ut); ++r < u;) { var i = null == t ? ut : t[eo(n[r])]; i === ut && (r = u, i = e), t = of(i) ? i.call(t) : i } return t } function Zf(t, n, e) { return null == t ? t : fu(t, n, e) } function qf(t, n, e, r) { return r = "function" == typeof r ? r : ut, null == t ? t : fu(t, n, e, r) } function tc(t, n, e) { var r = IE(t), u = r || dE(t) || CE(t); if (n = gi(n, 4), null == e) { var i = t && t.constructor; e = u ? r ? new i : [] : cf(t) && of(i) ? vs(Ml(t)) : {} } return (u ? c : er)(t, function (t, r, u) { return n(e, t, r, u) }), e } function nc(t, n) { return null == t || vu(t, n) } function ec(t, n, e) { return null == t ? t : Nu(t, n, Su(e)) } function rc(t, n, e, r) { return r = "function" == typeof r ? r : ut, null == t ? t : Nu(t, n, Su(e), r) } function uc(t) { return null == t ? [] : G(t, jf(t)) } function ic(t) { return null == t ? [] : G(t, Xf(t)) } function oc(t, n, e) { return e === ut && (e = n, n = ut), e !== ut && (e = Pf(e), e = e === e ? e : 0), n !== ut && (n = Pf(n), n = n === n ? n : 0), Fe(Pf(t), n, e) } function ac(t, n, e) { return n = Lf(n), e === ut ? (e = n, n = 0) : e = Lf(e), t = Pf(t), Sr(t, n, e) } function fc(t, n, e) { if (e && "boolean" != typeof e && Wi(t, n, e) && (n = e = ut), e === ut && ("boolean" == typeof n ? (e = n, n = ut) : "boolean" == typeof t && (e = t, t = ut)), t === ut && n === ut ? (t = 0, n = 1) : (t = Lf(t), n === ut ? (n = t, t = 0) : n = Lf(n)), t > n) { var r = t; t = n, n = r } if (e || t % 1 || n % 1) { var u = ts(); return Jl(t + u * (n - t + rr("1e-" + ((u + "").length - 1))), n) } return eu(t, n) } function cc(t) { return tp(Mf(t).toLowerCase()) } function lc(t) { return t = Mf(t), t && t.replace(qn, Ar).replace(Ye, "") } function sc(t, n, e) { t = Mf(t), n = _u(n); var r = t.length; e = e === ut ? r : Fe(yf(e), 0, r); var u = e; return e -= n.length, e >= 0 && t.slice(e, u) == n } function Ec(t) { return t = Mf(t), t && Cn.test(t) ? t.replace(Ln, Ir) : t } function pc(t) { return t = Mf(t), t && xn.test(t) ? t.replace(Gn, "\\$&") : t } function hc(t, n, e) { t = Mf(t), n = yf(n); var r = n ? q(t) : 0; if (!n || r >= n) return t; var u = (n - r) / 2; return ii(Yl(u), e) + t + ii(Vl(u), e) } function Rc(t, n, e) { t = Mf(t), n = yf(n); var r = n ? q(t) : 0; return n && n > r ? t + ii(n - r, e) : t } function _c(t, n, e) { t = Mf(t), n = yf(n); var r = n ? q(t) : 0; return n && n > r ? ii(n - r, e) + t : t } function Tc(t, n, e) { return e || null == n ? n = 0 : n && (n = +n), ql(Mf(t).replace(Fn, ""), n || 0) } function vc(t, n, e) { return n = (e ? Wi(t, n, e) : n === ut) ? 1 : yf(n), uu(Mf(t), n) } function Nc() { var t = arguments, n = Mf(t[0]); return 3 > t.length ? n : n.replace(t[1], t[2]) } function Ac(t, n, e) { return e && "number" != typeof e && Wi(t, n, e) && (n = e = ut), (e = e === ut ? Wt : e >>> 0) ? (t = Mf(t), t && ("string" == typeof n || null != n && !LE(n)) && (n = _u(n), !n && V(t)) ? yu(tt(t), 0, e) : t.split(n, e)) : [] } function Ic(t, n, e) { return t = Mf(t), e = null == e ? 0 : Fe(yf(e), 0, t.length), n = _u(n), t.slice(e, e + n.length) == n } function Oc(t, n, r) { var u = e.templateSettings; r && Wi(t, n, r) && (n = ut), t = Mf(t), n = mE({}, n, u, pi); var i, o, a = mE({}, n.imports, u.imports, pi), f = jf(a), c = G(a, f), l = 0, s = n.interpolate || te, E = "__p += '", p = pl((n.escape || te).source + "|" + s.source + "|" + (s === Un ? Xn : te).source + "|" + (n.evaluate || te).source + "|$", "g"), h = "//# sourceURL=" + ("sourceURL" in n ? n.sourceURL : "lodash.templateSources[" + ++$e + "]") + "\n"; t.replace(p, function (n, e, r, u, a, f) { return r || (r = u), E += t.slice(l, f).replace(ne, k), e && (i = !0, E += "' +\n__e(" + e + ") +\n'"), a && (o = !0, E += "';\n" + a + ";\n__p += '"), r && (E += "' +\n((__t = (" + r + ")) == null ? '' : __t) +\n'"), l = f + n.length, n }), E += "';\n"; var R = n.variable; R || (E = "with (obj) {\n" + E + "\n}\n"), E = (o ? E.replace(On, "") : E).replace(dn, "$1").replace(gn, "$1;"), E = "function(" + (R || "obj") + ") {\n" + (R ? "" : "obj || (obj = {});\n") + "var __t, __p = ''" + (i ? ", __e = _.escape" : "") + (o ? ", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n" : ";\n") + E + "return __p\n}"; var _ = np(function () { return ll(f, h + "return " + E).apply(ut, c) }); if (_.source = E, rf(_)) throw _; return _ } function dc(t) { return Mf(t).toLowerCase() } function gc(t) { return Mf(t).toUpperCase() } function Sc(t, n, e) { if (t = Mf(t), t && (e || n === ut)) return t.replace(Wn, ""); if (!t || !(n = _u(n))) return t; var r = tt(t), u = tt(n), i = W(r, u), o = F(r, u) + 1; return yu(r, i, o).join("") } function Lc(t, n, e) { if (t = Mf(t), t && (e || n === ut)) return t.replace(Bn, ""); if (!t || !(n = _u(n))) return t; var r = tt(t), u = F(r, tt(n)) + 1; return yu(r, 0, u).join("") } function yc(t, n, e) { if (t = Mf(t), t && (e || n === ut)) return t.replace(Fn, ""); if (!t || !(n = _u(n))) return t; var r = tt(t), u = W(r, tt(n)); return yu(r, u).join("") } function Cc(t, n) { + var e = yt, r = Ct; if (cf(n)) { + var u = "separator" in n ? n.separator : u; + e = "length" in n ? yf(n.length) : e, r = "omission" in n ? _u(n.omission) : r + } t = Mf(t); var i = t.length; if (V(t)) { var o = tt(t); i = o.length } if (e >= i) return t; var a = e - q(r); if (1 > a) return r; var f = o ? yu(o, 0, a).join("") : t.slice(0, a); if (u === ut) return f + r; if (o && (a += f.length - a), LE(u)) { if (t.slice(a).search(u)) { var c, l = f; for (u.global || (u = pl(u.source, Mf(Kn.exec(u)) + "g")), u.lastIndex = 0; c = u.exec(l);)var s = c.index; f = f.slice(0, s === ut ? a : s) } } else if (t.indexOf(_u(u), a) != a) { var E = f.lastIndexOf(u); E > -1 && (f = f.slice(0, E)) } return f + r + } function Pc(t) { return t = Mf(t), t && yn.test(t) ? t.replace(Sn, Or) : t } function Dc(t, n, e) { return t = Mf(t), n = e ? ut : n, n === ut ? Y(t) ? rt(t) : I(t) : t.match(n) || [] } function Uc(t) { var n = null == t ? 0 : t.length, e = gi(); return t = n ? R(t, function (t) { if ("function" != typeof t[1]) throw new Rl(ft); return [e(t[0]), t[1]] }) : [], iu(function (e) { for (var r = -1; ++r < n;) { var u = t[r]; if (a(u[0], this, e)) return a(u[1], this, e) } }) } function Mc(t) { return ke(Be(t, Et)) } function mc(t) { return function () { return t } } function wc(t, n) { return null == t || t !== t ? n : t } function bc(t) { return t } function Gc(t) { return Br("function" == typeof t ? t : Be(t, Et)) } function xc(t) { return jr(Be(t, Et)) } function Wc(t, n) { return Xr(t, Be(n, Et)) } function Fc(t, n, e) { var r = jf(n), u = or(n, r); null != e || cf(n) && (u.length || !r.length) || (e = n, n = t, t = this, u = or(n, jf(n))); var i = !(cf(e) && "chain" in e && !e.chain), o = of(t); return c(u, function (e) { var r = n[e]; t[e] = r, o && (t.prototype[e] = function () { var n = this.__chain__; if (i || n) { var e = t(this.__wrapped__), u = e.__actions__ = Bu(this.__actions__); return u.push({ func: r, args: arguments, thisArg: t }), e.__chain__ = n, e } return r.apply(t, _([this.value()], arguments)) }) }), t } function Bc() { return ar._ === this && (ar._ = Ll), this } function kc() { } function Hc(t) { return t = yf(t), iu(function (n) { return Qr(n, t) }) } function Vc(t) { return Fi(t) ? C(eo(t)) : qr(t) } function Yc(t) { return function (n) { return null == t ? ut : fr(t, n) } } function jc() { return [] } function Xc() { return !1 } function Kc() { return {} } function zc() { return "" } function Qc() { return !0 } function $c(t, n) { if (t = yf(t), 1 > t || t > bt) return []; var e = Wt, r = Jl(t, Wt); n = gi(n), t -= Wt; for (var u = m(r, n); ++e < t;)n(e); return u } function Jc(t) { return IE(t) ? R(t, eo) : If(t) ? [t] : Bu(Gs(Mf(t))) } function Zc(t) { var n = ++Ol; return Mf(t) + n } function qc(t) { return t && t.length ? ze(t, bc, Er) : ut } function tl(t, n) { return t && t.length ? ze(t, gi(n, 2), Er) : ut } function nl(t) { return y(t, bc) } function el(t, n) { return y(t, gi(n, 2)) } function rl(t) { return t && t.length ? ze(t, bc, Vr) : ut } function ul(t, n) { return t && t.length ? ze(t, gi(n, 2), Vr) : ut } function il(t) { return t && t.length ? M(t, bc) : 0 } function ol(t, n) { return t && t.length ? M(t, gi(n, 2)) : 0 } n = null == n ? ar : gr.defaults(ar.Object(), n, gr.pick(ar, Qe)); var al = n.Array, fl = n.Date, cl = n.Error, ll = n.Function, sl = n.Math, El = n.Object, pl = n.RegExp, hl = n.String, Rl = n.TypeError, _l = al.prototype, Tl = ll.prototype, vl = El.prototype, Nl = n["__core-js_shared__"], Al = Tl.toString, Il = vl.hasOwnProperty, Ol = 0, dl = function () { var t = /[^.]+$/.exec(Nl && Nl.keys && Nl.keys.IE_PROTO || ""); return t ? "Symbol(src)_1." + t : "" }(), gl = vl.toString, Sl = Al.call(El), Ll = ar._, yl = pl("^" + Al.call(Il).replace(Gn, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"), Cl = lr ? n.Buffer : ut, Pl = n.Symbol, Dl = n.Uint8Array, Ul = Cl ? Cl.allocUnsafe : ut, Ml = K(El.getPrototypeOf, El), ml = El.create, wl = vl.propertyIsEnumerable, bl = _l.splice, Gl = Pl ? Pl.isConcatSpreadable : ut, xl = Pl ? Pl.iterator : ut, Wl = Pl ? Pl.toStringTag : ut, Fl = function () { try { var t = yi(El, "defineProperty"); return t({}, "", {}), t } catch (t) { } }(), Bl = n.clearTimeout !== ar.clearTimeout && n.clearTimeout, kl = fl && fl.now !== ar.Date.now && fl.now, Hl = n.setTimeout !== ar.setTimeout && n.setTimeout, Vl = sl.ceil, Yl = sl.floor, jl = El.getOwnPropertySymbols, Xl = Cl ? Cl.isBuffer : ut, Kl = n.isFinite, zl = _l.join, Ql = K(El.keys, El), $l = sl.max, Jl = sl.min, Zl = fl.now, ql = n.parseInt, ts = sl.random, ns = _l.reverse, es = yi(n, "DataView"), rs = yi(n, "Map"), us = yi(n, "Promise"), is = yi(n, "Set"), os = yi(n, "WeakMap"), as = yi(El, "create"), fs = os && new os, cs = {}, ls = ro(es), ss = ro(rs), Es = ro(us), ps = ro(is), hs = ro(os), Rs = Pl ? Pl.prototype : ut, _s = Rs ? Rs.valueOf : ut, Ts = Rs ? Rs.toString : ut, vs = function () { function t() { } return function (n) { if (!cf(n)) return {}; if (ml) return ml(n); t.prototype = n; var e = new t; return t.prototype = ut, e } }(); e.templateSettings = { escape: Pn, evaluate: Dn, interpolate: Un, variable: "", imports: { _: e } }, e.prototype = r.prototype, e.prototype.constructor = e, u.prototype = vs(r.prototype), u.prototype.constructor = u, A.prototype = vs(r.prototype), A.prototype.constructor = A, et.prototype.clear = Yn, et.prototype.delete = ee, et.prototype.get = re, et.prototype.has = ue, et.prototype.set = ie, oe.prototype.clear = ae, oe.prototype.delete = fe, oe.prototype.get = ce, oe.prototype.has = le, oe.prototype.set = se, Ee.prototype.clear = pe, Ee.prototype.delete = he, Ee.prototype.get = Re, Ee.prototype.has = _e, Ee.prototype.set = Te, ve.prototype.add = ve.prototype.push = Ne, ve.prototype.has = Ae, Ie.prototype.clear = Oe, Ie.prototype.delete = de, Ie.prototype.get = ge, Ie.prototype.has = Se, Ie.prototype.set = Le; var Ns = Xu(er), As = Xu(ir, !0), Is = Ku(), Os = Ku(!0), ds = fs ? function (t, n) { return fs.set(t, n), t } : bc, gs = Fl ? function (t, n) { return Fl(t, "toString", { configurable: !0, enumerable: !1, value: mc(n), writable: !0 }) } : bc, Ss = iu, Ls = Bl || function (t) { return ar.clearTimeout(t) }, ys = is && 1 / Q(new is([, -0]))[1] == wt ? function (t) { return new is(t) } : kc, Cs = fs ? function (t) { return fs.get(t) } : kc, Ps = jl ? function (t) { return null == t ? [] : (t = El(t), E(jl(t), function (n) { return wl.call(t, n) })) } : jc, Ds = jl ? function (t) { for (var n = []; t;)_(n, Ps(t)), t = Ml(t); return n } : jc, Us = sr; (es && Us(new es(new ArrayBuffer(1))) != En || rs && Us(new rs) != Jt || us && Us(us.resolve()) != nn || is && Us(new is) != un || os && Us(new os) != cn) && (Us = function (t) { var n = sr(t), e = n == tn ? t.constructor : ut, r = e ? ro(e) : ""; if (r) switch (r) { case ls: return En; case ss: return Jt; case Es: return nn; case ps: return un; case hs: return cn }return n }); var Ms = Nl ? of : Xc, ms = to(ds), ws = Hl || function (t, n) { return ar.setTimeout(t, n) }, bs = to(gs), Gs = Xi(function (t) { var n = []; return wn.test(t) && n.push(""), t.replace(bn, function (t, e, r, u) { n.push(r ? u.replace(jn, "$1") : e || t) }), n }), xs = iu(function (t, n) { return Ja(t) ? Xe(t, nr(n, 1, Ja, !0)) : [] }), Ws = iu(function (t, n) { var e = So(n); return Ja(e) && (e = ut), Ja(t) ? Xe(t, nr(n, 1, Ja, !0), gi(e, 2)) : [] }), Fs = iu(function (t, n) { var e = So(n); return Ja(e) && (e = ut), Ja(t) ? Xe(t, nr(n, 1, Ja, !0), ut, e) : [] }), Bs = iu(function (t) { var n = R(t, gu); return n.length && n[0] === t[0] ? Lr(n) : [] }), ks = iu(function (t) { var n = So(t), e = R(t, gu); return n === So(e) ? n = ut : e.pop(), e.length && e[0] === t[0] ? Lr(e, gi(n, 2)) : [] }), Hs = iu(function (t) { var n = So(t), e = R(t, gu); return n = "function" == typeof n ? n : ut, n && e.pop(), e.length && e[0] === t[0] ? Lr(e, ut, n) : [] }), Vs = iu(Co), Ys = Ni(function (t, n) { var e = null == t ? 0 : t.length, r = We(t, n); return nu(t, R(n, function (t) { return xi(t, e) ? +t : t }).sort(Gu)), r }), js = iu(function (t) { return Tu(nr(t, 1, Ja, !0)) }), Xs = iu(function (t) { var n = So(t); return Ja(n) && (n = ut), Tu(nr(t, 1, Ja, !0), gi(n, 2)) }), Ks = iu(function (t) { var n = So(t); return n = "function" == typeof n ? n : ut, Tu(nr(t, 1, Ja, !0), ut, n) }), zs = iu(function (t, n) { return Ja(t) ? Xe(t, n) : [] }), Qs = iu(function (t) { return Ou(E(t, Ja)) }), $s = iu(function (t) { var n = So(t); return Ja(n) && (n = ut), Ou(E(t, Ja), gi(n, 2)) }), Js = iu(function (t) { var n = So(t); return n = "function" == typeof n ? n : ut, Ou(E(t, Ja), ut, n) }), Zs = iu($o), qs = iu(function (t) { var n = t.length, e = n > 1 ? t[n - 1] : ut; return e = "function" == typeof e ? (t.pop(), e) : ut, Jo(t, e) }), tE = Ni(function (t) { var n = t.length, e = n ? t[0] : 0, r = this.__wrapped__, i = function (n) { return We(n, t) }; return 1 >= n && !this.__actions__.length && r instanceof A && xi(e) ? (r = r.slice(e, +e + (n ? 1 : 0)), r.__actions__.push({ func: ea, args: [i], thisArg: ut }), new u(r, this.__chain__).thru(function (t) { return n && !t.length && t.push(ut), t })) : this.thru(i) }), nE = Yu(function (t, n, e) { Il.call(t, e) ? ++t[e] : xe(t, e, 1) }), eE = qu(ho), rE = qu(Ro), uE = Yu(function (t, n, e) { Il.call(t, e) ? t[e].push(n) : xe(t, e, [n]) }), iE = iu(function (t, n, e) { var r = -1, u = "function" == typeof n, i = $a(t) ? al(t.length) : []; return Ns(t, function (t) { i[++r] = u ? a(n, t, e) : Cr(t, n, e) }), i }), oE = Yu(function (t, n, e) { xe(t, e, n) }), aE = Yu(function (t, n, e) { t[e ? 0 : 1].push(n) }, function () { return [[], []] }), fE = iu(function (t, n) { if (null == t) return []; var e = n.length; return e > 1 && Wi(t, n[0], n[1]) ? n = [] : e > 2 && Wi(n[0], n[1], n[2]) && (n = [n[0]]), $r(t, nr(n, 1), []) }), cE = kl || function () { return ar.Date.now() }, lE = iu(function (t, n, e) { var r = Tt; if (e.length) { var u = z(e, di(lE)); r |= Ot } return Ei(t, r, n, e, u) }), sE = iu(function (t, n, e) { var r = Tt | vt; if (e.length) { var u = z(e, di(sE)); r |= Ot } return Ei(n, r, t, e, u) }), EE = iu(function (t, n) { return je(t, 1, n) }), pE = iu(function (t, n, e) { return je(t, Pf(n) || 0, e) }); ba.Cache = Ee; var hE = Ss(function (t, n) { n = 1 == n.length && IE(n[0]) ? R(n[0], b(gi())) : R(nr(n, 1), b(gi())); var e = n.length; return iu(function (r) { for (var u = -1, i = Jl(r.length, e); ++u < i;)r[u] = n[u].call(this, r[u]); return a(t, this, r) }) }), RE = iu(function (t, n) { var e = z(n, di(RE)); return Ei(t, Ot, ut, n, e) }), _E = iu(function (t, n) { var e = z(n, di(_E)); return Ei(t, dt, ut, n, e) }), TE = Ni(function (t, n) { return Ei(t, St, ut, ut, ut, n) }), vE = fi(Er), NE = fi(function (t, n) { return t >= n }), AE = Pr(function () { return arguments }()) ? Pr : function (t) { return lf(t) && Il.call(t, "callee") && !wl.call(t, "callee") }, IE = al.isArray, OE = pr ? b(pr) : Dr, dE = Xl || Xc, gE = hr ? b(hr) : Ur, SE = Rr ? b(Rr) : wr, LE = _r ? b(_r) : xr, yE = Tr ? b(Tr) : Wr, CE = vr ? b(vr) : Fr, PE = fi(Vr), DE = fi(function (t, n) { return n >= t }), UE = ju(function (t, n) { if (Vi(n) || $a(n)) return ku(n, jf(n), t), ut; for (var e in n) Il.call(n, e) && Me(t, e, n[e]) }), ME = ju(function (t, n) { ku(n, Xf(n), t) }), mE = ju(function (t, n, e, r) { ku(n, Xf(n), t, r) }), wE = ju(function (t, n, e, r) { ku(n, jf(n), t, r) }), bE = Ni(We), GE = iu(function (t) { return t.push(ut, pi), a(mE, ut, t) }), xE = iu(function (t) { return t.push(ut, hi), a(HE, ut, t) }), WE = ei(function (t, n, e) { t[n] = e }, mc(bc)), FE = ei(function (t, n, e) { Il.call(t, n) ? t[n].push(e) : t[n] = [e] }, gi), BE = iu(Cr), kE = ju(function (t, n, e) { Kr(t, n, e) }), HE = ju(function (t, n, e, r) { Kr(t, n, e, r) }), VE = Ni(function (t, n) { var e = {}; if (null == t) return e; var r = !1; n = R(n, function (n) { return n = Lu(n, t), r || (r = n.length > 1), n }), ku(t, Ii(t), e), r && (e = Be(e, Et | pt | ht, Ri)); for (var u = n.length; u--;)vu(e, n[u]); return e }), YE = Ni(function (t, n) { return null == t ? {} : Jr(t, n) }), jE = si(jf), XE = si(Xf), KE = $u(function (t, n, e) { return n = n.toLowerCase(), t + (e ? cc(n) : n) }), zE = $u(function (t, n, e) { return t + (e ? "-" : "") + n.toLowerCase() }), QE = $u(function (t, n, e) { return t + (e ? " " : "") + n.toLowerCase() }), $E = Qu("toLowerCase"), JE = $u(function (t, n, e) { return t + (e ? "_" : "") + n.toLowerCase() }), ZE = $u(function (t, n, e) { return t + (e ? " " : "") + tp(n) }), qE = $u(function (t, n, e) { return t + (e ? " " : "") + n.toUpperCase() }), tp = Qu("toUpperCase"), np = iu(function (t, n) { try { return a(t, ut, n) } catch (t) { return rf(t) ? t : new cl(t) } }), ep = Ni(function (t, n) { return c(n, function (n) { n = eo(n), xe(t, n, lE(t[n], t)) }), t }), rp = ti(), up = ti(!0), ip = iu(function (t, n) { return function (e) { return Cr(e, t, n) } }), op = iu(function (t, n) { return function (e) { return Cr(t, e, n) } }), ap = ui(R), fp = ui(s), cp = ui(N), lp = ai(), sp = ai(!0), Ep = ri(function (t, n) { return t + n }, 0), pp = li("ceil"), hp = ri(function (t, n) { return t / n }, 1), Rp = li("floor"), _p = ri(function (t, n) { return t * n }, 1), Tp = li("round"), vp = ri(function (t, n) { return t - n }, 0); return e.after = Ca, e.ary = Pa, e.assign = UE, e.assignIn = ME, e.assignInWith = mE, e.assignWith = wE, e.at = bE, e.before = Da, e.bind = lE, e.bindAll = ep, e.bindKey = sE, e.castArray = Va, e.chain = ta, e.chunk = oo, e.compact = ao, e.concat = fo, e.cond = Uc, e.conforms = Mc, e.constant = mc, e.countBy = nE, e.create = mf, e.curry = Ua, e.curryRight = Ma, e.debounce = ma, e.defaults = GE, e.defaultsDeep = xE, e.defer = EE, e.delay = pE, e.difference = xs, e.differenceBy = Ws, e.differenceWith = Fs, e.drop = co, e.dropRight = lo, e.dropRightWhile = so, e.dropWhile = Eo, e.fill = po, e.filter = sa, e.flatMap = Ea, e.flatMapDeep = pa, e.flatMapDepth = ha, e.flatten = _o, e.flattenDeep = To, e.flattenDepth = vo, e.flip = wa, e.flow = rp, e.flowRight = up, e.fromPairs = No, e.functions = Bf, e.functionsIn = kf, e.groupBy = uE, e.initial = Oo, e.intersection = Bs, e.intersectionBy = ks, e.intersectionWith = Hs, e.invert = WE, e.invertBy = FE, e.invokeMap = iE, e.iteratee = Gc, e.keyBy = oE, e.keys = jf, e.keysIn = Xf, e.map = va, e.mapKeys = Kf, e.mapValues = zf, e.matches = xc, e.matchesProperty = Wc, e.memoize = ba, e.merge = kE, e.mergeWith = HE, e.method = ip, e.methodOf = op, e.mixin = Fc, e.negate = Ga, e.nthArg = Hc, e.omit = VE, e.omitBy = Qf, e.once = xa, e.orderBy = Na, e.over = ap, e.overArgs = hE, e.overEvery = fp, e.overSome = cp, e.partial = RE, e.partialRight = _E, e.partition = aE, e.pick = YE, e.pickBy = $f, e.property = Vc, e.propertyOf = Yc, e.pull = Vs, e.pullAll = Co, e.pullAllBy = Po, e.pullAllWith = Do, e.pullAt = Ys, e.range = lp, e.rangeRight = sp, e.rearg = TE, e.reject = Oa, e.remove = Uo, e.rest = Wa, e.reverse = Mo, e.sampleSize = ga, e.set = Zf, e.setWith = qf, e.shuffle = Sa, e.slice = mo, e.sortBy = fE, e.sortedUniq = Bo, e.sortedUniqBy = ko, e.split = Ac, e.spread = Fa, e.tail = Ho, e.take = Vo, e.takeRight = Yo, e.takeRightWhile = jo, e.takeWhile = Xo, e.tap = na, e.throttle = Ba, e.thru = ea, e.toArray = Sf, e.toPairs = jE, e.toPairsIn = XE, e.toPath = Jc, e.toPlainObject = Df, e.transform = tc, e.unary = ka, e.union = js, e.unionBy = Xs, e.unionWith = Ks, e.uniq = Ko, e.uniqBy = zo, e.uniqWith = Qo, e.unset = nc, e.unzip = $o, e.unzipWith = Jo, e.update = ec, e.updateWith = rc, e.values = uc, e.valuesIn = ic, e.without = zs, e.words = Dc, e.wrap = Ha, e.xor = Qs, e.xorBy = $s, e.xorWith = Js, e.zip = Zs, e.zipObject = Zo, e.zipObjectDeep = qo, e.zipWith = qs, e.entries = jE, e.entriesIn = XE, e.extend = ME, e.extendWith = mE, Fc(e, e), e.add = Ep, e.attempt = np, e.camelCase = KE, e.capitalize = cc, e.ceil = pp, e.clamp = oc, e.clone = Ya, e.cloneDeep = Xa, e.cloneDeepWith = Ka, e.cloneWith = ja, e.conformsTo = za, e.deburr = lc, e.defaultTo = wc, e.divide = hp, e.endsWith = sc, e.eq = Qa, e.escape = Ec, e.escapeRegExp = pc, e.every = la, e.find = eE, e.findIndex = ho, e.findKey = wf, e.findLast = rE, e.findLastIndex = Ro, e.findLastKey = bf, e.floor = Rp, e.forEach = Ra, e.forEachRight = _a, e.forIn = Gf, e.forInRight = xf, e.forOwn = Wf, e.forOwnRight = Ff, e.get = Hf, e.gt = vE, e.gte = NE, e.has = Vf, e.hasIn = Yf, e.head = Ao, e.identity = bc, e.includes = Ta, e.indexOf = Io, e.inRange = ac, e.invoke = BE, e.isArguments = AE, e.isArray = IE, e.isArrayBuffer = OE, e.isArrayLike = $a, e.isArrayLikeObject = Ja, e.isBoolean = Za, e.isBuffer = dE, e.isDate = gE, e.isElement = qa, e.isEmpty = tf, e.isEqual = nf, e.isEqualWith = ef, e.isError = rf, e.isFinite = uf, e.isFunction = of, e.isInteger = af, e.isLength = ff, e.isMap = SE, e.isMatch = sf, e.isMatchWith = Ef, e.isNaN = pf, e.isNative = hf, e.isNil = _f, e.isNull = Rf, e.isNumber = Tf, e.isObject = cf, e.isObjectLike = lf, e.isPlainObject = vf, e.isRegExp = LE, e.isSafeInteger = Nf, e.isSet = yE, e.isString = Af, e.isSymbol = If, e.isTypedArray = CE, e.isUndefined = Of, e.isWeakMap = df, e.isWeakSet = gf, e.join = go, e.kebabCase = zE, e.last = So, e.lastIndexOf = Lo, e.lowerCase = QE, e.lowerFirst = $E, e.lt = PE, e.lte = DE, e.max = qc, e.maxBy = tl, e.mean = nl, e.meanBy = el, e.min = rl, e.minBy = ul, e.stubArray = jc, e.stubFalse = Xc, e.stubObject = Kc, e.stubString = zc, e.stubTrue = Qc, e.multiply = _p, e.nth = yo, e.noConflict = Bc, e.noop = kc, e.now = cE, e.pad = hc, e.padEnd = Rc, e.padStart = _c, e.parseInt = Tc, e.random = fc, e.reduce = Aa, e.reduceRight = Ia, e.repeat = vc, e.replace = Nc, e.result = Jf, e.round = Tp, e.runInContext = t, e.sample = da, e.size = La, e.snakeCase = JE, e.some = ya, e.sortedIndex = wo, e.sortedIndexBy = bo, e.sortedIndexOf = Go, e.sortedLastIndex = xo, e.sortedLastIndexBy = Wo, e.sortedLastIndexOf = Fo, e.startCase = ZE, e.startsWith = Ic, e.subtract = vp, e.sum = il, e.sumBy = ol, e.template = Oc, e.times = $c, e.toFinite = Lf, e.toInteger = yf, e.toLength = Cf, e.toLower = dc, e.toNumber = Pf, e.toSafeInteger = Uf, e.toString = Mf, e.toUpper = gc, e.trim = Sc, e.trimEnd = Lc, e.trimStart = yc, e.truncate = Cc, e.unescape = Pc, e.uniqueId = Zc, e.upperCase = qE, e.upperFirst = tp, e.each = Ra, e.eachRight = _a, e.first = Ao, Fc(e, function () { var t = {}; return er(e, function (n, r) { Il.call(e.prototype, r) || (t[r] = n) }), t }(), { chain: !1 }), e.VERSION = it, c(["bind", "bindKey", "curry", "curryRight", "partial", "partialRight"], function (t) { e[t].placeholder = e }), c(["drop", "take"], function (t, n) { A.prototype[t] = function (e) { e = e === ut ? 1 : $l(yf(e), 0); var r = this.__filtered__ && !n ? new A(this) : this.clone(); return r.__filtered__ ? r.__takeCount__ = Jl(e, r.__takeCount__) : r.__views__.push({ size: Jl(e, Wt), type: t + (0 > r.__dir__ ? "Right" : "") }), r }, A.prototype[t + "Right"] = function (n) { return this.reverse()[t](n).reverse() } }), c(["filter", "map", "takeWhile"], function (t, n) { var e = n + 1, r = e == Ut || e == mt; A.prototype[t] = function (t) { var n = this.clone(); return n.__iteratees__.push({ iteratee: gi(t, 3), type: e }), n.__filtered__ = n.__filtered__ || r, n } }), c(["head", "last"], function (t, n) { var e = "take" + (n ? "Right" : ""); A.prototype[t] = function () { return this[e](1).value()[0] } }), c(["initial", "tail"], function (t, n) { var e = "drop" + (n ? "" : "Right"); A.prototype[t] = function () { return this.__filtered__ ? new A(this) : this[e](1) } }), A.prototype.compact = function () { return this.filter(bc) }, A.prototype.find = function (t) { return this.filter(t).head() }, A.prototype.findLast = function (t) { return this.reverse().find(t) }, A.prototype.invokeMap = iu(function (t, n) { return "function" == typeof t ? new A(this) : this.map(function (e) { return Cr(e, t, n) }) }), A.prototype.reject = function (t) { return this.filter(Ga(gi(t))) }, A.prototype.slice = function (t, n) { t = yf(t); var e = this; return e.__filtered__ && (t > 0 || 0 > n) ? new A(e) : (0 > t ? e = e.takeRight(-t) : t && (e = e.drop(t)), n !== ut && (n = yf(n), e = 0 > n ? e.dropRight(-n) : e.take(n - t)), e) }, A.prototype.takeRightWhile = function (t) { return this.reverse().takeWhile(t).reverse() }, A.prototype.toArray = function () { return this.take(Wt) }, er(A.prototype, function (t, n) { var r = /^(?:filter|find|map|reject)|While$/.test(n), i = /^(?:head|last)$/.test(n), o = e[i ? "take" + ("last" == n ? "Right" : "") : n], a = i || /^find/.test(n); o && (e.prototype[n] = function () { var n = this.__wrapped__, f = i ? [1] : arguments, c = n instanceof A, l = f[0], s = c || IE(n), E = function (t) { var n = o.apply(e, _([t], f)); return i && p ? n[0] : n }; s && r && "function" == typeof l && 1 != l.length && (c = s = !1); var p = this.__chain__, h = !!this.__actions__.length, R = a && !p, T = c && !h; if (!a && s) { n = T ? n : new A(this); var v = t.apply(n, f); return v.__actions__.push({ func: ea, args: [E], thisArg: ut }), new u(v, p) } return R && T ? t.apply(this, f) : (v = this.thru(E), R ? i ? v.value()[0] : v.value() : v) }) }), c(["pop", "push", "shift", "sort", "splice", "unshift"], function (t) { var n = _l[t], r = /^(?:push|sort|unshift)$/.test(t) ? "tap" : "thru", u = /^(?:pop|shift)$/.test(t); e.prototype[t] = function () { var t = arguments; if (u && !this.__chain__) { var e = this.value(); return n.apply(IE(e) ? e : [], t) } return this[r](function (e) { return n.apply(IE(e) ? e : [], t) }) } }), er(A.prototype, function (t, n) { var r = e[n]; if (r) { var u = r.name + "", i = cs[u] || (cs[u] = []); i.push({ name: n, func: r }) } }), cs[ni(ut, vt).name] = [{ name: "wrapper", func: ut }], A.prototype.clone = P, A.prototype.reverse = J, A.prototype.value = nt, e.prototype.at = tE, e.prototype.chain = ra, e.prototype.commit = ua, e.prototype.next = ia, e.prototype.plant = aa, e.prototype.reverse = fa, e.prototype.toJSON = e.prototype.valueOf = e.prototype.value = ca, e.prototype.first = e.prototype.head, xl && (e.prototype[xl] = oa), e + }, gr = dr(); ar._ = gr, r = function () { return gr }.call(n, e, n, u), !(r !== ut && (u.exports = r)) + }).call(this) + }).call(n, function () { return this }(), e(12)(t)) + }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(5), a = r(o), f = "top-level", c = "block-level", l = function () { function t(n) { (0, i.default)(this, t), this.indent = n || " ", this.indentTypes = [] } return t.prototype.getIndent = function () { return a.default.repeat(this.indent, this.indentTypes.length) }, t.prototype.increaseToplevel = function () { this.indentTypes.push(f) }, t.prototype.increaseBlockLevel = function () { this.indentTypes.push(c) }, t.prototype.decreaseTopLevel = function () { a.default.last(this.indentTypes) === f && this.indentTypes.pop() }, t.prototype.decreaseBlockLevel = function () { for (; this.indentTypes.length > 0;) { var t = this.indentTypes.pop(); if (t !== f) break } }, t }(); n.default = l, t.exports = n.default }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(4), a = r(o), f = 50, c = function () { function t() { (0, i.default)(this, t), this.level = 0 } return t.prototype.beginIfPossible = function (t, n) { 0 === this.level && this.isInlineBlock(t, n) ? this.level = 1 : this.level > 0 ? this.level++ : this.level = 0 }, t.prototype.end = function () { this.level-- }, t.prototype.isActive = function () { return this.level > 0 }, t.prototype.isInlineBlock = function (t, n) { for (var e = 0, r = 0, u = n; t.length > u; u++) { var i = t[u]; if (e += i.value.length, e > f) return !1; if (i.type === a.default.OPEN_PAREN) r++; else if (i.type === a.default.CLOSE_PAREN && (r--, 0 === r)) return !0; if (this.isForbiddenToken(i)) return !1 } return !1 }, t.prototype.isForbiddenToken = function (t) { var n = t.type, e = t.value; return n === a.default.RESERVED_TOPLEVEL || n === a.default.RESERVED_NEWLINE || n === a.default.COMMENT || n === a.default.BLOCK_COMMENT || ";" === e }, t }(); n.default = c, t.exports = n.default }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = function () { function t(n) { (0, i.default)(this, t), this.params = n, this.index = 0 } return t.prototype.get = function (t) { var n = t.key, e = t.value; return this.params ? n ? this.params[n] : this.params[this.index++] : e }, t }(); n.default = o, t.exports = n.default }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(2), a = r(o), f = e(3), c = r(f), l = ["ABS", "ACTIVATE", "ALIAS", "ALL", "ALLOCATE", "ALLOW", "ALTER", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSOCIATE", "ASUTIME", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTES", "AUDIT", "AUTHORIZATION", "AUX", "AUXILIARY", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BUFFERPOOL", "BY", "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CASCADED", "CASE", "CAST", "CCSID", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLONE", "CLOSE", "CLUSTER", "COALESCE", "COLLATE", "COLLECT", "COLLECTION", "COLLID", "COLUMN", "COMMENT", "COMMIT", "CONCAT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONTAINS", "CONTINUE", "CONVERT", "CORR", "CORRESPONDING", "COUNT", "COUNT_BIG", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_LC_CTYPE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA", "CURRENT_SERVER", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TIMEZONE", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATABASE", "DATAPARTITIONNAME", "DATAPARTITIONNUM", "DATE", "DAY", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL", "DBINFO", "DBPARTITIONNAME", "DBPARTITIONNUM", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFINITION", "DELETE", "DENSERANK", "DENSE_RANK", "DEREF", "DESCRIBE", "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISABLE", "DISALLOW", "DISCONNECT", "DISTINCT", "DO", "DOCUMENT", "DOUBLE", "DROP", "DSSIZE", "DYNAMIC", "EACH", "EDITPROC", "ELEMENT", "ELSE", "ELSEIF", "ENABLE", "ENCODING", "ENCRYPTION", "END", "END-EXEC", "ENDING", "ERASE", "ESCAPE", "EVERY", "EXCEPTION", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTENDED", "EXTERNAL", "EXTRACT", "FALSE", "FENCED", "FETCH", "FIELDPROC", "FILE", "FILTER", "FINAL", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FULL", "FUNCTION", "FUSION", "GENERAL", "GENERATED", "GET", "GLOBAL", "GOTO", "GRANT", "GRAPHIC", "GROUP", "GROUPING", "HANDLER", "HASH", "HASHED_VALUE", "HINT", "HOLD", "HOUR", "HOURS", "IDENTITY", "IF", "IMMEDIATE", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INDEX", "INDICATOR", "INDICATORS", "INF", "INFINITY", "INHERIT", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTEGRITY", "INTERSECTION", "INTERVAL", "INTO", "IS", "ISOBID", "ISOLATION", "ITERATE", "JAR", "JAVA", "KEEP", "KEY", "LABEL", "LANGUAGE", "LARGE", "LATERAL", "LC_CTYPE", "LEADING", "LEAVE", "LEFT", "LIKE", "LINKTYPE", "LN", "LOCAL", "LOCALDATE", "LOCALE", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX", "LOCKSIZE", "LONG", "LOOP", "LOWER", "MAINTAINED", "MATCH", "MATERIALIZED", "MAX", "MAXVALUE", "MEMBER", "MERGE", "METHOD", "MICROSECOND", "MICROSECONDS", "MIN", "MINUTE", "MINUTES", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MONTHS", "MULTISET", "NAN", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEW_TABLE", "NEXTVAL", "NO", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER", "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NORMALIZE", "NORMALIZED", "NOT", "NULL", "NULLIF", "NULLS", "NUMERIC", "NUMPARTS", "OBID", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OLD_TABLE", "ON", "ONLY", "OPEN", "OPTIMIZATION", "OPTIMIZE", "OPTION", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", "PACKAGE", "PADDED", "PAGESIZE", "PARAMETER", "PART", "PARTITION", "PARTITIONED", "PARTITIONING", "PARTITIONS", "PASSWORD", "PATH", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PIECESIZE", "PLAN", "POSITION", "POWER", "PRECISION", "PREPARE", "PREVVAL", "PRIMARY", "PRIQTY", "PRIVILEGES", "PROCEDURE", "PROGRAM", "PSID", "PUBLIC", "QUERY", "QUERYNO", "RANGE", "RANK", "READ", "READS", "REAL", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RENAME", "REPEAT", "RESET", "RESIGNAL", "RESTART", "RESTRICT", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUND_CEILING", "ROUND_DOWN", "ROUND_FLOOR", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN", "ROUND_HALF_UP", "ROUND_UP", "ROUTINE", "ROW", "ROWNUMBER", "ROWS", "ROWSET", "ROW_NUMBER", "RRN", "RUN", "SAVEPOINT", "SCHEMA", "SCOPE", "SCRATCHPAD", "SCROLL", "SEARCH", "SECOND", "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", "SEQUENCE", "SESSION", "SESSION_USER", "SIGNAL", "SIMILAR", "SIMPLE", "SMALLINT", "SNAN", "SOME", "SOURCE", "SPECIFIC", "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLID", "SQLSTATE", "SQLWARNING", "SQRT", "STACKED", "STANDARD", "START", "STARTING", "STATEMENT", "STATIC", "STATMENT", "STAY", "STDDEV_POP", "STDDEV_SAMP", "STOGROUP", "STORES", "STYLE", "SUBMULTISET", "SUBSTRING", "SUM", "SUMMARY", "SYMMETRIC", "SYNONYM", "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TABLESPACE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TYPE", "UESCAPE", "UNDO", "UNIQUE", "UNKNOWN", "UNNEST", "UNTIL", "UPPER", "USAGE", "USER", "USING", "VALIDPROC", "VALUE", "VARCHAR", "VARIABLE", "VARIANT", "VARYING", "VAR_POP", "VAR_SAMP", "VCAT", "VERSION", "VIEW", "VOLATILE", "VOLUMES", "WHEN", "WHENEVER", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WLM", "WRITE", "XMLELEMENT", "XMLEXISTS", "XMLNAMESPACES", "YEAR", "YEARS"], s = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INTERSECT", "LIMIT", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UPDATE", "VALUES", "WHERE"], E = ["AND", "CROSS JOIN", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN"], p = void 0, h = function () { function t(n) { (0, i.default)(this, t), this.cfg = n } return t.prototype.format = function (t) { return p || (p = new c.default({ reservedWords: l, reservedToplevelWords: s, reservedNewlineWords: E, stringTypes: ['""', "''", "``", "[]"], openParens: ["("], closeParens: [")"], indexedPlaceholderTypes: ["?"], namedPlaceholderTypes: [":"], lineCommentTypes: ["--"], specialWordChars: ["#", "@"] })), new a.default(this.cfg, p).format(t) }, t }(); n.default = h, t.exports = n.default }, function (t, n, e) { "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(2), a = r(o), f = e(3), c = r(f), l = ["ALL", "ALTER", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "BEGIN", "BETWEEN", "BINARY", "BOOLEAN", "BREAK", "BUCKET", "BUILD", "BY", "CALL", "CASE", "CAST", "CLUSTER", "COLLATE", "COLLECTION", "COMMIT", "CONNECT", "CONTINUE", "CORRELATE", "COVER", "CREATE", "DATABASE", "DATASET", "DATASTORE", "DECLARE", "DECREMENT", "DELETE", "DERIVED", "DESC", "DESCRIBE", "DISTINCT", "DO", "DROP", "EACH", "ELEMENT", "ELSE", "END", "EVERY", "EXCEPT", "EXCLUDE", "EXECUTE", "EXISTS", "EXPLAIN", "FALSE", "FETCH", "FIRST", "FLATTEN", "FOR", "FORCE", "FROM", "FUNCTION", "GRANT", "GROUP", "GSI", "HAVING", "IF", "IGNORE", "ILIKE", "IN", "INCLUDE", "INCREMENT", "INDEX", "INFER", "INLINE", "INNER", "INSERT", "INTERSECT", "INTO", "IS", "JOIN", "KEY", "KEYS", "KEYSPACE", "KNOWN", "LAST", "LEFT", "LET", "LETTING", "LIKE", "LIMIT", "LSM", "MAP", "MAPPING", "MATCHED", "MATERIALIZED", "MERGE", "MINUS", "MISSING", "NAMESPACE", "NEST", "NOT", "NULL", "NUMBER", "OBJECT", "OFFSET", "ON", "OPTION", "OR", "ORDER", "OUTER", "OVER", "PARSE", "PARTITION", "PASSWORD", "PATH", "POOL", "PREPARE", "PRIMARY", "PRIVATE", "PRIVILEGE", "PROCEDURE", "PUBLIC", "RAW", "REALM", "REDUCE", "RENAME", "RETURN", "RETURNING", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "SATISFIES", "SCHEMA", "SELECT", "SELF", "SEMI", "SET", "SHOW", "SOME", "START", "STATISTICS", "STRING", "SYSTEM", "THEN", "TO", "TRANSACTION", "TRIGGER", "TRUE", "TRUNCATE", "UNDER", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UNSET", "UPDATE", "UPSERT", "USE", "USER", "USING", "VALIDATE", "VALUE", "VALUED", "VALUES", "VIA", "VIEW", "WHEN", "WHERE", "WHILE", "WITH", "WITHIN", "WORK", "XOR"], s = ["DELETE FROM", "EXCEPT ALL", "EXCEPT", "EXPLAIN DELETE FROM", "EXPLAIN UPDATE", "EXPLAIN UPSERT", "FROM", "GROUP BY", "HAVING", "INFER", "INSERT INTO", "INTERSECT ALL", "INTERSECT", "LET", "LIMIT", "MERGE", "NEST", "ORDER BY", "PREPARE", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UNNEST", "UPDATE", "UPSERT", "USE KEYS", "VALUES", "WHERE"], E = ["AND", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "XOR"], p = void 0, h = function () { function t(n) { (0, i.default)(this, t), this.cfg = n } return t.prototype.format = function (t) { return p || (p = new c.default({ reservedWords: l, reservedToplevelWords: s, reservedNewlineWords: E, stringTypes: ['""', "''", "``"], openParens: ["(", "[", "{"], closeParens: [")", "]", "}"], namedPlaceholderTypes: ["$"], lineCommentTypes: ["#", "--"] })), new a.default(this.cfg, p).format(t) }, t }(); n.default = h, t.exports = n.default }, function (t, n, e) { + "use strict"; function r(t) { return t && t.__esModule ? t : { default: t } } n.__esModule = !0; var u = e(1), i = r(u), o = e(2), a = r(o), f = e(3), c = r(f), l = ["ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", "AUTO_INCREMENT", "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", "CURRENT_TIMESTAMP", "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENDED", "FAST", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", "INTO", "INVOKER", "IS", "ISOLATION", "KEY", "KEYS", "KILL", "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", "NAMES", "NATURAL", "NOT", "NOW()", "NULL", "OFFSET", "ON DELETE", "ON UPDATE", "ON", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", "PURGE", "QUICK", "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", "YEAR_MONTH"], s = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INTERSECT", "LIMIT", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"], E = ["AND", "CROSS APPLY", "CROSS JOIN", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "XOR"], p = void 0, h = function () { + function t(n) { (0, i.default)(this, t), this.cfg = n } return t.prototype.format = function (t) { return p || (p = new c.default({ reservedWords: l, reservedToplevelWords: s, reservedNewlineWords: E, stringTypes: ['""', "N''", "''", "``", "[]"], openParens: ["("], closeParens: [")"], indexedPlaceholderTypes: ["?"], namedPlaceholderTypes: ["@", ":"], lineCommentTypes: ["#", "--"] })), new a.default(this.cfg, p).format(t) }, t + }(); n.default = h, t.exports = n.default + }, function (t, n) { t.exports = function (t) { return t.webpackPolyfill || (t.deprecate = function () { }, t.paths = [], t.children = [], t.webpackPolyfill = 1), t } }]) +}); \ No newline at end of file diff --git a/DocTools/TplFile/chm/hhc.cshtml b/DocTools/TplFile/chm/hhc.cshtml new file mode 100644 index 0000000..64044bd --- /dev/null +++ b/DocTools/TplFile/chm/hhc.cshtml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + +
    +
  • + + + + +
  • + @if (Model.Tables != null && Model.Tables.Count > 0) + { +
  • + + + +
      + @foreach (var table in Model.Tables) + { +
    • + + + + +
    • + } +
    +
  • + } + + @if (Model.Views != null && Model.Views.Count > 0) + { +
  • + + + +
      + @foreach (var item in Model.Views) + { +
    • + + + + +
    • + } +
    +
  • + + } + + + @if (Model.Procs != null && Model.Procs.Count > 0) + { +
  • + + + +
      + @foreach (var item in Model.Procs) + { +
    • + + + + +
    • + } +
    +
  • + } +
+ + diff --git a/DocTools/TplFile/chm/hhk.cshtml b/DocTools/TplFile/chm/hhk.cshtml new file mode 100644 index 0000000..6fc35aa --- /dev/null +++ b/DocTools/TplFile/chm/hhk.cshtml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + +
    +
  • + + + + +
  • + @if (Model.Tables != null && Model.Tables.Count > 0) + { + foreach (var tab in Model.Tables) + { +
  • + + + + +
  • + //hhkļ̫󣬲Ȼhhcʱᱨʾ An internal error has occurred.An error record has been saved to .. + //https://documentation.red-gate.com/sdoc5/troubleshooting/error-messages/assertion-failure-outputting-in-chm-format + @*foreach (var column in tab.Columns) + { +
  • + + + + +
  • + }*@ + } + } + + @if (Model.Views != null && Model.Views.Count > 0) + { + foreach (var item in Model.Views) + { +
  • + + + + +
  • + } + } + + + @if (Model.Procs != null && Model.Procs.Count > 0) + { + foreach (var item in Model.Procs) + { +
  • + + + + +
  • + } + } + +
+ + diff --git a/DocTools/TplFile/chm/hhp.cshtml b/DocTools/TplFile/chm/hhp.cshtml new file mode 100644 index 0000000..837e662 --- /dev/null +++ b/DocTools/TplFile/chm/hhp.cshtml @@ -0,0 +1,30 @@ +[OPTIONS] +Auto Index=Yes +CITATION=Made by lztkdr +Compatibility=1.1 or later +Compiled file=@Model.ChmFile +Contents file=chm.HHC +COPYRIGHT=www.51try.top +Default topic=@Model.DefaultFile +Default Window=Main +Display compile notes=Yes +Display compile progress=Yes +Full-text search=Yes +Language=0x804 (й) +Index file=chm.HHK +Title=@Model.Title +Enhanced decompilation=yes + +[WINDOWS] +Main="@Model.Title","chm.hhc","chm.hhk","@Model.DefaultFile","@Model.DefaultFile",,,,20000,0x63520,180,0x104E, [0,0,745,509],0x0,0x0,,,,,0 + +[MERGE FILES] + +[FILES] +@if (Model.Files != null && Model.Files.Count > 0) +{ + foreach (var filePath in Model.Files) + { + @(filePath + "\r\n") + } +} \ No newline at end of file diff --git a/DocTools/TplFile/chm/js/jQuery.js b/DocTools/TplFile/chm/js/jQuery.js new file mode 100644 index 0000000..c8b75a1 --- /dev/null +++ b/DocTools/TplFile/chm/js/jQuery.js @@ -0,0 +1,14 @@ +/*! jQuery v1.7 jquery.com | jquery.org/license */ +(function (a, b) { + function cA(a) { return f.isWindow(a) ? a : a.nodeType === 9 ? a.defaultView || a.parentWindow : !1 } function cx(a) { if (!cm[a]) { var b = c.body, d = f("<" + a + ">").appendTo(b), e = d.css("display"); d.remove(); if (e === "none" || e === "") { cn || (cn = c.createElement("iframe"), cn.frameBorder = cn.width = cn.height = 0), b.appendChild(cn); if (!co || !cn.createElement) co = (cn.contentWindow || cn.contentDocument).document, co.write((c.compatMode === "CSS1Compat" ? "" : "") + ""), co.close(); d = co.createElement(a), co.body.appendChild(d), e = f.css(d, "display"), b.removeChild(cn) } cm[a] = e } return cm[a] } function cw(a, b) { var c = {}; f.each(cs.concat.apply([], cs.slice(0, b)), function () { c[this] = a }); return c } function cv() { ct = b } function cu() { setTimeout(cv, 0); return ct = f.now() } function cl() { try { return new a.ActiveXObject("Microsoft.XMLHTTP") } catch (b) { } } function ck() { try { return new a.XMLHttpRequest } catch (b) { } } function ce(a, c) { a.dataFilter && (c = a.dataFilter(c, a.dataType)); var d = a.dataTypes, e = {}, g, h, i = d.length, j, k = d[0], l, m, n, o, p; for (g = 1; g < i; g++) { if (g === 1) for (h in a.converters) typeof h == "string" && (e[h.toLowerCase()] = a.converters[h]); l = k, k = d[g]; if (k === "*") k = l; else if (l !== "*" && l !== k) { m = l + " " + k, n = e[m] || e["* " + k]; if (!n) { p = b; for (o in e) { j = o.split(" "); if (j[0] === l || j[0] === "*") { p = e[j[1] + " " + k]; if (p) { o = e[o], o === !0 ? n = p : p === !0 && (n = o); break } } } } !n && !p && f.error("No conversion from " + m.replace(" ", " to ")), n !== !0 && (c = n ? n(c) : p(o(c))) } } return c } function cd(a, c, d) { var e = a.contents, f = a.dataTypes, g = a.responseFields, h, i, j, k; for (i in g) i in d && (c[g[i]] = d[i]); while (f[0] === "*") f.shift(), h === b && (h = a.mimeType || c.getResponseHeader("content-type")); if (h) for (i in e) if (e[i] && e[i].test(h)) { f.unshift(i); break } if (f[0] in d) j = f[0]; else { for (i in d) { if (!f[0] || a.converters[i + " " + f[0]]) { j = i; break } k || (k = i) } j = j || k } if (j) { j !== f[0] && f.unshift(j); return d[j] } } function cc(a, b, c, d) { if (f.isArray(b)) f.each(b, function (b, e) { c || bG.test(a) ? d(a, e) : cc(a + "[" + (typeof e == "object" || f.isArray(e) ? b : "") + "]", e, c, d) }); else if (!c && b != null && typeof b == "object") for (var e in b) cc(a + "[" + e + "]", b[e], c, d); else d(a, b) } function cb(a, c) { var d, e, g = f.ajaxSettings.flatOptions || {}; for (d in c) c[d] !== b && ((g[d] ? a : e || (e = {}))[d] = c[d]); e && f.extend(!0, a, e) } function ca(a, c, d, e, f, g) { f = f || c.dataTypes[0], g = g || {}, g[f] = !0; var h = a[f], i = 0, j = h ? h.length : 0, k = a === bV, l; for (; i < j && (k || !l); i++)l = h[i](c, d, e), typeof l == "string" && (!k || g[l] ? l = b : (c.dataTypes.unshift(l), l = ca(a, c, d, e, l, g))); (k || !l) && !g["*"] && (l = ca(a, c, d, e, "*", g)); return l } function b_(a) { return function (b, c) { typeof b != "string" && (c = b, b = "*"); if (f.isFunction(c)) { var d = b.toLowerCase().split(bR), e = 0, g = d.length, h, i, j; for (; e < g; e++)h = d[e], j = /^\+/.test(h), j && (h = h.substr(1) || "*"), i = a[h] = a[h] || [], i[j ? "unshift" : "push"](c) } } } function bE(a, b, c) { var d = b === "width" ? a.offsetWidth : a.offsetHeight, e = b === "width" ? bz : bA; if (d > 0) { c !== "border" && f.each(e, function () { c || (d -= parseFloat(f.css(a, "padding" + this)) || 0), c === "margin" ? d += parseFloat(f.css(a, c + this)) || 0 : d -= parseFloat(f.css(a, "border" + this + "Width")) || 0 }); return d + "px" } d = bB(a, b, b); if (d < 0 || d == null) d = a.style[b] || 0; d = parseFloat(d) || 0, c && f.each(e, function () { d += parseFloat(f.css(a, "padding" + this)) || 0, c !== "padding" && (d += parseFloat(f.css(a, "border" + this + "Width")) || 0), c === "margin" && (d += parseFloat(f.css(a, c + this)) || 0) }); return d + "px" } function br(a, b) { b.src ? f.ajax({ url: b.src, async: !1, dataType: "script" }) : f.globalEval((b.text || b.textContent || b.innerHTML || "").replace(bi, "/*$0*/")), b.parentNode && b.parentNode.removeChild(b) } function bq(a) { var b = (a.nodeName || "").toLowerCase(); b === "input" ? bp(a) : b !== "script" && typeof a.getElementsByTagName != "undefined" && f.grep(a.getElementsByTagName("input"), bp) } function bp(a) { if (a.type === "checkbox" || a.type === "radio") a.defaultChecked = a.checked } function bo(a) { return typeof a.getElementsByTagName != "undefined" ? a.getElementsByTagName("*") : typeof a.querySelectorAll != "undefined" ? a.querySelectorAll("*") : [] } function bn(a, b) { var c; if (b.nodeType === 1) { b.clearAttributes && b.clearAttributes(), b.mergeAttributes && b.mergeAttributes(a), c = b.nodeName.toLowerCase(); if (c === "object") b.outerHTML = a.outerHTML; else if (c !== "input" || a.type !== "checkbox" && a.type !== "radio") { if (c === "option") b.selected = a.defaultSelected; else if (c === "input" || c === "textarea") b.defaultValue = a.defaultValue } else a.checked && (b.defaultChecked = b.checked = a.checked), b.value !== a.value && (b.value = a.value); b.removeAttribute(f.expando) } } function bm(a, b) { if (b.nodeType === 1 && !!f.hasData(a)) { var c, d, e, g = f._data(a), h = f._data(b, g), i = g.events; if (i) { delete h.handle, h.events = {}; for (c in i) for (d = 0, e = i[c].length; d < e; d++)f.event.add(b, c + (i[c][d].namespace ? "." : "") + i[c][d].namespace, i[c][d], i[c][d].data) } h.data && (h.data = f.extend({}, h.data)) } } function bl(a, b) { return f.nodeName(a, "table") ? a.getElementsByTagName("tbody")[0] || a.appendChild(a.ownerDocument.createElement("tbody")) : a } function X(a) { var b = Y.split(" "), c = a.createDocumentFragment(); if (c.createElement) while (b.length) c.createElement(b.pop()); return c } function W(a, b, c) { b = b || 0; if (f.isFunction(b)) return f.grep(a, function (a, d) { var e = !!b.call(a, d, a); return e === c }); if (b.nodeType) return f.grep(a, function (a, d) { return a === b === c }); if (typeof b == "string") { var d = f.grep(a, function (a) { return a.nodeType === 1 }); if (R.test(b)) return f.filter(b, d, !c); b = f.filter(b, d) } return f.grep(a, function (a, d) { return f.inArray(a, b) >= 0 === c }) } function V(a) { return !a || !a.parentNode || a.parentNode.nodeType === 11 } function N() { return !0 } function M() { return !1 } function n(a, b, c) { var d = b + "defer", e = b + "queue", g = b + "mark", h = f._data(a, d); h && (c === "queue" || !f._data(a, e)) && (c === "mark" || !f._data(a, g)) && setTimeout(function () { !f._data(a, e) && !f._data(a, g) && (f.removeData(a, d, !0), h.fire()) }, 0) } function m(a) { for (var b in a) { if (b === "data" && f.isEmptyObject(a[b])) continue; if (b !== "toJSON") return !1 } return !0 } function l(a, c, d) { if (d === b && a.nodeType === 1) { var e = "data-" + c.replace(k, "-$1").toLowerCase(); d = a.getAttribute(e); if (typeof d == "string") { try { d = d === "true" ? !0 : d === "false" ? !1 : d === "null" ? null : f.isNumeric(d) ? parseFloat(d) : j.test(d) ? f.parseJSON(d) : d } catch (g) { } f.data(a, c, d) } else d = b } return d } function h(a) { var b = g[a] = {}, c, d; a = a.split(/\s+/); for (c = 0, d = a.length; c < d; c++)b[a[c]] = !0; return b } var c = a.document, d = a.navigator, e = a.location, f = function () { function K() { if (!e.isReady) { try { c.documentElement.doScroll("left") } catch (a) { setTimeout(K, 1); return } e.ready() } } var e = function (a, b) { return new e.fn.init(a, b, h) }, f = a.jQuery, g = a.$, h, i = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, j = /\S/, k = /^\s+/, l = /\s+$/, m = /\d/, n = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, o = /^[\],:{}\s]*$/, p = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, q = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, r = /(?:^|:|,)(?:\s*\[)+/g, s = /(webkit)[ \/]([\w.]+)/, t = /(opera)(?:.*version)?[ \/]([\w.]+)/, u = /(msie) ([\w.]+)/, v = /(mozilla)(?:.*? rv:([\w.]+))?/, w = /-([a-z]|[0-9])/ig, x = /^-ms-/, y = function (a, b) { return (b + "").toUpperCase() }, z = d.userAgent, A, B, C, D = Object.prototype.toString, E = Object.prototype.hasOwnProperty, F = Array.prototype.push, G = Array.prototype.slice, H = String.prototype.trim, I = Array.prototype.indexOf, J = {}; e.fn = e.prototype = { constructor: e, init: function (a, d, f) { var g, h, j, k; if (!a) return this; if (a.nodeType) { this.context = this[0] = a, this.length = 1; return this } if (a === "body" && !d && c.body) { this.context = c, this[0] = c.body, this.selector = a, this.length = 1; return this } if (typeof a == "string") { a.charAt(0) !== "<" || a.charAt(a.length - 1) !== ">" || a.length < 3 ? g = i.exec(a) : g = [null, a, null]; if (g && (g[1] || !d)) { if (g[1]) { d = d instanceof e ? d[0] : d, k = d ? d.ownerDocument || d : c, j = n.exec(a), j ? e.isPlainObject(d) ? (a = [c.createElement(j[1])], e.fn.attr.call(a, d, !0)) : a = [k.createElement(j[1])] : (j = e.buildFragment([g[1]], [k]), a = (j.cacheable ? e.clone(j.fragment) : j.fragment).childNodes); return e.merge(this, a) } h = c.getElementById(g[2]); if (h && h.parentNode) { if (h.id !== g[2]) return f.find(a); this.length = 1, this[0] = h } this.context = c, this.selector = a; return this } return !d || d.jquery ? (d || f).find(a) : this.constructor(d).find(a) } if (e.isFunction(a)) return f.ready(a); a.selector !== b && (this.selector = a.selector, this.context = a.context); return e.makeArray(a, this) }, selector: "", jquery: "1.7", length: 0, size: function () { return this.length }, toArray: function () { return G.call(this, 0) }, get: function (a) { return a == null ? this.toArray() : a < 0 ? this[this.length + a] : this[a] }, pushStack: function (a, b, c) { var d = this.constructor(); e.isArray(a) ? F.apply(d, a) : e.merge(d, a), d.prevObject = this, d.context = this.context, b === "find" ? d.selector = this.selector + (this.selector ? " " : "") + c : b && (d.selector = this.selector + "." + b + "(" + c + ")"); return d }, each: function (a, b) { return e.each(this, a, b) }, ready: function (a) { e.bindReady(), B.add(a); return this }, eq: function (a) { return a === -1 ? this.slice(a) : this.slice(a, +a + 1) }, first: function () { return this.eq(0) }, last: function () { return this.eq(-1) }, slice: function () { return this.pushStack(G.apply(this, arguments), "slice", G.call(arguments).join(",")) }, map: function (a) { return this.pushStack(e.map(this, function (b, c) { return a.call(b, c, b) })) }, end: function () { return this.prevObject || this.constructor(null) }, push: F, sort: [].sort, splice: [].splice }, e.fn.init.prototype = e.fn, e.extend = e.fn.extend = function () { var a, c, d, f, g, h, i = arguments[0] || {}, j = 1, k = arguments.length, l = !1; typeof i == "boolean" && (l = i, i = arguments[1] || {}, j = 2), typeof i != "object" && !e.isFunction(i) && (i = {}), k === j && (i = this, --j); for (; j < k; j++)if ((a = arguments[j]) != null) for (c in a) { d = i[c], f = a[c]; if (i === f) continue; l && f && (e.isPlainObject(f) || (g = e.isArray(f))) ? (g ? (g = !1, h = d && e.isArray(d) ? d : []) : h = d && e.isPlainObject(d) ? d : {}, i[c] = e.extend(l, h, f)) : f !== b && (i[c] = f) } return i }, e.extend({ noConflict: function (b) { a.$ === e && (a.$ = g), b && a.jQuery === e && (a.jQuery = f); return e }, isReady: !1, readyWait: 1, holdReady: function (a) { a ? e.readyWait++ : e.ready(!0) }, ready: function (a) { if (a === !0 && !--e.readyWait || a !== !0 && !e.isReady) { if (!c.body) return setTimeout(e.ready, 1); e.isReady = !0; if (a !== !0 && --e.readyWait > 0) return; B.fireWith(c, [e]), e.fn.trigger && e(c).trigger("ready").unbind("ready") } }, bindReady: function () { if (!B) { B = e.Callbacks("once memory"); if (c.readyState === "complete") return setTimeout(e.ready, 1); if (c.addEventListener) c.addEventListener("DOMContentLoaded", C, !1), a.addEventListener("load", e.ready, !1); else if (c.attachEvent) { c.attachEvent("onreadystatechange", C), a.attachEvent("onload", e.ready); var b = !1; try { b = a.frameElement == null } catch (d) { } c.documentElement.doScroll && b && K() } } }, isFunction: function (a) { return e.type(a) === "function" }, isArray: Array.isArray || function (a) { return e.type(a) === "array" }, isWindow: function (a) { return a && typeof a == "object" && "setInterval" in a }, isNumeric: function (a) { return a != null && m.test(a) && !isNaN(a) }, type: function (a) { return a == null ? String(a) : J[D.call(a)] || "object" }, isPlainObject: function (a) { if (!a || e.type(a) !== "object" || a.nodeType || e.isWindow(a)) return !1; try { if (a.constructor && !E.call(a, "constructor") && !E.call(a.constructor.prototype, "isPrototypeOf")) return !1 } catch (c) { return !1 } var d; for (d in a); return d === b || E.call(a, d) }, isEmptyObject: function (a) { for (var b in a) return !1; return !0 }, error: function (a) { throw a }, parseJSON: function (b) { if (typeof b != "string" || !b) return null; b = e.trim(b); if (a.JSON && a.JSON.parse) return a.JSON.parse(b); if (o.test(b.replace(p, "@").replace(q, "]").replace(r, ""))) return (new Function("return " + b))(); e.error("Invalid JSON: " + b) }, parseXML: function (c) { var d, f; try { a.DOMParser ? (f = new DOMParser, d = f.parseFromString(c, "text/xml")) : (d = new ActiveXObject("Microsoft.XMLDOM"), d.async = "false", d.loadXML(c)) } catch (g) { d = b } (!d || !d.documentElement || d.getElementsByTagName("parsererror").length) && e.error("Invalid XML: " + c); return d }, noop: function () { }, globalEval: function (b) { b && j.test(b) && (a.execScript || function (b) { a.eval.call(a, b) })(b) }, camelCase: function (a) { return a.replace(x, "ms-").replace(w, y) }, nodeName: function (a, b) { return a.nodeName && a.nodeName.toUpperCase() === b.toUpperCase() }, each: function (a, c, d) { var f, g = 0, h = a.length, i = h === b || e.isFunction(a); if (d) { if (i) { for (f in a) if (c.apply(a[f], d) === !1) break } else for (; g < h;)if (c.apply(a[g++], d) === !1) break } else if (i) { for (f in a) if (c.call(a[f], f, a[f]) === !1) break } else for (; g < h;)if (c.call(a[g], g, a[g++]) === !1) break; return a }, trim: H ? function (a) { return a == null ? "" : H.call(a) } : function (a) { return a == null ? "" : (a + "").replace(k, "").replace(l, "") }, makeArray: function (a, b) { var c = b || []; if (a != null) { var d = e.type(a); a.length == null || d === "string" || d === "function" || d === "regexp" || e.isWindow(a) ? F.call(c, a) : e.merge(c, a) } return c }, inArray: function (a, b, c) { var d; if (b) { if (I) return I.call(b, a, c); d = b.length, c = c ? c < 0 ? Math.max(0, d + c) : c : 0; for (; c < d; c++)if (c in b && b[c] === a) return c } return -1 }, merge: function (a, c) { var d = a.length, e = 0; if (typeof c.length == "number") for (var f = c.length; e < f; e++)a[d++] = c[e]; else while (c[e] !== b) a[d++] = c[e++]; a.length = d; return a }, grep: function (a, b, c) { var d = [], e; c = !!c; for (var f = 0, g = a.length; f < g; f++)e = !!b(a[f], f), c !== e && d.push(a[f]); return d }, map: function (a, c, d) { var f, g, h = [], i = 0, j = a.length, k = a instanceof e || j !== b && typeof j == "number" && (j > 0 && a[0] && a[j - 1] || j === 0 || e.isArray(a)); if (k) for (; i < j; i++)f = c(a[i], i, d), f != null && (h[h.length] = f); else for (g in a) f = c(a[g], g, d), f != null && (h[h.length] = f); return h.concat.apply([], h) }, guid: 1, proxy: function (a, c) { if (typeof c == "string") { var d = a[c]; c = a, a = d } if (!e.isFunction(a)) return b; var f = G.call(arguments, 2), g = function () { return a.apply(c, f.concat(G.call(arguments))) }; g.guid = a.guid = a.guid || g.guid || e.guid++; return g }, access: function (a, c, d, f, g, h) { var i = a.length; if (typeof c == "object") { for (var j in c) e.access(a, j, c[j], f, g, d); return a } if (d !== b) { f = !h && f && e.isFunction(d); for (var k = 0; k < i; k++)g(a[k], c, f ? d.call(a[k], k, g(a[k], c)) : d, h); return a } return i ? g(a[0], c) : b }, now: function () { return (new Date).getTime() }, uaMatch: function (a) { a = a.toLowerCase(); var b = s.exec(a) || t.exec(a) || u.exec(a) || a.indexOf("compatible") < 0 && v.exec(a) || []; return { browser: b[1] || "", version: b[2] || "0" } }, sub: function () { function a(b, c) { return new a.fn.init(b, c) } e.extend(!0, a, this), a.superclass = this, a.fn = a.prototype = this(), a.fn.constructor = a, a.sub = this.sub, a.fn.init = function (d, f) { f && f instanceof e && !(f instanceof a) && (f = a(f)); return e.fn.init.call(this, d, f, b) }, a.fn.init.prototype = a.fn; var b = a(c); return a }, browser: {} }), e.each("Boolean Number String Function Array Date RegExp Object".split(" "), function (a, b) { J["[object " + b + "]"] = b.toLowerCase() }), A = e.uaMatch(z), A.browser && (e.browser[A.browser] = !0, e.browser.version = A.version), e.browser.webkit && (e.browser.safari = !0), j.test(" ") && (k = /^[\s\xA0]+/, l = /[\s\xA0]+$/), h = e(c), c.addEventListener ? C = function () { c.removeEventListener("DOMContentLoaded", C, !1), e.ready() } : c.attachEvent && (C = function () { c.readyState === "complete" && (c.detachEvent("onreadystatechange", C), e.ready()) }), typeof define == "function" && define.amd && define.amd.jQuery && define("jquery", [], function () { return e }); return e }(), g = {}; f.Callbacks = function (a) { a = a ? g[a] || h(a) : {}; var c = [], d = [], e, i, j, k, l, m = function (b) { var d, e, g, h, i; for (d = 0, e = b.length; d < e; d++)g = b[d], h = f.type(g), h === "array" ? m(g) : h === "function" && (!a.unique || !o.has(g)) && c.push(g) }, n = function (b, f) { f = f || [], e = !a.memory || [b, f], i = !0, l = j || 0, j = 0, k = c.length; for (; c && l < k; l++)if (c[l].apply(b, f) === !1 && a.stopOnFalse) { e = !0; break } i = !1, c && (a.once ? e === !0 ? o.disable() : c = [] : d && d.length && (e = d.shift(), o.fireWith(e[0], e[1]))) }, o = { add: function () { if (c) { var a = c.length; m(arguments), i ? k = c.length : e && e !== !0 && (j = a, n(e[0], e[1])) } return this }, remove: function () { if (c) { var b = arguments, d = 0, e = b.length; for (; d < e; d++)for (var f = 0; f < c.length; f++)if (b[d] === c[f]) { i && f <= k && (k--, f <= l && l--), c.splice(f--, 1); if (a.unique) break } } return this }, has: function (a) { if (c) { var b = 0, d = c.length; for (; b < d; b++)if (a === c[b]) return !0 } return !1 }, empty: function () { c = []; return this }, disable: function () { c = d = e = b; return this }, disabled: function () { return !c }, lock: function () { d = b, (!e || e === !0) && o.disable(); return this }, locked: function () { return !d }, fireWith: function (b, c) { d && (i ? a.once || d.push([b, c]) : (!a.once || !e) && n(b, c)); return this }, fire: function () { o.fireWith(this, arguments); return this }, fired: function () { return !!e } }; return o }; var i = [].slice; f.extend({ Deferred: function (a) { var b = f.Callbacks("once memory"), c = f.Callbacks("once memory"), d = f.Callbacks("memory"), e = "pending", g = { resolve: b, reject: c, notify: d }, h = { done: b.add, fail: c.add, progress: d.add, state: function () { return e }, isResolved: b.fired, isRejected: c.fired, then: function (a, b, c) { i.done(a).fail(b).progress(c); return this }, always: function () { return i.done.apply(i, arguments).fail.apply(i, arguments) }, pipe: function (a, b, c) { return f.Deferred(function (d) { f.each({ done: [a, "resolve"], fail: [b, "reject"], progress: [c, "notify"] }, function (a, b) { var c = b[0], e = b[1], g; f.isFunction(c) ? i[a](function () { g = c.apply(this, arguments), g && f.isFunction(g.promise) ? g.promise().then(d.resolve, d.reject, d.notify) : d[e + "With"](this === i ? d : this, [g]) }) : i[a](d[e]) }) }).promise() }, promise: function (a) { if (a == null) a = h; else for (var b in h) a[b] = h[b]; return a } }, i = h.promise({}), j; for (j in g) i[j] = g[j].fire, i[j + "With"] = g[j].fireWith; i.done(function () { e = "resolved" }, c.disable, d.lock).fail(function () { e = "rejected" }, b.disable, d.lock), a && a.call(i, i); return i }, when: function (a) { function m(a) { return function (b) { e[a] = arguments.length > 1 ? i.call(arguments, 0) : b, j.notifyWith(k, e) } } function l(a) { return function (c) { b[a] = arguments.length > 1 ? i.call(arguments, 0) : c, --g || j.resolveWith(j, b) } } var b = i.call(arguments, 0), c = 0, d = b.length, e = Array(d), g = d, h = d, j = d <= 1 && a && f.isFunction(a.promise) ? a : f.Deferred(), k = j.promise(); if (d > 1) { for (; c < d; c++)b[c] && b[c].promise && f.isFunction(b[c].promise) ? b[c].promise().then(l(c), j.reject, m(c)) : --g; g || j.resolveWith(j, b) } else j !== a && j.resolveWith(j, d ? [a] : []); return k } }), f.support = function () { var a = c.createElement("div"), b = c.documentElement, d, e, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; a.setAttribute("className", "t"), a.innerHTML = "
a", d = a.getElementsByTagName("*"), e = a.getElementsByTagName("a")[0]; if (!d || !d.length || !e) return {}; g = c.createElement("select"), h = g.appendChild(c.createElement("option")), i = a.getElementsByTagName("input")[0], k = { leadingWhitespace: a.firstChild.nodeType === 3, tbody: !a.getElementsByTagName("tbody").length, htmlSerialize: !!a.getElementsByTagName("link").length, style: /top/.test(e.getAttribute("style")), hrefNormalized: e.getAttribute("href") === "/a", opacity: /^0.55/.test(e.style.opacity), cssFloat: !!e.style.cssFloat, unknownElems: !!a.getElementsByTagName("nav").length, checkOn: i.value === "on", optSelected: h.selected, getSetAttribute: a.className !== "t", enctype: !!c.createElement("form").enctype, submitBubbles: !0, changeBubbles: !0, focusinBubbles: !1, deleteExpando: !0, noCloneEvent: !0, inlineBlockNeedsLayout: !1, shrinkWrapBlocks: !1, reliableMarginRight: !0 }, i.checked = !0, k.noCloneChecked = i.cloneNode(!0).checked, g.disabled = !0, k.optDisabled = !h.disabled; try { delete a.test } catch (v) { k.deleteExpando = !1 } !a.addEventListener && a.attachEvent && a.fireEvent && (a.attachEvent("onclick", function () { k.noCloneEvent = !1 }), a.cloneNode(!0).fireEvent("onclick")), i = c.createElement("input"), i.value = "t", i.setAttribute("type", "radio"), k.radioValue = i.value === "t", i.setAttribute("checked", "checked"), a.appendChild(i), l = c.createDocumentFragment(), l.appendChild(a.lastChild), k.checkClone = l.cloneNode(!0).cloneNode(!0).lastChild.checked, a.innerHTML = "", a.style.width = a.style.paddingLeft = "1px", m = c.getElementsByTagName("body")[0], o = c.createElement(m ? "div" : "body"), p = { visibility: "hidden", width: 0, height: 0, border: 0, margin: 0, background: "none" }, m && f.extend(p, { position: "absolute", left: "-999px", top: "-999px" }); for (t in p) o.style[t] = p[t]; o.appendChild(a), n = m || b, n.insertBefore(o, n.firstChild), k.appendChecked = i.checked, k.boxModel = a.offsetWidth === 2, "zoom" in a.style && (a.style.display = "inline", a.style.zoom = 1, k.inlineBlockNeedsLayout = a.offsetWidth === 2, a.style.display = "", a.innerHTML = "
", k.shrinkWrapBlocks = a.offsetWidth !== 2), a.innerHTML = "
t
", q = a.getElementsByTagName("td"), u = q[0].offsetHeight === 0, q[0].style.display = "", q[1].style.display = "none", k.reliableHiddenOffsets = u && q[0].offsetHeight === 0, a.innerHTML = "", c.defaultView && c.defaultView.getComputedStyle && (j = c.createElement("div"), j.style.width = "0", j.style.marginRight = "0", a.appendChild(j), k.reliableMarginRight = (parseInt((c.defaultView.getComputedStyle(j, null) || { marginRight: 0 }).marginRight, 10) || 0) === 0); if (a.attachEvent) for (t in { submit: 1, change: 1, focusin: 1 }) s = "on" + t, u = s in a, u || (a.setAttribute(s, "return;"), u = typeof a[s] == "function"), k[t + "Bubbles"] = u; f(function () { var a, b, d, e, g, h, i = 1, j = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;", l = "visibility:hidden;border:0;", n = "style='" + j + "border:5px solid #000;padding:0;'", p = "
" + "" + "
"; m = c.getElementsByTagName("body")[0]; !m || (a = c.createElement("div"), a.style.cssText = l + "width:0;height:0;position:static;top:0;margin-top:" + i + "px", m.insertBefore(a, m.firstChild), o = c.createElement("div"), o.style.cssText = j + l, o.innerHTML = p, a.appendChild(o), b = o.firstChild, d = b.firstChild, g = b.nextSibling.firstChild.firstChild, h = { doesNotAddBorder: d.offsetTop !== 5, doesAddBorderForTableAndCells: g.offsetTop === 5 }, d.style.position = "fixed", d.style.top = "20px", h.fixedPosition = d.offsetTop === 20 || d.offsetTop === 15, d.style.position = d.style.top = "", b.style.overflow = "hidden", b.style.position = "relative", h.subtractsBorderForOverflowNotVisible = d.offsetTop === -5, h.doesNotIncludeMarginInBodyOffset = m.offsetTop !== i, m.removeChild(a), o = a = null, f.extend(k, h)) }), o.innerHTML = "", n.removeChild(o), o = l = g = h = m = j = a = i = null; return k }(), f.boxModel = f.support.boxModel; var j = /^(?:\{.*\}|\[.*\])$/, k = /([A-Z])/g; f.extend({ cache: {}, uuid: 0, expando: "jQuery" + (f.fn.jquery + Math.random()).replace(/\D/g, ""), noData: { embed: !0, object: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", applet: !0 }, hasData: function (a) { a = a.nodeType ? f.cache[a[f.expando]] : a[f.expando]; return !!a && !m(a) }, data: function (a, c, d, e) { if (!!f.acceptData(a)) { var g, h, i, j = f.expando, k = typeof c == "string", l = a.nodeType, m = l ? f.cache : a, n = l ? a[f.expando] : a[f.expando] && f.expando, o = c === "events"; if ((!n || !m[n] || !o && !e && !m[n].data) && k && d === b) return; n || (l ? a[f.expando] = n = ++f.uuid : n = f.expando), m[n] || (m[n] = {}, l || (m[n].toJSON = f.noop)); if (typeof c == "object" || typeof c == "function") e ? m[n] = f.extend(m[n], c) : m[n].data = f.extend(m[n].data, c); g = h = m[n], e || (h.data || (h.data = {}), h = h.data), d !== b && (h[f.camelCase(c)] = d); if (o && !h[c]) return g.events; k ? (i = h[c], i == null && (i = h[f.camelCase(c)])) : i = h; return i } }, removeData: function (a, b, c) { if (!!f.acceptData(a)) { var d, e, g, h = f.expando, i = a.nodeType, j = i ? f.cache : a, k = i ? a[f.expando] : f.expando; if (!j[k]) return; if (b) { d = c ? j[k] : j[k].data; if (d) { f.isArray(b) ? b = b : b in d ? b = [b] : (b = f.camelCase(b), b in d ? b = [b] : b = b.split(" ")); for (e = 0, g = b.length; e < g; e++)delete d[b[e]]; if (!(c ? m : f.isEmptyObject)(d)) return } } if (!c) { delete j[k].data; if (!m(j[k])) return } f.support.deleteExpando || !j.setInterval ? delete j[k] : j[k] = null, i && (f.support.deleteExpando ? delete a[f.expando] : a.removeAttribute ? a.removeAttribute(f.expando) : a[f.expando] = null) } }, _data: function (a, b, c) { return f.data(a, b, c, !0) }, acceptData: function (a) { if (a.nodeName) { var b = f.noData[a.nodeName.toLowerCase()]; if (b) return b !== !0 && a.getAttribute("classid") === b } return !0 } }), f.fn.extend({ data: function (a, c) { var d, e, g, h = null; if (typeof a == "undefined") { if (this.length) { h = f.data(this[0]); if (this[0].nodeType === 1 && !f._data(this[0], "parsedAttrs")) { e = this[0].attributes; for (var i = 0, j = e.length; i < j; i++)g = e[i].name, g.indexOf("data-") === 0 && (g = f.camelCase(g.substring(5)), l(this[0], g, h[g])); f._data(this[0], "parsedAttrs", !0) } } return h } if (typeof a == "object") return this.each(function () { f.data(this, a) }); d = a.split("."), d[1] = d[1] ? "." + d[1] : ""; if (c === b) { h = this.triggerHandler("getData" + d[1] + "!", [d[0]]), h === b && this.length && (h = f.data(this[0], a), h = l(this[0], a, h)); return h === b && d[1] ? this.data(d[0]) : h } return this.each(function () { var b = f(this), e = [d[0], c]; b.triggerHandler("setData" + d[1] + "!", e), f.data(this, a, c), b.triggerHandler("changeData" + d[1] + "!", e) }) }, removeData: function (a) { return this.each(function () { f.removeData(this, a) }) } }), f.extend({ _mark: function (a, b) { a && (b = (b || "fx") + "mark", f._data(a, b, (f._data(a, b) || 0) + 1)) }, _unmark: function (a, b, c) { a !== !0 && (c = b, b = a, a = !1); if (b) { c = c || "fx"; var d = c + "mark", e = a ? 0 : (f._data(b, d) || 1) - 1; e ? f._data(b, d, e) : (f.removeData(b, d, !0), n(b, c, "mark")) } }, queue: function (a, b, c) { var d; if (a) { b = (b || "fx") + "queue", d = f._data(a, b), c && (!d || f.isArray(c) ? d = f._data(a, b, f.makeArray(c)) : d.push(c)); return d || [] } }, dequeue: function (a, b) { b = b || "fx"; var c = f.queue(a, b), d = c.shift(), e = {}; d === "inprogress" && (d = c.shift()), d && (b === "fx" && c.unshift("inprogress"), f._data(a, b + ".run", e), d.call(a, function () { f.dequeue(a, b) }, e)), c.length || (f.removeData(a, b + "queue " + b + ".run", !0), n(a, b, "queue")) } }), f.fn.extend({ queue: function (a, c) { typeof a != "string" && (c = a, a = "fx"); if (c === b) return f.queue(this[0], a); return this.each(function () { var b = f.queue(this, a, c); a === "fx" && b[0] !== "inprogress" && f.dequeue(this, a) }) }, dequeue: function (a) { return this.each(function () { f.dequeue(this, a) }) }, delay: function (a, b) { a = f.fx ? f.fx.speeds[a] || a : a, b = b || "fx"; return this.queue(b, function (b, c) { var d = setTimeout(b, a); c.stop = function () { clearTimeout(d) } }) }, clearQueue: function (a) { return this.queue(a || "fx", []) }, promise: function (a, c) { function m() { --h || d.resolveWith(e, [e]) } typeof a != "string" && (c = a, a = b), a = a || "fx"; var d = f.Deferred(), e = this, g = e.length, h = 1, i = a + "defer", j = a + "queue", k = a + "mark", l; while (g--) if (l = f.data(e[g], i, b, !0) || (f.data(e[g], j, b, !0) || f.data(e[g], k, b, !0)) && f.data(e[g], i, f.Callbacks("once memory"), !0)) h++, l.add(m); m(); return d.promise() } }); var o = /[\n\t\r]/g, p = /\s+/, q = /\r/g, r = /^(?:button|input)$/i, s = /^(?:button|input|object|select|textarea)$/i, t = /^a(?:rea)?$/i, u = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, v = f.support.getSetAttribute, w, x, y; f.fn.extend({ attr: function (a, b) { return f.access(this, a, b, !0, f.attr) }, removeAttr: function (a) { return this.each(function () { f.removeAttr(this, a) }) }, prop: function (a, b) { return f.access(this, a, b, !0, f.prop) }, removeProp: function (a) { a = f.propFix[a] || a; return this.each(function () { try { this[a] = b, delete this[a] } catch (c) { } }) }, addClass: function (a) { var b, c, d, e, g, h, i; if (f.isFunction(a)) return this.each(function (b) { f(this).addClass(a.call(this, b, this.className)) }); if (a && typeof a == "string") { b = a.split(p); for (c = 0, d = this.length; c < d; c++) { e = this[c]; if (e.nodeType === 1) if (!e.className && b.length === 1) e.className = a; else { g = " " + e.className + " "; for (h = 0, i = b.length; h < i; h++)~g.indexOf(" " + b[h] + " ") || (g += b[h] + " "); e.className = f.trim(g) } } } return this }, removeClass: function (a) { var c, d, e, g, h, i, j; if (f.isFunction(a)) return this.each(function (b) { f(this).removeClass(a.call(this, b, this.className)) }); if (a && typeof a == "string" || a === b) { c = (a || "").split(p); for (d = 0, e = this.length; d < e; d++) { g = this[d]; if (g.nodeType === 1 && g.className) if (a) { h = (" " + g.className + " ").replace(o, " "); for (i = 0, j = c.length; i < j; i++)h = h.replace(" " + c[i] + " ", " "); g.className = f.trim(h) } else g.className = "" } } return this }, toggleClass: function (a, b) { var c = typeof a, d = typeof b == "boolean"; if (f.isFunction(a)) return this.each(function (c) { f(this).toggleClass(a.call(this, c, this.className, b), b) }); return this.each(function () { if (c === "string") { var e, g = 0, h = f(this), i = b, j = a.split(p); while (e = j[g++]) i = d ? i : !h.hasClass(e), h[i ? "addClass" : "removeClass"](e) } else if (c === "undefined" || c === "boolean") this.className && f._data(this, "__className__", this.className), this.className = this.className || a === !1 ? "" : f._data(this, "__className__") || "" }) }, hasClass: function (a) { var b = " " + a + " ", c = 0, d = this.length; for (; c < d; c++)if (this[c].nodeType === 1 && (" " + this[c].className + " ").replace(o, " ").indexOf(b) > -1) return !0; return !1 }, val: function (a) { var c, d, e, g = this[0]; if (!arguments.length) { if (g) { c = f.valHooks[g.nodeName.toLowerCase()] || f.valHooks[g.type]; if (c && "get" in c && (d = c.get(g, "value")) !== b) return d; d = g.value; return typeof d == "string" ? d.replace(q, "") : d == null ? "" : d } return b } e = f.isFunction(a); return this.each(function (d) { var g = f(this), h; if (this.nodeType === 1) { e ? h = a.call(this, d, g.val()) : h = a, h == null ? h = "" : typeof h == "number" ? h += "" : f.isArray(h) && (h = f.map(h, function (a) { return a == null ? "" : a + "" })), c = f.valHooks[this.nodeName.toLowerCase()] || f.valHooks[this.type]; if (!c || !("set" in c) || c.set(this, h, "value") === b) this.value = h } }) } }), f.extend({ valHooks: { option: { get: function (a) { var b = a.attributes.value; return !b || b.specified ? a.value : a.text } }, select: { get: function (a) { var b, c, d, e, g = a.selectedIndex, h = [], i = a.options, j = a.type === "select-one"; if (g < 0) return null; c = j ? g : 0, d = j ? g + 1 : i.length; for (; c < d; c++) { e = i[c]; if (e.selected && (f.support.optDisabled ? !e.disabled : e.getAttribute("disabled") === null) && (!e.parentNode.disabled || !f.nodeName(e.parentNode, "optgroup"))) { b = f(e).val(); if (j) return b; h.push(b) } } if (j && !h.length && i.length) return f(i[g]).val(); return h }, set: function (a, b) { var c = f.makeArray(b); f(a).find("option").each(function () { this.selected = f.inArray(f(this).val(), c) >= 0 }), c.length || (a.selectedIndex = -1); return c } } }, attrFn: { val: !0, css: !0, html: !0, text: !0, data: !0, width: !0, height: !0, offset: !0 }, attr: function (a, c, d, e) { var g, h, i, j = a.nodeType; if (!a || j === 3 || j === 8 || j === 2) return b; if (e && c in f.attrFn) return f(a)[c](d); if (!("getAttribute" in a)) return f.prop(a, c, d); i = j !== 1 || !f.isXMLDoc(a), i && (c = c.toLowerCase(), h = f.attrHooks[c] || (u.test(c) ? x : w)); if (d !== b) { if (d === null) { f.removeAttr(a, c); return b } if (h && "set" in h && i && (g = h.set(a, d, c)) !== b) return g; a.setAttribute(c, "" + d); return d } if (h && "get" in h && i && (g = h.get(a, c)) !== null) return g; g = a.getAttribute(c); return g === null ? b : g }, removeAttr: function (a, b) { var c, d, e, g, h = 0; if (a.nodeType === 1) { d = (b || "").split(p), g = d.length; for (; h < g; h++)e = d[h].toLowerCase(), c = f.propFix[e] || e, f.attr(a, e, ""), a.removeAttribute(v ? e : c), u.test(e) && c in a && (a[c] = !1) } }, attrHooks: { type: { set: function (a, b) { if (r.test(a.nodeName) && a.parentNode) f.error("type property can't be changed"); else if (!f.support.radioValue && b === "radio" && f.nodeName(a, "input")) { var c = a.value; a.setAttribute("type", b), c && (a.value = c); return b } } }, value: { get: function (a, b) { if (w && f.nodeName(a, "button")) return w.get(a, b); return b in a ? a.value : null }, set: function (a, b, c) { if (w && f.nodeName(a, "button")) return w.set(a, b, c); a.value = b } } }, propFix: { tabindex: "tabIndex", readonly: "readOnly", "for": "htmlFor", "class": "className", maxlength: "maxLength", cellspacing: "cellSpacing", cellpadding: "cellPadding", rowspan: "rowSpan", colspan: "colSpan", usemap: "useMap", frameborder: "frameBorder", contenteditable: "contentEditable" }, prop: function (a, c, d) { var e, g, h, i = a.nodeType; if (!a || i === 3 || i === 8 || i === 2) return b; h = i !== 1 || !f.isXMLDoc(a), h && (c = f.propFix[c] || c, g = f.propHooks[c]); return d !== b ? g && "set" in g && (e = g.set(a, d, c)) !== b ? e : a[c] = d : g && "get" in g && (e = g.get(a, c)) !== null ? e : a[c] }, propHooks: { tabIndex: { get: function (a) { var c = a.getAttributeNode("tabindex"); return c && c.specified ? parseInt(c.value, 10) : s.test(a.nodeName) || t.test(a.nodeName) && a.href ? 0 : b } } } }), f.attrHooks.tabindex = f.propHooks.tabIndex, x = { get: function (a, c) { var d, e = f.prop(a, c); return e === !0 || typeof e != "boolean" && (d = a.getAttributeNode(c)) && d.nodeValue !== !1 ? c.toLowerCase() : b }, set: function (a, b, c) { var d; b === !1 ? f.removeAttr(a, c) : (d = f.propFix[c] || c, d in a && (a[d] = !0), a.setAttribute(c, c.toLowerCase())); return c } }, v || (y = { name: !0, id: !0 }, w = f.valHooks.button = { get: function (a, c) { var d; d = a.getAttributeNode(c); return d && (y[c] ? d.nodeValue !== "" : d.specified) ? d.nodeValue : b }, set: function (a, b, d) { var e = a.getAttributeNode(d); e || (e = c.createAttribute(d), a.setAttributeNode(e)); return e.nodeValue = b + "" } }, f.attrHooks.tabindex.set = w.set, f.each(["width", "height"], function (a, b) { f.attrHooks[b] = f.extend(f.attrHooks[b], { set: function (a, c) { if (c === "") { a.setAttribute(b, "auto"); return c } } }) }), f.attrHooks.contenteditable = { get: w.get, set: function (a, b, c) { b === "" && (b = "false"), w.set(a, b, c) } }), f.support.hrefNormalized || f.each(["href", "src", "width", "height"], function (a, c) { f.attrHooks[c] = f.extend(f.attrHooks[c], { get: function (a) { var d = a.getAttribute(c, 2); return d === null ? b : d } }) }), f.support.style || (f.attrHooks.style = { get: function (a) { return a.style.cssText.toLowerCase() || b }, set: function (a, b) { return a.style.cssText = "" + b } }), f.support.optSelected || (f.propHooks.selected = f.extend(f.propHooks.selected, { get: function (a) { var b = a.parentNode; b && (b.selectedIndex, b.parentNode && b.parentNode.selectedIndex); return null } })), f.support.enctype || (f.propFix.enctype = "encoding"), f.support.checkOn || f.each(["radio", "checkbox"], function () { f.valHooks[this] = { get: function (a) { return a.getAttribute("value") === null ? "on" : a.value } } }), f.each(["radio", "checkbox"], function () { f.valHooks[this] = f.extend(f.valHooks[this], { set: function (a, b) { if (f.isArray(b)) return a.checked = f.inArray(f(a).val(), b) >= 0 } }) }); var z = /\.(.*)$/, A = /^(?:textarea|input|select)$/i, B = /\./g, C = / /g, D = /[^\w\s.|`]/g, E = /^([^\.]*)?(?:\.(.+))?$/, F = /\bhover(\.\S+)?/, G = /^key/, H = /^(?:mouse|contextmenu)|click/, I = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, J = function (a) { + var b = I.exec(a); b && + (b[1] = (b[1] || "").toLowerCase(), b[3] = b[3] && new RegExp("(?:^|\\s)" + b[3] + "(?:\\s|$)")); return b + }, K = function (a, b) { return (!b[1] || a.nodeName.toLowerCase() === b[1]) && (!b[2] || a.id === b[2]) && (!b[3] || b[3].test(a.className)) }, L = function (a) { return f.event.special.hover ? a : a.replace(F, "mouseenter$1 mouseleave$1") }; f.event = { add: function (a, c, d, e, g) { var h, i, j, k, l, m, n, o, p, q, r, s; if (!(a.nodeType === 3 || a.nodeType === 8 || !c || !d || !(h = f._data(a)))) { d.handler && (p = d, d = p.handler), d.guid || (d.guid = f.guid++), j = h.events, j || (h.events = j = {}), i = h.handle, i || (h.handle = i = function (a) { return typeof f != "undefined" && (!a || f.event.triggered !== a.type) ? f.event.dispatch.apply(i.elem, arguments) : b }, i.elem = a), c = L(c).split(" "); for (k = 0; k < c.length; k++) { l = E.exec(c[k]) || [], m = l[1], n = (l[2] || "").split(".").sort(), s = f.event.special[m] || {}, m = (g ? s.delegateType : s.bindType) || m, s = f.event.special[m] || {}, o = f.extend({ type: m, origType: l[1], data: e, handler: d, guid: d.guid, selector: g, namespace: n.join(".") }, p), g && (o.quick = J(g), !o.quick && f.expr.match.POS.test(g) && (o.isPositional = !0)), r = j[m]; if (!r) { r = j[m] = [], r.delegateCount = 0; if (!s.setup || s.setup.call(a, e, n, i) === !1) a.addEventListener ? a.addEventListener(m, i, !1) : a.attachEvent && a.attachEvent("on" + m, i) } s.add && (s.add.call(a, o), o.handler.guid || (o.handler.guid = d.guid)), g ? r.splice(r.delegateCount++, 0, o) : r.push(o), f.event.global[m] = !0 } a = null } }, global: {}, remove: function (a, b, c, d) { var e = f.hasData(a) && f._data(a), g, h, i, j, k, l, m, n, o, p, q; if (!!e && !!(m = e.events)) { b = L(b || "").split(" "); for (g = 0; g < b.length; g++) { h = E.exec(b[g]) || [], i = h[1], j = h[2]; if (!i) { j = j ? "." + j : ""; for (l in m) f.event.remove(a, l + j, c, d); return } n = f.event.special[i] || {}, i = (d ? n.delegateType : n.bindType) || i, p = m[i] || [], k = p.length, j = j ? new RegExp("(^|\\.)" + j.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; if (c || j || d || n.remove) for (l = 0; l < p.length; l++) { q = p[l]; if (!c || c.guid === q.guid) if (!j || j.test(q.namespace)) if (!d || d === q.selector || d === "**" && q.selector) p.splice(l--, 1), q.selector && p.delegateCount--, n.remove && n.remove.call(a, q) } else p.length = 0; p.length === 0 && k !== p.length && ((!n.teardown || n.teardown.call(a, j) === !1) && f.removeEvent(a, i, e.handle), delete m[i]) } f.isEmptyObject(m) && (o = e.handle, o && (o.elem = null), f.removeData(a, ["events", "handle"], !0)) } }, customEvent: { getData: !0, setData: !0, changeData: !0 }, trigger: function (c, d, e, g) { if (!e || e.nodeType !== 3 && e.nodeType !== 8) { var h = c.type || c, i = [], j, k, l, m, n, o, p, q, r, s; h.indexOf("!") >= 0 && (h = h.slice(0, -1), k = !0), h.indexOf(".") >= 0 && (i = h.split("."), h = i.shift(), i.sort()); if ((!e || f.event.customEvent[h]) && !f.event.global[h]) return; c = typeof c == "object" ? c[f.expando] ? c : new f.Event(h, c) : new f.Event(h), c.type = h, c.isTrigger = !0, c.exclusive = k, c.namespace = i.join("."), c.namespace_re = c.namespace ? new RegExp("(^|\\.)" + i.join("\\.(?:.*\\.)?") + "(\\.|$)") : null, o = h.indexOf(":") < 0 ? "on" + h : "", (g || !e) && c.preventDefault(); if (!e) { j = f.cache; for (l in j) j[l].events && j[l].events[h] && f.event.trigger(c, d, j[l].handle.elem, !0); return } c.result = b, c.target || (c.target = e), d = d != null ? f.makeArray(d) : [], d.unshift(c), p = f.event.special[h] || {}; if (p.trigger && p.trigger.apply(e, d) === !1) return; r = [[e, p.bindType || h]]; if (!g && !p.noBubble && !f.isWindow(e)) { s = p.delegateType || h, n = null; for (m = e.parentNode; m; m = m.parentNode)r.push([m, s]), n = m; n && n === e.ownerDocument && r.push([n.defaultView || n.parentWindow || a, s]) } for (l = 0; l < r.length; l++) { m = r[l][0], c.type = r[l][1], q = (f._data(m, "events") || {})[c.type] && f._data(m, "handle"), q && q.apply(m, d), q = o && m[o], q && f.acceptData(m) && q.apply(m, d); if (c.isPropagationStopped()) break } c.type = h, c.isDefaultPrevented() || (!p._default || p._default.apply(e.ownerDocument, d) === !1) && (h !== "click" || !f.nodeName(e, "a")) && f.acceptData(e) && o && e[h] && (h !== "focus" && h !== "blur" || c.target.offsetWidth !== 0) && !f.isWindow(e) && (n = e[o], n && (e[o] = null), f.event.triggered = h, e[h](), f.event.triggered = b, n && (e[o] = n)); return c.result } }, dispatch: function (c) { c = f.event.fix(c || a.event); var d = (f._data(this, "events") || {})[c.type] || [], e = d.delegateCount, g = [].slice.call(arguments, 0), h = !c.exclusive && !c.namespace, i = (f.event.special[c.type] || {}).handle, j = [], k, l, m, n, o, p, q, r, s, t, u; g[0] = c, c.delegateTarget = this; if (e && !c.target.disabled && (!c.button || c.type !== "click")) for (m = c.target; m != this; m = m.parentNode || this) { o = {}, q = []; for (k = 0; k < e; k++)r = d[k], s = r.selector, t = o[s], r.isPositional ? t = (t || (o[s] = f(s))).index(m) >= 0 : t === b && (t = o[s] = r.quick ? K(m, r.quick) : f(m).is(s)), t && q.push(r); q.length && j.push({ elem: m, matches: q }) } d.length > e && j.push({ elem: this, matches: d.slice(e) }); for (k = 0; k < j.length && !c.isPropagationStopped(); k++) { p = j[k], c.currentTarget = p.elem; for (l = 0; l < p.matches.length && !c.isImmediatePropagationStopped(); l++) { r = p.matches[l]; if (h || !c.namespace && !r.namespace || c.namespace_re && c.namespace_re.test(r.namespace)) c.data = r.data, c.handleObj = r, n = (i || r.handler).apply(p.elem, g), n !== b && (c.result = n, n === !1 && (c.preventDefault(), c.stopPropagation())) } } return c.result }, props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function (a, b) { a.which == null && (a.which = b.charCode != null ? b.charCode : b.keyCode); return a } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement wheelDelta".split(" "), filter: function (a, d) { var e, f, g, h = d.button, i = d.fromElement; a.pageX == null && d.clientX != null && (e = a.target.ownerDocument || c, f = e.documentElement, g = e.body, a.pageX = d.clientX + (f && f.scrollLeft || g && g.scrollLeft || 0) - (f && f.clientLeft || g && g.clientLeft || 0), a.pageY = d.clientY + (f && f.scrollTop || g && g.scrollTop || 0) - (f && f.clientTop || g && g.clientTop || 0)), !a.relatedTarget && i && (a.relatedTarget = i === a.target ? d.toElement : i), !a.which && h !== b && (a.which = h & 1 ? 1 : h & 2 ? 3 : h & 4 ? 2 : 0); return a } }, fix: function (a) { if (a[f.expando]) return a; var d, e, g = a, h = f.event.fixHooks[a.type] || {}, i = h.props ? this.props.concat(h.props) : this.props; a = f.Event(g); for (d = i.length; d;)e = i[--d], a[e] = g[e]; a.target || (a.target = g.srcElement || c), a.target.nodeType === 3 && (a.target = a.target.parentNode), a.metaKey === b && (a.metaKey = a.ctrlKey); return h.filter ? h.filter(a, g) : a }, special: { ready: { setup: f.bindReady }, focus: { delegateType: "focusin", noBubble: !0 }, blur: { delegateType: "focusout", noBubble: !0 }, beforeunload: { setup: function (a, b, c) { f.isWindow(this) && (this.onbeforeunload = c) }, teardown: function (a, b) { this.onbeforeunload === b && (this.onbeforeunload = null) } } }, simulate: function (a, b, c, d) { var e = f.extend(new f.Event, c, { type: a, isSimulated: !0, originalEvent: {} }); d ? f.event.trigger(e, null, b) : f.event.dispatch.call(b, e), e.isDefaultPrevented() && c.preventDefault() } }, f.event.handle = f.event.dispatch, f.removeEvent = c.removeEventListener ? function (a, b, c) { a.removeEventListener && a.removeEventListener(b, c, !1) } : function (a, b, c) { a.detachEvent && a.detachEvent("on" + b, c) }, f.Event = function (a, b) { if (!(this instanceof f.Event)) return new f.Event(a, b); a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || a.returnValue === !1 || a.getPreventDefault && a.getPreventDefault() ? N : M) : this.type = a, b && f.extend(this, b), this.timeStamp = a && a.timeStamp || f.now(), this[f.expando] = !0 }, f.Event.prototype = { preventDefault: function () { this.isDefaultPrevented = N; var a = this.originalEvent; !a || (a.preventDefault ? a.preventDefault() : a.returnValue = !1) }, stopPropagation: function () { this.isPropagationStopped = N; var a = this.originalEvent; !a || (a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0) }, stopImmediatePropagation: function () { this.isImmediatePropagationStopped = N, this.stopPropagation() }, isDefaultPrevented: M, isPropagationStopped: M, isImmediatePropagationStopped: M }, f.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function (a, b) { f.event.special[a] = f.event.special[b] = { delegateType: b, bindType: b, handle: function (a) { var b = this, c = a.relatedTarget, d = a.handleObj, e = d.selector, g, h; if (!c || d.origType === a.type || c !== b && !f.contains(b, c)) g = a.type, a.type = d.origType, h = d.handler.apply(this, arguments), a.type = g; return h } } }), f.support.submitBubbles || (f.event.special.submit = { setup: function () { if (f.nodeName(this, "form")) return !1; f.event.add(this, "click._submit keypress._submit", function (a) { var c = a.target, d = f.nodeName(c, "input") || f.nodeName(c, "button") ? c.form : b; d && !d._submit_attached && (f.event.add(d, "submit._submit", function (a) { this.parentNode && f.event.simulate("submit", this.parentNode, a, !0) }), d._submit_attached = !0) }) }, teardown: function () { if (f.nodeName(this, "form")) return !1; f.event.remove(this, "._submit") } }), f.support.changeBubbles || (f.event.special.change = { setup: function () { if (A.test(this.nodeName)) { if (this.type === "checkbox" || this.type === "radio") f.event.add(this, "propertychange._change", function (a) { a.originalEvent.propertyName === "checked" && (this._just_changed = !0) }), f.event.add(this, "click._change", function (a) { this._just_changed && (this._just_changed = !1, f.event.simulate("change", this, a, !0)) }); return !1 } f.event.add(this, "beforeactivate._change", function (a) { var b = a.target; A.test(b.nodeName) && !b._change_attached && (f.event.add(b, "change._change", function (a) { this.parentNode && !a.isSimulated && f.event.simulate("change", this.parentNode, a, !0) }), b._change_attached = !0) }) }, handle: function (a) { var b = a.target; if (this !== b || a.isSimulated || a.isTrigger || b.type !== "radio" && b.type !== "checkbox") return a.handleObj.handler.apply(this, arguments) }, teardown: function () { f.event.remove(this, "._change"); return A.test(this.nodeName) } }), f.support.focusinBubbles || f.each({ focus: "focusin", blur: "focusout" }, function (a, b) { var d = 0, e = function (a) { f.event.simulate(b, a.target, f.event.fix(a), !0) }; f.event.special[b] = { setup: function () { d++ === 0 && c.addEventListener(a, e, !0) }, teardown: function () { --d === 0 && c.removeEventListener(a, e, !0) } } }), f.fn.extend({ on: function (a, c, d, e, g) { var h, i; if (typeof a == "object") { typeof c != "string" && (d = c, c = b); for (i in a) this.on(i, c, d, a[i], g); return this } d == null && e == null ? (e = c, d = c = b) : e == null && (typeof c == "string" ? (e = d, d = b) : (e = d, d = c, c = b)); if (e === !1) e = M; else if (!e) return this; g === 1 && (h = e, e = function (a) { f().off(a); return h.apply(this, arguments) }, e.guid = h.guid || (h.guid = f.guid++)); return this.each(function () { f.event.add(this, a, e, d, c) }) }, one: function (a, b, c, d) { return this.on.call(this, a, b, c, d, 1) }, off: function (a, c, d) { if (a && a.preventDefault && a.handleObj) { var e = a.handleObj; f(a.delegateTarget).off(e.namespace ? e.type + "." + e.namespace : e.type, e.selector, e.handler); return this } if (typeof a == "object") { for (var g in a) this.off(g, c, a[g]); return this } if (c === !1 || typeof c == "function") d = c, c = b; d === !1 && (d = M); return this.each(function () { f.event.remove(this, a, d, c) }) }, bind: function (a, b, c) { return this.on(a, null, b, c) }, unbind: function (a, b) { return this.off(a, null, b) }, live: function (a, b, c) { f(this.context).on(a, this.selector, b, c); return this }, die: function (a, b) { f(this.context).off(a, this.selector || "**", b); return this }, delegate: function (a, b, c, d) { return this.on(b, a, c, d) }, undelegate: function (a, b, c) { return arguments.length == 1 ? this.off(a, "**") : this.off(b, a, c) }, trigger: function (a, b) { return this.each(function () { f.event.trigger(a, b, this) }) }, triggerHandler: function (a, b) { if (this[0]) return f.event.trigger(a, b, this[0], !0) }, toggle: function (a) { var b = arguments, c = a.guid || f.guid++, d = 0, e = function (c) { var e = (f._data(this, "lastToggle" + a.guid) || 0) % d; f._data(this, "lastToggle" + a.guid, e + 1), c.preventDefault(); return b[e].apply(this, arguments) || !1 }; e.guid = c; while (d < b.length) b[d++].guid = c; return this.click(e) }, hover: function (a, b) { return this.mouseenter(a).mouseleave(b || a) } }), f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "), function (a, b) { f.fn[b] = function (a, c) { c == null && (c = a, a = null); return arguments.length > 0 ? this.bind(b, a, c) : this.trigger(b) }, f.attrFn && (f.attrFn[b] = !0), G.test(b) && (f.event.fixHooks[b] = f.event.keyHooks), H.test(b) && (f.event.fixHooks[b] = f.event.mouseHooks) }), function () { function x(a, b, c, e, f, g) { for (var h = 0, i = e.length; h < i; h++) { var j = e[h]; if (j) { var k = !1; j = j[a]; while (j) { if (j[d] === c) { k = e[j.sizset]; break } if (j.nodeType === 1) { g || (j[d] = c, j.sizset = h); if (typeof b != "string") { if (j === b) { k = !0; break } } else if (m.filter(b, [j]).length > 0) { k = j; break } } j = j[a] } e[h] = k } } } function w(a, b, c, e, f, g) { for (var h = 0, i = e.length; h < i; h++) { var j = e[h]; if (j) { var k = !1; j = j[a]; while (j) { if (j[d] === c) { k = e[j.sizset]; break } j.nodeType === 1 && !g && (j[d] = c, j.sizset = h); if (j.nodeName.toLowerCase() === b) { k = j; break } j = j[a] } e[h] = k } } } var a = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, d = "sizcache" + (Math.random() + "").replace(".", ""), e = 0, g = Object.prototype.toString, h = !1, i = !0, j = /\\/g, k = /\r\n/g, l = /\W/;[0, 0].sort(function () { i = !1; return 0 }); var m = function (b, d, e, f) { e = e || [], d = d || c; var h = d; if (d.nodeType !== 1 && d.nodeType !== 9) return []; if (!b || typeof b != "string") return e; var i, j, k, l, n, q, r, t, u = !0, v = m.isXML(d), w = [], x = b; do { a.exec(""), i = a.exec(x); if (i) { x = i[3], w.push(i[1]); if (i[2]) { l = i[3]; break } } } while (i); if (w.length > 1 && p.exec(b)) if (w.length === 2 && o.relative[w[0]]) j = y(w[0] + w[1], d, f); else { j = o.relative[w[0]] ? [d] : m(w.shift(), d); while (w.length) b = w.shift(), o.relative[b] && (b += w.shift()), j = y(b, j, f) } else { !f && w.length > 1 && d.nodeType === 9 && !v && o.match.ID.test(w[0]) && !o.match.ID.test(w[w.length - 1]) && (n = m.find(w.shift(), d, v), d = n.expr ? m.filter(n.expr, n.set)[0] : n.set[0]); if (d) { n = f ? { expr: w.pop(), set: s(f) } : m.find(w.pop(), w.length === 1 && (w[0] === "~" || w[0] === "+") && d.parentNode ? d.parentNode : d, v), j = n.expr ? m.filter(n.expr, n.set) : n.set, w.length > 0 ? k = s(j) : u = !1; while (w.length) q = w.pop(), r = q, o.relative[q] ? r = w.pop() : q = "", r == null && (r = d), o.relative[q](k, r, v) } else k = w = [] } k || (k = j), k || m.error(q || b); if (g.call(k) === "[object Array]") if (!u) e.push.apply(e, k); else if (d && d.nodeType === 1) for (t = 0; k[t] != null; t++)k[t] && (k[t] === !0 || k[t].nodeType === 1 && m.contains(d, k[t])) && e.push(j[t]); else for (t = 0; k[t] != null; t++)k[t] && k[t].nodeType === 1 && e.push(j[t]); else s(k, e); l && (m(l, h, e, f), m.uniqueSort(e)); return e }; m.uniqueSort = function (a) { if (u) { h = i, a.sort(u); if (h) for (var b = 1; b < a.length; b++)a[b] === a[b - 1] && a.splice(b--, 1) } return a }, m.matches = function (a, b) { return m(a, null, null, b) }, m.matchesSelector = function (a, b) { return m(b, null, null, [a]).length > 0 }, m.find = function (a, b, c) { var d, e, f, g, h, i; if (!a) return []; for (e = 0, f = o.order.length; e < f; e++) { h = o.order[e]; if (g = o.leftMatch[h].exec(a)) { i = g[1], g.splice(1, 1); if (i.substr(i.length - 1) !== "\\") { g[1] = (g[1] || "").replace(j, ""), d = o.find[h](g, b, c); if (d != null) { a = a.replace(o.match[h], ""); break } } } } d || (d = typeof b.getElementsByTagName != "undefined" ? b.getElementsByTagName("*") : []); return { set: d, expr: a } }, m.filter = function (a, c, d, e) { var f, g, h, i, j, k, l, n, p, q = a, r = [], s = c, t = c && c[0] && m.isXML(c[0]); while (a && c.length) { for (h in o.filter) if ((f = o.leftMatch[h].exec(a)) != null && f[2]) { k = o.filter[h], l = f[1], g = !1, f.splice(1, 1); if (l.substr(l.length - 1) === "\\") continue; s === r && (r = []); if (o.preFilter[h]) { f = o.preFilter[h](f, s, d, r, e, t); if (!f) g = i = !0; else if (f === !0) continue } if (f) for (n = 0; (j = s[n]) != null; n++)j && (i = k(j, f, n, s), p = e ^ i, d && i != null ? p ? g = !0 : s[n] = !1 : p && (r.push(j), g = !0)); if (i !== b) { d || (s = r), a = a.replace(o.match[h], ""); if (!g) return []; break } } if (a === q) if (g == null) m.error(a); else break; q = a } return s }, m.error = function (a) { throw "Syntax error, unrecognized expression: " + a }; var n = m.getText = function (a) { var b, c, d = a.nodeType, e = ""; if (d) { if (d === 1) { if (typeof a.textContent == "string") return a.textContent; if (typeof a.innerText == "string") return a.innerText.replace(k, ""); for (a = a.firstChild; a; a = a.nextSibling)e += n(a) } else if (d === 3 || d === 4) return a.nodeValue } else for (b = 0; c = a[b]; b++)c.nodeType !== 8 && (e += n(c)); return e }, o = m.selectors = { order: ["ID", "NAME", "TAG"], match: { ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function (a) { return a.getAttribute("href") }, type: function (a) { return a.getAttribute("type") } }, relative: { "+": function (a, b) { var c = typeof b == "string", d = c && !l.test(b), e = c && !d; d && (b = b.toLowerCase()); for (var f = 0, g = a.length, h; f < g; f++)if (h = a[f]) { while ((h = h.previousSibling) && h.nodeType !== 1); a[f] = e || h && h.nodeName.toLowerCase() === b ? h || !1 : h === b } e && m.filter(b, a, !0) }, ">": function (a, b) { var c, d = typeof b == "string", e = 0, f = a.length; if (d && !l.test(b)) { b = b.toLowerCase(); for (; e < f; e++) { c = a[e]; if (c) { var g = c.parentNode; a[e] = g.nodeName.toLowerCase() === b ? g : !1 } } } else { for (; e < f; e++)c = a[e], c && (a[e] = d ? c.parentNode : c.parentNode === b); d && m.filter(b, a, !0) } }, "": function (a, b, c) { var d, f = e++, g = x; typeof b == "string" && !l.test(b) && (b = b.toLowerCase(), d = b, g = w), g("parentNode", b, f, a, d, c) }, "~": function (a, b, c) { var d, f = e++, g = x; typeof b == "string" && !l.test(b) && (b = b.toLowerCase(), d = b, g = w), g("previousSibling", b, f, a, d, c) } }, find: { ID: function (a, b, c) { if (typeof b.getElementById != "undefined" && !c) { var d = b.getElementById(a[1]); return d && d.parentNode ? [d] : [] } }, NAME: function (a, b) { if (typeof b.getElementsByName != "undefined") { var c = [], d = b.getElementsByName(a[1]); for (var e = 0, f = d.length; e < f; e++)d[e].getAttribute("name") === a[1] && c.push(d[e]); return c.length === 0 ? null : c } }, TAG: function (a, b) { if (typeof b.getElementsByTagName != "undefined") return b.getElementsByTagName(a[1]) } }, preFilter: { CLASS: function (a, b, c, d, e, f) { a = " " + a[1].replace(j, "") + " "; if (f) return a; for (var g = 0, h; (h = b[g]) != null; g++)h && (e ^ (h.className && (" " + h.className + " ").replace(/[\t\n\r]/g, " ").indexOf(a) >= 0) ? c || d.push(h) : c && (b[g] = !1)); return !1 }, ID: function (a) { return a[1].replace(j, "") }, TAG: function (a, b) { return a[1].replace(j, "").toLowerCase() }, CHILD: function (a) { if (a[1] === "nth") { a[2] || m.error(a[0]), a[2] = a[2].replace(/^\+|\s*/g, ""); var b = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2] === "even" && "2n" || a[2] === "odd" && "2n+1" || !/\D/.test(a[2]) && "0n+" + a[2] || a[2]); a[2] = b[1] + (b[2] || 1) - 0, a[3] = b[3] - 0 } else a[2] && m.error(a[0]); a[0] = e++; return a }, ATTR: function (a, b, c, d, e, f) { var g = a[1] = a[1].replace(j, ""); !f && o.attrMap[g] && (a[1] = o.attrMap[g]), a[4] = (a[4] || a[5] || "").replace(j, ""), a[2] === "~=" && (a[4] = " " + a[4] + " "); return a }, PSEUDO: function (b, c, d, e, f) { if (b[1] === "not") if ((a.exec(b[3]) || "").length > 1 || /^\w/.test(b[3])) b[3] = m(b[3], null, null, c); else { var g = m.filter(b[3], c, d, !0 ^ f); d || e.push.apply(e, g); return !1 } else if (o.match.POS.test(b[0]) || o.match.CHILD.test(b[0])) return !0; return b }, POS: function (a) { a.unshift(!0); return a } }, filters: { enabled: function (a) { return a.disabled === !1 && a.type !== "hidden" }, disabled: function (a) { return a.disabled === !0 }, checked: function (a) { return a.checked === !0 }, selected: function (a) { a.parentNode && a.parentNode.selectedIndex; return a.selected === !0 }, parent: function (a) { return !!a.firstChild }, empty: function (a) { return !a.firstChild }, has: function (a, b, c) { return !!m(c[3], a).length }, header: function (a) { return /h\d/i.test(a.nodeName) }, text: function (a) { var b = a.getAttribute("type"), c = a.type; return a.nodeName.toLowerCase() === "input" && "text" === c && (b === c || b === null) }, radio: function (a) { return a.nodeName.toLowerCase() === "input" && "radio" === a.type }, checkbox: function (a) { return a.nodeName.toLowerCase() === "input" && "checkbox" === a.type }, file: function (a) { return a.nodeName.toLowerCase() === "input" && "file" === a.type }, password: function (a) { return a.nodeName.toLowerCase() === "input" && "password" === a.type }, submit: function (a) { var b = a.nodeName.toLowerCase(); return (b === "input" || b === "button") && "submit" === a.type }, image: function (a) { return a.nodeName.toLowerCase() === "input" && "image" === a.type }, reset: function (a) { var b = a.nodeName.toLowerCase(); return (b === "input" || b === "button") && "reset" === a.type }, button: function (a) { var b = a.nodeName.toLowerCase(); return b === "input" && "button" === a.type || b === "button" }, input: function (a) { return /input|select|textarea|button/i.test(a.nodeName) }, focus: function (a) { return a === a.ownerDocument.activeElement } }, setFilters: { first: function (a, b) { return b === 0 }, last: function (a, b, c, d) { return b === d.length - 1 }, even: function (a, b) { return b % 2 === 0 }, odd: function (a, b) { return b % 2 === 1 }, lt: function (a, b, c) { return b < c[3] - 0 }, gt: function (a, b, c) { return b > c[3] - 0 }, nth: function (a, b, c) { return c[3] - 0 === b }, eq: function (a, b, c) { return c[3] - 0 === b } }, filter: { PSEUDO: function (a, b, c, d) { var e = b[1], f = o.filters[e]; if (f) return f(a, c, b, d); if (e === "contains") return (a.textContent || a.innerText || n([a]) || "").indexOf(b[3]) >= 0; if (e === "not") { var g = b[3]; for (var h = 0, i = g.length; h < i; h++)if (g[h] === a) return !1; return !0 } m.error(e) }, CHILD: function (a, b) { var c, e, f, g, h, i, j, k = b[1], l = a; switch (k) { case "only": case "first": while (l = l.previousSibling) if (l.nodeType === 1) return !1; if (k === "first") return !0; l = a; case "last": while (l = l.nextSibling) if (l.nodeType === 1) return !1; return !0; case "nth": c = b[2], e = b[3]; if (c === 1 && e === 0) return !0; f = b[0], g = a.parentNode; if (g && (g[d] !== f || !a.nodeIndex)) { i = 0; for (l = g.firstChild; l; l = l.nextSibling)l.nodeType === 1 && (l.nodeIndex = ++i); g[d] = f } j = a.nodeIndex - e; return c === 0 ? j === 0 : j % c === 0 && j / c >= 0 } }, ID: function (a, b) { return a.nodeType === 1 && a.getAttribute("id") === b }, TAG: function (a, b) { return b === "*" && a.nodeType === 1 || !!a.nodeName && a.nodeName.toLowerCase() === b }, CLASS: function (a, b) { return (" " + (a.className || a.getAttribute("class")) + " ").indexOf(b) > -1 }, ATTR: function (a, b) { var c = b[1], d = m.attr ? m.attr(a, c) : o.attrHandle[c] ? o.attrHandle[c](a) : a[c] != null ? a[c] : a.getAttribute(c), e = d + "", f = b[2], g = b[4]; return d == null ? f === "!=" : !f && m.attr ? d != null : f === "=" ? e === g : f === "*=" ? e.indexOf(g) >= 0 : f === "~=" ? (" " + e + " ").indexOf(g) >= 0 : g ? f === "!=" ? e !== g : f === "^=" ? e.indexOf(g) === 0 : f === "$=" ? e.substr(e.length - g.length) === g : f === "|=" ? e === g || e.substr(0, g.length + 1) === g + "-" : !1 : e && d !== !1 }, POS: function (a, b, c, d) { var e = b[2], f = o.setFilters[e]; if (f) return f(a, c, b, d) } } }, p = o.match.POS, q = function (a, b) { return "\\" + (b - 0 + 1) }; for (var r in o.match) o.match[r] = new RegExp(o.match[r].source + /(?![^\[]*\])(?![^\(]*\))/.source), o.leftMatch[r] = new RegExp(/(^(?:.|\r|\n)*?)/.source + o.match[r].source.replace(/\\(\d+)/g, q)); var s = function (a, b) { a = Array.prototype.slice.call(a, 0); if (b) { b.push.apply(b, a); return b } return a }; try { Array.prototype.slice.call(c.documentElement.childNodes, 0)[0].nodeType } catch (t) { s = function (a, b) { var c = 0, d = b || []; if (g.call(a) === "[object Array]") Array.prototype.push.apply(d, a); else if (typeof a.length == "number") for (var e = a.length; c < e; c++)d.push(a[c]); else for (; a[c]; c++)d.push(a[c]); return d } } var u, v; c.documentElement.compareDocumentPosition ? u = function (a, b) { if (a === b) { h = !0; return 0 } if (!a.compareDocumentPosition || !b.compareDocumentPosition) return a.compareDocumentPosition ? -1 : 1; return a.compareDocumentPosition(b) & 4 ? -1 : 1 } : (u = function (a, b) { if (a === b) { h = !0; return 0 } if (a.sourceIndex && b.sourceIndex) return a.sourceIndex - b.sourceIndex; var c, d, e = [], f = [], g = a.parentNode, i = b.parentNode, j = g; if (g === i) return v(a, b); if (!g) return -1; if (!i) return 1; while (j) e.unshift(j), j = j.parentNode; j = i; while (j) f.unshift(j), j = j.parentNode; c = e.length, d = f.length; for (var k = 0; k < c && k < d; k++)if (e[k] !== f[k]) return v(e[k], f[k]); return k === c ? v(a, f[k], -1) : v(e[k], b, 1) }, v = function (a, b, c) { if (a === b) return c; var d = a.nextSibling; while (d) { if (d === b) return -1; d = d.nextSibling } return 1 }), function () { var a = c.createElement("div"), d = "script" + (new Date).getTime(), e = c.documentElement; a.innerHTML = "", e.insertBefore(a, e.firstChild), c.getElementById(d) && (o.find.ID = function (a, c, d) { if (typeof c.getElementById != "undefined" && !d) { var e = c.getElementById(a[1]); return e ? e.id === a[1] || typeof e.getAttributeNode != "undefined" && e.getAttributeNode("id").nodeValue === a[1] ? [e] : b : [] } }, o.filter.ID = function (a, b) { var c = typeof a.getAttributeNode != "undefined" && a.getAttributeNode("id"); return a.nodeType === 1 && c && c.nodeValue === b }), e.removeChild(a), e = a = null }(), function () { var a = c.createElement("div"); a.appendChild(c.createComment("")), a.getElementsByTagName("*").length > 0 && (o.find.TAG = function (a, b) { var c = b.getElementsByTagName(a[1]); if (a[1] === "*") { var d = []; for (var e = 0; c[e]; e++)c[e].nodeType === 1 && d.push(c[e]); c = d } return c }), a.innerHTML = "", a.firstChild && typeof a.firstChild.getAttribute != "undefined" && a.firstChild.getAttribute("href") !== "#" && (o.attrHandle.href = function (a) { return a.getAttribute("href", 2) }), a = null }(), c.querySelectorAll && function () { var a = m, b = c.createElement("div"), d = "__sizzle__"; b.innerHTML = "

"; if (!b.querySelectorAll || b.querySelectorAll(".TEST").length !== 0) { m = function (b, e, f, g) { e = e || c; if (!g && !m.isXML(e)) { var h = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b); if (h && (e.nodeType === 1 || e.nodeType === 9)) { if (h[1]) return s(e.getElementsByTagName(b), f); if (h[2] && o.find.CLASS && e.getElementsByClassName) return s(e.getElementsByClassName(h[2]), f) } if (e.nodeType === 9) { if (b === "body" && e.body) return s([e.body], f); if (h && h[3]) { var i = e.getElementById(h[3]); if (!i || !i.parentNode) return s([], f); if (i.id === h[3]) return s([i], f) } try { return s(e.querySelectorAll(b), f) } catch (j) { } } else if (e.nodeType === 1 && e.nodeName.toLowerCase() !== "object") { var k = e, l = e.getAttribute("id"), n = l || d, p = e.parentNode, q = /^\s*[+~]/.test(b); l ? n = n.replace(/'/g, "\\$&") : e.setAttribute("id", n), q && p && (e = e.parentNode); try { if (!q || p) return s(e.querySelectorAll("[id='" + n + "'] " + b), f) } catch (r) { } finally { l || k.removeAttribute("id") } } } return a(b, e, f, g) }; for (var e in a) m[e] = a[e]; b = null } }(), function () { var a = c.documentElement, b = a.matchesSelector || a.mozMatchesSelector || a.webkitMatchesSelector || a.msMatchesSelector; if (b) { var d = !b.call(c.createElement("div"), "div"), e = !1; try { b.call(c.documentElement, "[test!='']:sizzle") } catch (f) { e = !0 } m.matchesSelector = function (a, c) { c = c.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); if (!m.isXML(a)) try { if (e || !o.match.PSEUDO.test(c) && !/!=/.test(c)) { var f = b.call(a, c); if (f || !d || a.document && a.document.nodeType !== 11) return f } } catch (g) { } return m(c, null, null, [a]).length > 0 } } }(), function () { var a = c.createElement("div"); a.innerHTML = "
"; if (!!a.getElementsByClassName && a.getElementsByClassName("e").length !== 0) { a.lastChild.className = "e"; if (a.getElementsByClassName("e").length === 1) return; o.order.splice(1, 0, "CLASS"), o.find.CLASS = function (a, b, c) { if (typeof b.getElementsByClassName != "undefined" && !c) return b.getElementsByClassName(a[1]) }, a = null } }(), c.documentElement.contains ? m.contains = function (a, b) { return a !== b && (a.contains ? a.contains(b) : !0) } : c.documentElement.compareDocumentPosition ? m.contains = function (a, b) { return !!(a.compareDocumentPosition(b) & 16) } : m.contains = function () { return !1 }, m.isXML = function (a) { var b = (a ? a.ownerDocument || a : 0).documentElement; return b ? b.nodeName !== "HTML" : !1 }; var y = function (a, b, c) { var d, e = [], f = "", g = b.nodeType ? [b] : b; while (d = o.match.PSEUDO.exec(a)) f += d[0], a = a.replace(o.match.PSEUDO, ""); a = o.relative[a] ? a + "*" : a; for (var h = 0, i = g.length; h < i; h++)m(a, g[h], e, c); return m.filter(f, e) }; m.attr = f.attr, m.selectors.attrMap = {}, f.find = m, f.expr = m.selectors, f.expr[":"] = f.expr.filters, f.unique = m.uniqueSort, f.text = m.getText, f.isXMLDoc = m.isXML, f.contains = m.contains }(); var O = /Until$/, P = /^(?:parents|prevUntil|prevAll)/, Q = /,/, R = /^.[^:#\[\.,]*$/, S = Array.prototype.slice, T = f.expr.match.POS, U = { children: !0, contents: !0, next: !0, prev: !0 }; f.fn.extend({ find: function (a) { var b = this, c, d; if (typeof a != "string") return f(a).filter(function () { for (c = 0, d = b.length; c < d; c++)if (f.contains(b[c], this)) return !0 }); var e = this.pushStack("", "find", a), g, h, i; for (c = 0, d = this.length; c < d; c++) { g = e.length, f.find(a, this[c], e); if (c > 0) for (h = g; h < e.length; h++)for (i = 0; i < g; i++)if (e[i] === e[h]) { e.splice(h--, 1); break } } return e }, has: function (a) { var b = f(a); return this.filter(function () { for (var a = 0, c = b.length; a < c; a++)if (f.contains(this, b[a])) return !0 }) }, not: function (a) { return this.pushStack(W(this, a, !1), "not", a) }, filter: function (a) { return this.pushStack(W(this, a, !0), "filter", a) }, is: function (a) { return !!a && (typeof a == "string" ? T.test(a) ? f(a, this.context).index(this[0]) >= 0 : f.filter(a, this).length > 0 : this.filter(a).length > 0) }, closest: function (a, b) { var c = [], d, e, g = this[0]; if (f.isArray(a)) { var h = 1; while (g && g.ownerDocument && g !== b) { for (d = 0; d < a.length; d++)f(g).is(a[d]) && c.push({ selector: a[d], elem: g, level: h }); g = g.parentNode, h++ } return c } var i = T.test(a) || typeof a != "string" ? f(a, b || this.context) : 0; for (d = 0, e = this.length; d < e; d++) { g = this[d]; while (g) { if (i ? i.index(g) > -1 : f.find.matchesSelector(g, a)) { c.push(g); break } g = g.parentNode; if (!g || !g.ownerDocument || g === b || g.nodeType === 11) break } } c = c.length > 1 ? f.unique(c) : c; return this.pushStack(c, "closest", a) }, index: function (a) { if (!a) return this[0] && this[0].parentNode ? this.prevAll().length : -1; if (typeof a == "string") return f.inArray(this[0], f(a)); return f.inArray(a.jquery ? a[0] : a, this) }, add: function (a, b) { var c = typeof a == "string" ? f(a, b) : f.makeArray(a && a.nodeType ? [a] : a), d = f.merge(this.get(), c); return this.pushStack(V(c[0]) || V(d[0]) ? d : f.unique(d)) }, andSelf: function () { return this.add(this.prevObject) } }), f.each({ parent: function (a) { var b = a.parentNode; return b && b.nodeType !== 11 ? b : null }, parents: function (a) { return f.dir(a, "parentNode") }, parentsUntil: function (a, b, c) { return f.dir(a, "parentNode", c) }, next: function (a) { return f.nth(a, 2, "nextSibling") }, prev: function (a) { return f.nth(a, 2, "previousSibling") }, nextAll: function (a) { return f.dir(a, "nextSibling") }, prevAll: function (a) { return f.dir(a, "previousSibling") }, nextUntil: function (a, b, c) { return f.dir(a, "nextSibling", c) }, prevUntil: function (a, b, c) { return f.dir(a, "previousSibling", c) }, siblings: function (a) { return f.sibling(a.parentNode.firstChild, a) }, children: function (a) { return f.sibling(a.firstChild) }, contents: function (a) { return f.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : f.makeArray(a.childNodes) } }, function (a, b) { f.fn[a] = function (c, d) { var e = f.map(this, b, c), g = S.call(arguments); O.test(a) || (d = c), d && typeof d == "string" && (e = f.filter(d, e)), e = this.length > 1 && !U[a] ? f.unique(e) : e, (this.length > 1 || Q.test(d)) && P.test(a) && (e = e.reverse()); return this.pushStack(e, a, g.join(",")) } }), f.extend({ filter: function (a, b, c) { c && (a = ":not(" + a + ")"); return b.length === 1 ? f.find.matchesSelector(b[0], a) ? [b[0]] : [] : f.find.matches(a, b) }, dir: function (a, c, d) { var e = [], g = a[c]; while (g && g.nodeType !== 9 && (d === b || g.nodeType !== 1 || !f(g).is(d))) g.nodeType === 1 && e.push(g), g = g[c]; return e }, nth: function (a, b, c, d) { b = b || 1; var e = 0; for (; a; a = a[c])if (a.nodeType === 1 && ++e === b) break; return a }, sibling: function (a, b) { var c = []; for (; a; a = a.nextSibling)a.nodeType === 1 && a !== b && c.push(a); return c } }); var Y = "abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video", Z = / jQuery\d+="(?:\d+|null)"/g, $ = /^\s+/, _ = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, ba = /<([\w:]+)/, bb = /", ""], legend: [1, "
", "
"], thead: [1, "", "
"], tr: [2, "", "
"], td: [3, "", "
"], col: [2, "", "
"], area: [1, "", ""], _default: [0, "", ""] }, bk = X(c); bj.optgroup = bj.option, bj.tbody = bj.tfoot = bj.colgroup = bj.caption = bj.thead, bj.th = bj.td, f.support.htmlSerialize || (bj._default = [1, "div
", "
"]), f.fn.extend({ + text: function (a) { if (f.isFunction(a)) return this.each(function (b) { var c = f(this); c.text(a.call(this, b, c.text())) }); if (typeof a != "object" && a !== b) return this.empty().append((this[0] && this[0].ownerDocument || c).createTextNode(a)); return f.text(this) }, wrapAll: function (a) { if (f.isFunction(a)) return this.each(function (b) { f(this).wrapAll(a.call(this, b)) }); if (this[0]) { var b = f(a, this[0].ownerDocument).eq(0).clone(!0); this[0].parentNode && b.insertBefore(this[0]), b.map(function () { var a = this; while (a.firstChild && a.firstChild.nodeType === 1) a = a.firstChild; return a }).append(this) } return this }, wrapInner: function (a) { if (f.isFunction(a)) return this.each(function (b) { f(this).wrapInner(a.call(this, b)) }); return this.each(function () { var b = f(this), c = b.contents(); c.length ? c.wrapAll(a) : b.append(a) }) }, wrap: function (a) { return this.each(function () { f(this).wrapAll(a) }) }, unwrap: function () { return this.parent().each(function () { f.nodeName(this, "body") || f(this).replaceWith(this.childNodes) }).end() }, append: function () { return this.domManip(arguments, !0, function (a) { this.nodeType === 1 && this.appendChild(a) }) }, prepend: function () { return this.domManip(arguments, !0, function (a) { this.nodeType === 1 && this.insertBefore(a, this.firstChild) }) }, before: function () { if (this[0] && this[0].parentNode) return this.domManip(arguments, !1, function (a) { this.parentNode.insertBefore(a, this) }); if (arguments.length) { var a = f(arguments[0]); a.push.apply(a, this.toArray()); return this.pushStack(a, "before", arguments) } }, after: function () { + if (this[0] && this[0].parentNode) return this.domManip(arguments, !1, function (a) { this.parentNode.insertBefore(a, this.nextSibling) }); if (arguments.length) { + var a = this.pushStack(this, "after" + , arguments); a.push.apply(a, f(arguments[0]).toArray()); return a + } + }, remove: function (a, b) { for (var c = 0, d; (d = this[c]) != null; c++)if (!a || f.filter(a, [d]).length) !b && d.nodeType === 1 && (f.cleanData(d.getElementsByTagName("*")), f.cleanData([d])), d.parentNode && d.parentNode.removeChild(d); return this }, empty: function () { for (var a = 0, b; (b = this[a]) != null; a++) { b.nodeType === 1 && f.cleanData(b.getElementsByTagName("*")); while (b.firstChild) b.removeChild(b.firstChild) } return this }, clone: function (a, b) { a = a == null ? !1 : a, b = b == null ? a : b; return this.map(function () { return f.clone(this, a, b) }) }, html: function (a) { if (a === b) return this[0] && this[0].nodeType === 1 ? this[0].innerHTML.replace(Z, "") : null; if (typeof a == "string" && !bd.test(a) && (f.support.leadingWhitespace || !$.test(a)) && !bj[(ba.exec(a) || ["", ""])[1].toLowerCase()]) { a = a.replace(_, "<$1>"); try { for (var c = 0, d = this.length; c < d; c++)this[c].nodeType === 1 && (f.cleanData(this[c].getElementsByTagName("*")), this[c].innerHTML = a) } catch (e) { this.empty().append(a) } } else f.isFunction(a) ? this.each(function (b) { var c = f(this); c.html(a.call(this, b, c.html())) }) : this.empty().append(a); return this }, replaceWith: function (a) { if (this[0] && this[0].parentNode) { if (f.isFunction(a)) return this.each(function (b) { var c = f(this), d = c.html(); c.replaceWith(a.call(this, b, d)) }); typeof a != "string" && (a = f(a).detach()); return this.each(function () { var b = this.nextSibling, c = this.parentNode; f(this).remove(), b ? f(b).before(a) : f(c).append(a) }) } return this.length ? this.pushStack(f(f.isFunction(a) ? a() : a), "replaceWith", a) : this }, detach: function (a) { return this.remove(a, !0) }, domManip: function (a, c, d) { var e, g, h, i, j = a[0], k = []; if (!f.support.checkClone && arguments.length === 3 && typeof j == "string" && bg.test(j)) return this.each(function () { f(this).domManip(a, c, d, !0) }); if (f.isFunction(j)) return this.each(function (e) { var g = f(this); a[0] = j.call(this, e, c ? g.html() : b), g.domManip(a, c, d) }); if (this[0]) { i = j && j.parentNode, f.support.parentNode && i && i.nodeType === 11 && i.childNodes.length === this.length ? e = { fragment: i } : e = f.buildFragment(a, this, k), h = e.fragment, h.childNodes.length === 1 ? g = h = h.firstChild : g = h.firstChild; if (g) { c = c && f.nodeName(g, "tr"); for (var l = 0, m = this.length, n = m - 1; l < m; l++)d.call(c ? bl(this[l], g) : this[l], e.cacheable || m > 1 && l < n ? f.clone(h, !0, !0) : h) } k.length && f.each(k, br) } return this } + }), f.buildFragment = function (a, b, d) { var e, g, h, i, j = a[0]; b && b[0] && (i = b[0].ownerDocument || b[0]), i.createDocumentFragment || (i = c), a.length === 1 && typeof j == "string" && j.length < 512 && i === c && j.charAt(0) === "<" && !be.test(j) && (f.support.checkClone || !bg.test(j)) && !f.support.unknownElems && bf.test(j) && (g = !0, h = f.fragments[j], h && h !== 1 && (e = h)), e || (e = i.createDocumentFragment(), f.clean(a, i, e, d)), g && (f.fragments[j] = h ? e : 1); return { fragment: e, cacheable: g } }, f.fragments = {}, f.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function (a, b) { f.fn[a] = function (c) { var d = [], e = f(c), g = this.length === 1 && this[0].parentNode; if (g && g.nodeType === 11 && g.childNodes.length === 1 && e.length === 1) { e[b](this[0]); return this } for (var h = 0, i = e.length; h < i; h++) { var j = (h > 0 ? this.clone(!0) : this).get(); f(e[h])[b](j), d = d.concat(j) } return this.pushStack(d, a, e.selector) } }), f.extend({ clone: function (a, b, c) { var d = a.cloneNode(!0), e, g, h; if ((!f.support.noCloneEvent || !f.support.noCloneChecked) && (a.nodeType === 1 || a.nodeType === 11) && !f.isXMLDoc(a)) { bn(a, d), e = bo(a), g = bo(d); for (h = 0; e[h]; ++h)g[h] && bn(e[h], g[h]) } if (b) { bm(a, d); if (c) { e = bo(a), g = bo(d); for (h = 0; e[h]; ++h)bm(e[h], g[h]) } } e = g = null; return d }, clean: function (a, b, d, e) { var g; b = b || c, typeof b.createElement == "undefined" && (b = b.ownerDocument || b[0] && b[0].ownerDocument || c); var h = [], i; for (var j = 0, k; (k = a[j]) != null; j++) { typeof k == "number" && (k += ""); if (!k) continue; if (typeof k == "string") if (!bc.test(k)) k = b.createTextNode(k); else { k = k.replace(_, "<$1>"); var l = (ba.exec(k) || ["", ""])[1].toLowerCase(), m = bj[l] || bj._default, n = m[0], o = b.createElement("div"); b === c ? bk.appendChild(o) : X(b).appendChild(o), o.innerHTML = m[1] + k + m[2]; while (n--) o = o.lastChild; if (!f.support.tbody) { var p = bb.test(k), q = l === "table" && !p ? o.firstChild && o.firstChild.childNodes : m[1] === "" && !p ? o.childNodes : []; for (i = q.length - 1; i >= 0; --i)f.nodeName(q[i], "tbody") && !q[i].childNodes.length && q[i].parentNode.removeChild(q[i]) } !f.support.leadingWhitespace && $.test(k) && o.insertBefore(b.createTextNode($.exec(k)[0]), o.firstChild), k = o.childNodes } var r; if (!f.support.appendChecked) if (k[0] && typeof (r = k.length) == "number") for (i = 0; i < r; i++)bq(k[i]); else bq(k); k.nodeType ? h.push(k) : h = f.merge(h, k) } if (d) { g = function (a) { return !a.type || bh.test(a.type) }; for (j = 0; h[j]; j++)if (e && f.nodeName(h[j], "script") && (!h[j].type || h[j].type.toLowerCase() === "text/javascript")) e.push(h[j].parentNode ? h[j].parentNode.removeChild(h[j]) : h[j]); else { if (h[j].nodeType === 1) { var s = f.grep(h[j].getElementsByTagName("script"), g); h.splice.apply(h, [j + 1, 0].concat(s)) } d.appendChild(h[j]) } } return h }, cleanData: function (a) { var b, c, d = f.cache, e = f.event.special, g = f.support.deleteExpando; for (var h = 0, i; (i = a[h]) != null; h++) { if (i.nodeName && f.noData[i.nodeName.toLowerCase()]) continue; c = i[f.expando]; if (c) { b = d[c]; if (b && b.events) { for (var j in b.events) e[j] ? f.event.remove(i, j) : f.removeEvent(i, j, b.handle); b.handle && (b.handle.elem = null) } g ? delete i[f.expando] : i.removeAttribute && i.removeAttribute(f.expando), delete d[c] } } } }); var bs = /alpha\([^)]*\)/i, bt = /opacity=([^)]*)/, bu = /([A-Z]|^ms)/g, bv = /^-?\d+(?:px)?$/i, bw = /^-?\d/, bx = /^([\-+])=([\-+.\de]+)/, by = { position: "absolute", visibility: "hidden", display: "block" }, bz = ["Left", "Right"], bA = ["Top", "Bottom"], bB, bC, bD; f.fn.css = function (a, c) { if (arguments.length === 2 && c === b) return this; return f.access(this, a, c, !0, function (a, c, d) { return d !== b ? f.style(a, c, d) : f.css(a, c) }) }, f.extend({ cssHooks: { opacity: { get: function (a, b) { if (b) { var c = bB(a, "opacity", "opacity"); return c === "" ? "1" : c } return a.style.opacity } } }, cssNumber: { fillOpacity: !0, fontWeight: !0, lineHeight: !0, opacity: !0, orphans: !0, widows: !0, zIndex: !0, zoom: !0 }, cssProps: { "float": f.support.cssFloat ? "cssFloat" : "styleFloat" }, style: function (a, c, d, e) { if (!!a && a.nodeType !== 3 && a.nodeType !== 8 && !!a.style) { var g, h, i = f.camelCase(c), j = a.style, k = f.cssHooks[i]; c = f.cssProps[i] || i; if (d === b) { if (k && "get" in k && (g = k.get(a, !1, e)) !== b) return g; return j[c] } h = typeof d, h === "string" && (g = bx.exec(d)) && (d = +(g[1] + 1) * +g[2] + parseFloat(f.css(a, c)), h = "number"); if (d == null || h === "number" && isNaN(d)) return; h === "number" && !f.cssNumber[i] && (d += "px"); if (!k || !("set" in k) || (d = k.set(a, d)) !== b) try { j[c] = d } catch (l) { } } }, css: function (a, c, d) { var e, g; c = f.camelCase(c), g = f.cssHooks[c], c = f.cssProps[c] || c, c === "cssFloat" && (c = "float"); if (g && "get" in g && (e = g.get(a, !0, d)) !== b) return e; if (bB) return bB(a, c) }, swap: function (a, b, c) { var d = {}; for (var e in b) d[e] = a.style[e], a.style[e] = b[e]; c.call(a); for (e in b) a.style[e] = d[e] } }), f.curCSS = f.css, f.each(["height", "width"], function (a, b) { f.cssHooks[b] = { get: function (a, c, d) { var e; if (c) { if (a.offsetWidth !== 0) return bE(a, b, d); f.swap(a, by, function () { e = bE(a, b, d) }); return e } }, set: function (a, b) { if (!bv.test(b)) return b; b = parseFloat(b); if (b >= 0) return b + "px" } } }), f.support.opacity || (f.cssHooks.opacity = { get: function (a, b) { return bt.test((b && a.currentStyle ? a.currentStyle.filter : a.style.filter) || "") ? parseFloat(RegExp.$1) / 100 + "" : b ? "1" : "" }, set: function (a, b) { var c = a.style, d = a.currentStyle, e = f.isNumeric(b) ? "alpha(opacity=" + b * 100 + ")" : "", g = d && d.filter || c.filter || ""; c.zoom = 1; if (b >= 1 && f.trim(g.replace(bs, "")) === "") { c.removeAttribute("filter"); if (d && !d.filter) return } c.filter = bs.test(g) ? g.replace(bs, e) : g + " " + e } }), f(function () { f.support.reliableMarginRight || (f.cssHooks.marginRight = { get: function (a, b) { var c; f.swap(a, { display: "inline-block" }, function () { b ? c = bB(a, "margin-right", "marginRight") : c = a.style.marginRight }); return c } }) }), c.defaultView && c.defaultView.getComputedStyle && (bC = function (a, c) { var d, e, g; c = c.replace(bu, "-$1").toLowerCase(); if (!(e = a.ownerDocument.defaultView)) return b; if (g = e.getComputedStyle(a, null)) d = g.getPropertyValue(c), d === "" && !f.contains(a.ownerDocument.documentElement, a) && (d = f.style(a, c)); return d }), c.documentElement.currentStyle && (bD = function (a, b) { var c, d, e, f = a.currentStyle && a.currentStyle[b], g = a.style; f === null && g && (e = g[b]) && (f = e), !bv.test(f) && bw.test(f) && (c = g.left, d = a.runtimeStyle && a.runtimeStyle.left, d && (a.runtimeStyle.left = a.currentStyle.left), g.left = b === "fontSize" ? "1em" : f || 0, f = g.pixelLeft + "px", g.left = c, d && (a.runtimeStyle.left = d)); return f === "" ? "auto" : f }), bB = bC || bD, f.expr && f.expr.filters && (f.expr.filters.hidden = function (a) { var b = a.offsetWidth, c = a.offsetHeight; return b === 0 && c === 0 || !f.support.reliableHiddenOffsets && (a.style && a.style.display || f.css(a, "display")) === "none" }, f.expr.filters.visible = function (a) { return !f.expr.filters.hidden(a) }); var bF = /%20/g, bG = /\[\]$/, bH = /\r?\n/g, bI = /#.*$/, bJ = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, bK = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, bL = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, bM = /^(?:GET|HEAD)$/, bN = /^\/\//, bO = /\?/, bP = /)<[^<]*)*<\/script>/gi, bQ = /^(?:select|textarea)/i, bR = /\s+/, bS = /([?&])_=[^&]*/, bT = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/, bU = f.fn.load, bV = {}, bW = {}, bX, bY, bZ = ["*/"] + ["*"]; try { bX = e.href } catch (b$) { bX = c.createElement("a"), bX.href = "", bX = bX.href } bY = bT.exec(bX.toLowerCase()) || [], f.fn.extend({ load: function (a, c, d) { if (typeof a != "string" && bU) return bU.apply(this, arguments); if (!this.length) return this; var e = a.indexOf(" "); if (e >= 0) { var g = a.slice(e, a.length); a = a.slice(0, e) } var h = "GET"; c && (f.isFunction(c) ? (d = c, c = b) : typeof c == "object" && (c = f.param(c, f.ajaxSettings.traditional), h = "POST")); var i = this; f.ajax({ url: a, type: h, dataType: "html", data: c, complete: function (a, b, c) { c = a.responseText, a.isResolved() && (a.done(function (a) { c = a }), i.html(g ? f("
").append(c.replace(bP, "")).find(g) : c)), d && i.each(d, [c, b, a]) } }); return this }, serialize: function () { return f.param(this.serializeArray()) }, serializeArray: function () { return this.map(function () { return this.elements ? f.makeArray(this.elements) : this }).filter(function () { return this.name && !this.disabled && (this.checked || bQ.test(this.nodeName) || bK.test(this.type)) }).map(function (a, b) { var c = f(this).val(); return c == null ? null : f.isArray(c) ? f.map(c, function (a, c) { return { name: b.name, value: a.replace(bH, "\r\n") } }) : { name: b.name, value: c.replace(bH, "\r\n") } }).get() } }), f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function (a, b) { f.fn[b] = function (a) { return this.bind(b, a) } }), f.each(["get", "post"], function (a, c) { f[c] = function (a, d, e, g) { f.isFunction(d) && (g = g || e, e = d, d = b); return f.ajax({ type: c, url: a, data: d, success: e, dataType: g }) } }), f.extend({ getScript: function (a, c) { return f.get(a, b, c, "script") }, getJSON: function (a, b, c) { return f.get(a, b, c, "json") }, ajaxSetup: function (a, b) { b ? cb(a, f.ajaxSettings) : (b = a, a = f.ajaxSettings), cb(a, b); return a }, ajaxSettings: { url: bX, isLocal: bL.test(bY[1]), global: !0, type: "GET", contentType: "application/x-www-form-urlencoded", processData: !0, async: !0, accepts: { xml: "application/xml, text/xml", html: "text/html", text: "text/plain", json: "application/json, text/javascript", "*": bZ }, contents: { xml: /xml/, html: /html/, json: /json/ }, responseFields: { xml: "responseXML", text: "responseText" }, converters: { "* text": a.String, "text html": !0, "text json": f.parseJSON, "text xml": f.parseXML }, flatOptions: { context: !0, url: !0 } }, ajaxPrefilter: b_(bV), ajaxTransport: b_(bW), ajax: function (a, c) { function w(a, c, l, m) { if (s !== 2) { s = 2, q && clearTimeout(q), p = b, n = m || "", v.readyState = a > 0 ? 4 : 0; var o, r, u, w = c, x = l ? cd(d, v, l) : b, y, z; if (a >= 200 && a < 300 || a === 304) { if (d.ifModified) { if (y = v.getResponseHeader("Last-Modified")) f.lastModified[k] = y; if (z = v.getResponseHeader("Etag")) f.etag[k] = z } if (a === 304) w = "notmodified", o = !0; else try { r = ce(d, x), w = "success", o = !0 } catch (A) { w = "parsererror", u = A } } else { u = w; if (!w || a) w = "error", a < 0 && (a = 0) } v.status = a, v.statusText = "" + (c || w), o ? h.resolveWith(e, [r, w, v]) : h.rejectWith(e, [v, w, u]), v.statusCode(j), j = b, t && g.trigger("ajax" + (o ? "Success" : "Error"), [v, d, o ? r : u]), i.fireWith(e, [v, w]), t && (g.trigger("ajaxComplete", [v, d]), --f.active || f.event.trigger("ajaxStop")) } } typeof a == "object" && (c = a, a = b), c = c || {}; var d = f.ajaxSetup({}, c), e = d.context || d, g = e !== d && (e.nodeType || e instanceof f) ? f(e) : f.event, h = f.Deferred(), i = f.Callbacks("once memory"), j = d.statusCode || {}, k, l = {}, m = {}, n, o, p, q, r, s = 0, t, u, v = { readyState: 0, setRequestHeader: function (a, b) { if (!s) { var c = a.toLowerCase(); a = m[c] = m[c] || a, l[a] = b } return this }, getAllResponseHeaders: function () { return s === 2 ? n : null }, getResponseHeader: function (a) { var c; if (s === 2) { if (!o) { o = {}; while (c = bJ.exec(n)) o[c[1].toLowerCase()] = c[2] } c = o[a.toLowerCase()] } return c === b ? null : c }, overrideMimeType: function (a) { s || (d.mimeType = a); return this }, abort: function (a) { a = a || "abort", p && p.abort(a), w(0, a); return this } }; h.promise(v), v.success = v.done, v.error = v.fail, v.complete = i.add, v.statusCode = function (a) { if (a) { var b; if (s < 2) for (b in a) j[b] = [j[b], a[b]]; else b = a[v.status], v.then(b, b) } return this }, d.url = ((a || d.url) + "").replace(bI, "").replace(bN, bY[1] + "//"), d.dataTypes = f.trim(d.dataType || "*").toLowerCase().split(bR), d.crossDomain == null && (r = bT.exec(d.url.toLowerCase()), d.crossDomain = !(!r || r[1] == bY[1] && r[2] == bY[2] && (r[3] || (r[1] === "http:" ? 80 : 443)) == (bY[3] || (bY[1] === "http:" ? 80 : 443)))), d.data && d.processData && typeof d.data != "string" && (d.data = f.param(d.data, d.traditional)), ca(bV, d, c, v); if (s === 2) return !1; t = d.global, d.type = d.type.toUpperCase(), d.hasContent = !bM.test(d.type), t && f.active++ === 0 && f.event.trigger("ajaxStart"); if (!d.hasContent) { d.data && (d.url += (bO.test(d.url) ? "&" : "?") + d.data, delete d.data), k = d.url; if (d.cache === !1) { var x = f.now(), y = d.url.replace(bS, "$1_=" + x); d.url = y + (y === d.url ? (bO.test(d.url) ? "&" : "?") + "_=" + x : "") } } (d.data && d.hasContent && d.contentType !== !1 || c.contentType) && v.setRequestHeader("Content-Type", d.contentType), d.ifModified && (k = k || d.url, f.lastModified[k] && v.setRequestHeader("If-Modified-Since", f.lastModified[k]), f.etag[k] && v.setRequestHeader("If-None-Match", f.etag[k])), v.setRequestHeader("Accept", d.dataTypes[0] && d.accepts[d.dataTypes[0]] ? d.accepts[d.dataTypes[0]] + (d.dataTypes[0] !== "*" ? ", " + bZ + "; q=0.01" : "") : d.accepts["*"]); for (u in d.headers) v.setRequestHeader(u, d.headers[u]); if (d.beforeSend && (d.beforeSend.call(e, v, d) === !1 || s === 2)) { v.abort(); return !1 } for (u in { success: 1, error: 1, complete: 1 }) v[u](d[u]); p = ca(bW, d, c, v); if (!p) w(-1, "No Transport"); else { v.readyState = 1, t && g.trigger("ajaxSend", [v, d]), d.async && d.timeout > 0 && (q = setTimeout(function () { v.abort("timeout") }, d.timeout)); try { s = 1, p.send(l, w) } catch (z) { s < 2 ? w(-1, z) : f.error(z) } } return v }, param: function (a, c) { var d = [], e = function (a, b) { b = f.isFunction(b) ? b() : b, d[d.length] = encodeURIComponent(a) + "=" + encodeURIComponent(b) }; c === b && (c = f.ajaxSettings.traditional); if (f.isArray(a) || a.jquery && !f.isPlainObject(a)) f.each(a, function () { e(this.name, this.value) }); else for (var g in a) cc(g, a[g], c, e); return d.join("&").replace(bF, "+") } }), f.extend({ active: 0, lastModified: {}, etag: {} }); var cf = f.now(), cg = /(\=)\?(&|$)|\?\?/i; f.ajaxSetup({ jsonp: "callback", jsonpCallback: function () { return f.expando + "_" + cf++ } }), f.ajaxPrefilter("json jsonp", function (b, c, d) { var e = b.contentType === "application/x-www-form-urlencoded" && typeof b.data == "string"; if (b.dataTypes[0] === "jsonp" || b.jsonp !== !1 && (cg.test(b.url) || e && cg.test(b.data))) { var g, h = b.jsonpCallback = f.isFunction(b.jsonpCallback) ? b.jsonpCallback() : b.jsonpCallback, i = a[h], j = b.url, k = b.data, l = "$1" + h + "$2"; b.jsonp !== !1 && (j = j.replace(cg, l), b.url === j && (e && (k = k.replace(cg, l)), b.data === k && (j += (/\?/.test(j) ? "&" : "?") + b.jsonp + "=" + h))), b.url = j, b.data = k, a[h] = function (a) { g = [a] }, d.always(function () { a[h] = i, g && f.isFunction(i) && a[h](g[0]) }), b.converters["script json"] = function () { g || f.error(h + " was not called"); return g[0] }, b.dataTypes[0] = "json"; return "script" } }), f.ajaxSetup({ accepts: { script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" }, contents: { script: /javascript|ecmascript/ }, converters: { "text script": function (a) { f.globalEval(a); return a } } }), f.ajaxPrefilter("script", function (a) { a.cache === b && (a.cache = !1), a.crossDomain && (a.type = "GET", a.global = !1) }), f.ajaxTransport("script", function (a) { if (a.crossDomain) { var d, e = c.head || c.getElementsByTagName("head")[0] || c.documentElement; return { send: function (f, g) { d = c.createElement("script"), d.async = "async", a.scriptCharset && (d.charset = a.scriptCharset), d.src = a.url, d.onload = d.onreadystatechange = function (a, c) { if (c || !d.readyState || /loaded|complete/.test(d.readyState)) d.onload = d.onreadystatechange = null, e && d.parentNode && e.removeChild(d), d = b, c || g(200, "success") }, e.insertBefore(d, e.firstChild) }, abort: function () { d && d.onload(0, 1) } } } }); var ch = a.ActiveXObject ? function () { for (var a in cj) cj[a](0, 1) } : !1, ci = 0, cj; f.ajaxSettings.xhr = a.ActiveXObject ? function () { return !this.isLocal && ck() || cl() } : ck, function (a) { f.extend(f.support, { ajax: !!a, cors: !!a && "withCredentials" in a }) }(f.ajaxSettings.xhr()), f.support.ajax && f.ajaxTransport(function (c) { if (!c.crossDomain || f.support.cors) { var d; return { send: function (e, g) { var h = c.xhr(), i, j; c.username ? h.open(c.type, c.url, c.async, c.username, c.password) : h.open(c.type, c.url, c.async); if (c.xhrFields) for (j in c.xhrFields) h[j] = c.xhrFields[j]; c.mimeType && h.overrideMimeType && h.overrideMimeType(c.mimeType), !c.crossDomain && !e["X-Requested-With"] && (e["X-Requested-With"] = "XMLHttpRequest"); try { for (j in e) h.setRequestHeader(j, e[j]) } catch (k) { } h.send(c.hasContent && c.data || null), d = function (a, e) { var j, k, l, m, n; try { if (d && (e || h.readyState === 4)) { d = b, i && (h.onreadystatechange = f.noop, ch && delete cj[i]); if (e) h.readyState !== 4 && h.abort(); else { j = h.status, l = h.getAllResponseHeaders(), m = {}, n = h.responseXML, n && n.documentElement && (m.xml = n), m.text = h.responseText; try { k = h.statusText } catch (o) { k = "" } !j && c.isLocal && !c.crossDomain ? j = m.text ? 200 : 404 : j === 1223 && (j = 204) } } } catch (p) { e || g(-1, p) } m && g(j, k, m, l) }, !c.async || h.readyState === 4 ? d() : (i = ++ci, ch && (cj || (cj = {}, f(a).unload(ch)), cj[i] = d), h.onreadystatechange = d) }, abort: function () { d && d(0, 1) } } } }); var cm = {}, cn, co, cp = /^(?:toggle|show|hide)$/, cq = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i, cr, cs = [["height", "marginTop", "marginBottom", "paddingTop", "paddingBottom"], ["width", "marginLeft", "marginRight", "paddingLeft", "paddingRight"], ["opacity"]], ct; f.fn.extend({ show: function (a, b, c) { var d, e; if (a || a === 0) return this.animate(cw("show", 3), a, b, c); for (var g = 0, h = this.length; g < h; g++)d = this[g], d.style && (e = d.style.display, !f._data(d, "olddisplay") && e === "none" && (e = d.style.display = ""), e === "" && f.css(d, "display") === "none" && f._data(d, "olddisplay", cx(d.nodeName))); for (g = 0; g < h; g++) { d = this[g]; if (d.style) { e = d.style.display; if (e === "" || e === "none") d.style.display = f._data(d, "olddisplay") || "" } } return this }, hide: function (a, b, c) { if (a || a === 0) return this.animate(cw("hide", 3), a, b, c); var d, e, g = 0, h = this.length; for (; g < h; g++)d = this[g], d.style && (e = f.css(d, "display"), e !== "none" && !f._data(d, "olddisplay") && f._data(d, "olddisplay", e)); for (g = 0; g < h; g++)this[g].style && (this[g].style.display = "none"); return this }, _toggle: f.fn.toggle, toggle: function (a, b, c) { var d = typeof a == "boolean"; f.isFunction(a) && f.isFunction(b) ? this._toggle.apply(this, arguments) : a == null || d ? this.each(function () { var b = d ? a : f(this).is(":hidden"); f(this)[b ? "show" : "hide"]() }) : this.animate(cw("toggle", 3), a, b, c); return this }, fadeTo: function (a, b, c, d) { return this.filter(":hidden").css("opacity", 0).show().end().animate({ opacity: b }, a, c, d) }, animate: function (a, b, c, d) { function g() { e.queue === !1 && f._mark(this); var b = f.extend({}, e), c = this.nodeType === 1, d = c && f(this).is(":hidden"), g, h, i, j, k, l, m, n, o; b.animatedProperties = {}; for (i in a) { g = f.camelCase(i), i !== g && (a[g] = a[i], delete a[i]), h = a[g], f.isArray(h) ? (b.animatedProperties[g] = h[1], h = a[g] = h[0]) : b.animatedProperties[g] = b.specialEasing && b.specialEasing[g] || b.easing || "swing"; if (h === "hide" && d || h === "show" && !d) return b.complete.call(this); c && (g === "height" || g === "width") && (b.overflow = [this.style.overflow, this.style.overflowX, this.style.overflowY], f.css(this, "display") === "inline" && f.css(this, "float") === "none" && (!f.support.inlineBlockNeedsLayout || cx(this.nodeName) === "inline" ? this.style.display = "inline-block" : this.style.zoom = 1)) } b.overflow != null && (this.style.overflow = "hidden"); for (i in a) j = new f.fx(this, b, i), h = a[i], cp.test(h) ? (o = f._data(this, "toggle" + i) || (h === "toggle" ? d ? "show" : "hide" : 0), o ? (f._data(this, "toggle" + i, o === "show" ? "hide" : "show"), j[o]()) : j[h]()) : (k = cq.exec(h), l = j.cur(), k ? (m = parseFloat(k[2]), n = k[3] || (f.cssNumber[i] ? "" : "px"), n !== "px" && (f.style(this, i, (m || 1) + n), l = (m || 1) / j.cur() * l, f.style(this, i, l + n)), k[1] && (m = (k[1] === "-=" ? -1 : 1) * m + l), j.custom(l, m, n)) : j.custom(l, h, "")); return !0 } var e = f.speed(b, c, d); if (f.isEmptyObject(a)) return this.each(e.complete, [!1]); a = f.extend({}, a); return e.queue === !1 ? this.each(g) : this.queue(e.queue, g) }, stop: function (a, c, d) { typeof a != "string" && (d = c, c = a, a = b), c && a !== !1 && this.queue(a || "fx", []); return this.each(function () { function h(a, b, c) { var e = b[c]; f.removeData(a, c, !0), e.stop(d) } var b, c = !1, e = f.timers, g = f._data(this); d || f._unmark(!0, this); if (a == null) for (b in g) g[b].stop && b.indexOf(".run") === b.length - 4 && h(this, g, b); else g[b = a + ".run"] && g[b].stop && h(this, g, b); for (b = e.length; b--;)e[b].elem === this && (a == null || e[b].queue === a) && (d ? e[b](!0) : e[b].saveState(), c = !0, e.splice(b, 1)); (!d || !c) && f.dequeue(this, a) }) } }), f.each({ slideDown: cw("show", 1), slideUp: cw("hide", 1), slideToggle: cw("toggle", 1), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } }, function (a, b) { f.fn[a] = function (a, c, d) { return this.animate(b, a, c, d) } }), f.extend({ speed: function (a, b, c) { var d = a && typeof a == "object" ? f.extend({}, a) : { complete: c || !c && b || f.isFunction(a) && a, duration: a, easing: c && b || b && !f.isFunction(b) && b }; d.duration = f.fx.off ? 0 : typeof d.duration == "number" ? d.duration : d.duration in f.fx.speeds ? f.fx.speeds[d.duration] : f.fx.speeds._default; if (d.queue == null || d.queue === !0) d.queue = "fx"; d.old = d.complete, d.complete = function (a) { f.isFunction(d.old) && d.old.call(this), d.queue ? f.dequeue(this, d.queue) : a !== !1 && f._unmark(this) }; return d }, easing: { linear: function (a, b, c, d) { return c + d * a }, swing: function (a, b, c, d) { return (-Math.cos(a * Math.PI) / 2 + .5) * d + c } }, timers: [], fx: function (a, b, c) { this.options = b, this.elem = a, this.prop = c, b.orig = b.orig || {} } }), f.fx.prototype = { update: function () { this.options.step && this.options.step.call(this.elem, this.now, this), (f.fx.step[this.prop] || f.fx.step._default)(this) }, cur: function () { if (this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null)) return this.elem[this.prop]; var a, b = f.css(this.elem, this.prop); return isNaN(a = parseFloat(b)) ? !b || b === "auto" ? 0 : b : a }, custom: function (a, c, d) { function h(a) { return e.step(a) } var e = this, g = f.fx; this.startTime = ct || cu(), this.end = c, this.now = this.start = a, this.pos = this.state = 0, this.unit = d || this.unit || (f.cssNumber[this.prop] ? "" : "px"), h.queue = this.options.queue, h.elem = this.elem, h.saveState = function () { e.options.hide && f._data(e.elem, "fxshow" + e.prop) === b && f._data(e.elem, "fxshow" + e.prop, e.start) }, h() && f.timers.push(h) && !cr && (cr = setInterval(g.tick, g.interval)) }, show: function () { var a = f._data(this.elem, "fxshow" + this.prop); this.options.orig[this.prop] = a || f.style(this.elem, this.prop), this.options.show = !0, a !== b ? this.custom(this.cur(), a) : this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur()), f(this.elem).show() }, hide: function () { this.options.orig[this.prop] = f._data(this.elem, "fxshow" + this.prop) || f.style(this.elem, this.prop), this.options.hide = !0, this.custom(this.cur(), 0) }, step: function (a) { var b, c, d, e = ct || cu(), g = !0, h = this.elem, i = this.options; if (a || e >= i.duration + this.startTime) { this.now = this.end, this.pos = this.state = 1, this.update(), i.animatedProperties[this.prop] = !0; for (b in i.animatedProperties) i.animatedProperties[b] !== !0 && (g = !1); if (g) { i.overflow != null && !f.support.shrinkWrapBlocks && f.each(["", "X", "Y"], function (a, b) { h.style["overflow" + b] = i.overflow[a] }), i.hide && f(h).hide(); if (i.hide || i.show) for (b in i.animatedProperties) f.style(h, b, i.orig[b]), f.removeData(h, "fxshow" + b, !0), f.removeData(h, "toggle" + b, !0); d = i.complete, d && (i.complete = !1, d.call(h)) } return !1 } i.duration == Infinity ? this.now = e : (c = e - this.startTime, this.state = c / i.duration, this.pos = f.easing[i.animatedProperties[this.prop]](this.state, c, 0, 1, i.duration), this.now = this.start + (this.end - this.start) * this.pos), this.update(); return !0 } }, f.extend(f.fx, { tick: function () { var a, b = f.timers, c = 0; for (; c < b.length; c++)a = b[c], !a() && b[c] === a && b.splice(c--, 1); b.length || f.fx.stop() }, interval: 13, stop: function () { clearInterval(cr), cr = null }, speeds: { slow: 600, fast: 200, _default: 400 }, step: { opacity: function (a) { f.style(a.elem, "opacity", a.now) }, _default: function (a) { a.elem.style && a.elem.style[a.prop] != null ? a.elem.style[a.prop] = a.now + a.unit : a.elem[a.prop] = a.now } } }), f.each(["width", "height"], function (a, b) { f.fx.step[b] = function (a) { f.style(a.elem, b, Math.max(0, a.now)) } }), f.expr && f.expr.filters && (f.expr.filters.animated = function (a) { return f.grep(f.timers, function (b) { return a === b.elem }).length }); var cy = /^t(?:able|d|h)$/i, cz = /^(?:body|html)$/i; "getBoundingClientRect" in c.documentElement ? f.fn.offset = function (a) { var b = this[0], c; if (a) return this.each(function (b) { f.offset.setOffset(this, a, b) }); if (!b || !b.ownerDocument) return null; if (b === b.ownerDocument.body) return f.offset.bodyOffset(b); try { c = b.getBoundingClientRect() } catch (d) { } var e = b.ownerDocument, g = e.documentElement; if (!c || !f.contains(g, b)) return c ? { top: c.top, left: c.left } : { top: 0, left: 0 }; var h = e.body, i = cA(e), j = g.clientTop || h.clientTop || 0, k = g.clientLeft || h.clientLeft || 0, l = i.pageYOffset || f.support.boxModel && g.scrollTop || h.scrollTop, m = i.pageXOffset || f.support.boxModel && g.scrollLeft || h.scrollLeft, n = c.top + l - j, o = c.left + m - k; return { top: n, left: o } } : f.fn.offset = function (a) { var b = this[0]; if (a) return this.each(function (b) { f.offset.setOffset(this, a, b) }); if (!b || !b.ownerDocument) return null; if (b === b.ownerDocument.body) return f.offset.bodyOffset(b); var c, d = b.offsetParent, e = b, g = b.ownerDocument, h = g.documentElement, i = g.body, j = g.defaultView, k = j ? j.getComputedStyle(b, null) : b.currentStyle, l = b.offsetTop, m = b.offsetLeft; while ((b = b.parentNode) && b !== i && b !== h) { if (f.support.fixedPosition && k.position === "fixed") break; c = j ? j.getComputedStyle(b, null) : b.currentStyle, l -= b.scrollTop, m -= b.scrollLeft, b === d && (l += b.offsetTop, m += b.offsetLeft, f.support.doesNotAddBorder && (!f.support.doesAddBorderForTableAndCells || !cy.test(b.nodeName)) && (l += parseFloat(c.borderTopWidth) || 0, m += parseFloat(c.borderLeftWidth) || 0), e = d, d = b.offsetParent), f.support.subtractsBorderForOverflowNotVisible && c.overflow !== "visible" && (l += parseFloat(c.borderTopWidth) || 0, m += parseFloat(c.borderLeftWidth) || 0), k = c } if (k.position === "relative" || k.position === "static") l += i.offsetTop, m += i.offsetLeft; f.support.fixedPosition && k.position === "fixed" && (l += Math.max(h.scrollTop, i.scrollTop), m += Math.max(h.scrollLeft, i.scrollLeft)); return { top: l, left: m } }, f.offset = { bodyOffset: function (a) { var b = a.offsetTop, c = a.offsetLeft; f.support.doesNotIncludeMarginInBodyOffset && (b += parseFloat(f.css(a, "marginTop")) || 0, c += parseFloat(f.css(a, "marginLeft")) || 0); return { top: b, left: c } }, setOffset: function (a, b, c) { var d = f.css(a, "position"); d === "static" && (a.style.position = "relative"); var e = f(a), g = e.offset(), h = f.css(a, "top"), i = f.css(a, "left"), j = (d === "absolute" || d === "fixed") && f.inArray("auto", [h, i]) > -1, k = {}, l = {}, m, n; j ? (l = e.position(), m = l.top, n = l.left) : (m = parseFloat(h) || 0, n = parseFloat(i) || 0), f.isFunction(b) && (b = b.call(a, c, g)), b.top != null && (k.top = b.top - g.top + m), b.left != null && (k.left = b.left - g.left + n), "using" in b ? b.using.call(a, k) : e.css(k) } }, f.fn.extend({ position: function () { if (!this[0]) return null; var a = this[0], b = this.offsetParent(), c = this.offset(), d = cz.test(b[0].nodeName) ? { top: 0, left: 0 } : b.offset(); c.top -= parseFloat(f.css(a, "marginTop")) || 0, c.left -= parseFloat(f.css(a, "marginLeft")) || 0, d.top += parseFloat(f.css(b[0], "borderTopWidth")) || 0, d.left += parseFloat(f.css(b[0], "borderLeftWidth")) || 0; return { top: c.top - d.top, left: c.left - d.left } }, offsetParent: function () { return this.map(function () { var a = this.offsetParent || c.body; while (a && !cz.test(a.nodeName) && f.css(a, "position") === "static") a = a.offsetParent; return a }) } }), f.each(["Left", "Top"], function (a, c) { var d = "scroll" + c; f.fn[d] = function (c) { var e, g; if (c === b) { e = this[0]; if (!e) return null; g = cA(e); return g ? "pageXOffset" in g ? g[a ? "pageYOffset" : "pageXOffset"] : f.support.boxModel && g.document.documentElement[d] || g.document.body[d] : e[d] } return this.each(function () { g = cA(this), g ? g.scrollTo(a ? f(g).scrollLeft() : c, a ? c : f(g).scrollTop()) : this[d] = c }) } }), f.each(["Height", "Width"], function (a, c) { var d = c.toLowerCase(); f.fn["inner" + c] = function () { var a = this[0]; return a ? a.style ? parseFloat(f.css(a, d, "padding")) : this[d]() : null }, f.fn["outer" + c] = function (a) { var b = this[0]; return b ? b.style ? parseFloat(f.css(b, d, a ? "margin" : "border")) : this[d]() : null }, f.fn[d] = function (a) { var e = this[0]; if (!e) return a == null ? null : this; if (f.isFunction(a)) return this.each(function (b) { var c = f(this); c[d](a.call(this, b, c[d]())) }); if (f.isWindow(e)) { var g = e.document.documentElement["client" + c], h = e.document.body; return e.document.compatMode === "CSS1Compat" && g || h && h["client" + c] || g } if (e.nodeType === 9) return Math.max(e.documentElement["client" + c], e.body["scroll" + c], e.documentElement["scroll" + c], e.body["offset" + c], e.documentElement["offset" + c]); if (a === b) { var i = f.css(e, d), j = parseFloat(i); return f.isNumeric(j) ? j : i } return this.css(d, typeof a == "string" ? a : a + "px") } }), a.jQuery = a.$ = f +})(window); diff --git a/DocTools/TplFile/chm/list.cshtml b/DocTools/TplFile/chm/list.cshtml new file mode 100644 index 0000000..1f7773a --- /dev/null +++ b/DocTools/TplFile/chm/list.cshtml @@ -0,0 +1,159 @@ + + + + + 数据库目录 + + + +
+ @if (Model.Tables != null && Model.Tables.Count > 0) + { +
+ + + + + + + + @foreach (var item in Model.Tables) + { + + + + + + } +
+ 表 +
序号表名表说明
@item.TableOrder + @item.TableName + @(string.IsNullOrEmpty(item.Comment)?" " : item.Comment)
+ } + + @if (Model.Views != null && Model.Views.Count > 0) + { + + + + + + + + @{ var vwIndex = 1; } + @foreach (var item in Model.Views) + { + + + + + vwIndex++; + } + +
+ 视图 +
序号视图名称
@vwIndex + @item.Key +
+ } + + @if (Model.Procs != null && Model.Procs.Count > 0) + { + + + + + + + + @{ var procIndex = 1; } + @foreach (var item in Model.Procs) + { + + + + + procIndex++; + } + +
+ 存储过程 +
序号存储过程名称
@procIndex + @item.Key +
+ } + + + + + diff --git a/DocTools/TplFile/chm/sqlcode.cshtml b/DocTools/TplFile/chm/sqlcode.cshtml new file mode 100644 index 0000000..8e3ec7c --- /dev/null +++ b/DocTools/TplFile/chm/sqlcode.cshtml @@ -0,0 +1,84 @@ + + + + + @Model.CodeName + + + + +
+

@Model.CodeName

+
@Model.StyleContent
+
+ + diff --git a/DocTools/TplFile/chm/table.cshtml b/DocTools/TplFile/chm/table.cshtml new file mode 100644 index 0000000..485f875 --- /dev/null +++ b/DocTools/TplFile/chm/table.cshtml @@ -0,0 +1,134 @@ + + + + + @Model.TableName + + + + +
+
+ + + + +
+ + + + + + + + + + @if (!Model.DBType.StartsWith("Oracle")) + { + + } + + + + + @{ + var index = 1; + } + @foreach (var item in Model.Columns) + { + + + + + + + + + @if (!Model.DBType.StartsWith("Oracle")) + { + + } + + + + + + index++; + } +
+
@Model.TableName
+ @Model.Comment + + 返回目录 + +
序号列名数据类型长度小数位数主键自增允许空默认值列说明
@index@item.ColumnName@item.ColumnTypeName@(string.IsNullOrWhiteSpace(item.Length)? " ":item.Length)@(string.IsNullOrWhiteSpace(item.Scale)? " ":item.Scale)@(string.IsNullOrWhiteSpace(item.IsPK)?" ": item.IsPK)@(string.IsNullOrWhiteSpace(item.IsIdentity) ?" ": item.IsIdentity)@(string.IsNullOrWhiteSpace(item.CanNull) ?" ": item.CanNull)@(string.IsNullOrWhiteSpace(item.DefaultVal) ?" ": item.DefaultVal)@(string.IsNullOrWhiteSpace(item.Comment) ?" ": item.Comment)
+
+
+
+ + diff --git a/DocTools/TplFile/html/html.cshtml b/DocTools/TplFile/html/html.cshtml new file mode 100644 index 0000000..534c6e3 --- /dev/null +++ b/DocTools/TplFile/html/html.cshtml @@ -0,0 +1,825 @@ + + + + + @Model.DBName 数据库文档_v1.0 + + + + + + +
+
+
+ + +
+
+

数据库表目录

+
+
+ + + + @for (int j = 0; j < Model.Tables.Count; j++) + { + var item = Model.Tables[j]; + + + + + + } +
序号表名表说明
@( j + 1 )@item.TableName@(!string.IsNullOrWhiteSpace(item.Comment) ? item.Comment : " ")
+ + @if (Model.Views.Count > 0) + { + + + + + @{ var index = 1; } + @foreach (var item in Model.Views) + { + + + + + index++; + } + +
视图
序号视图名称
@index@item.Key
+ } + + @if (Model.Procs.Count > 0) + { + + + + + @{ var index = 1; } + @foreach (var item in Model.Procs) + { + + + + + index++; + } + +
存储过程
序号存储过程名称
@index@item.Key
+ } + +
+
+ + @foreach (var item in Model.Tables) + { +
+
+ 表名:@item.TableName + 返回目录 +
+
表注释:@item.Comment
+
+ + + + + + + + + + + @if (!item.DBType.StartsWith("Oracle")) + { + + } + + + + + + + @foreach (var column in item.Columns) + { + + + + + + + + + @if (!item.DBType.StartsWith("Oracle")) + { + + } + + + + + + } + +
序号列名数据类型长度小数位数主键自增允许空默认值列说明
@column.ColumnOrder@column.ColumnName@column.ColumnTypeName@column.Length@column.Scale@column.IsPK@column.IsIdentity@column.CanNull@column.DefaultVal@column.Comment
+
+
+ } + + @if (Model.Views.Count > 0) + { + foreach (var item in Model.Views) + { +
+
+ 视图名称:@item.Key + 返回目录 +
+
+
@item.Value
+
+
+ } + } + + @if (Model.Procs.Count > 0) + { + foreach (var item in Model.Procs) + { +
+
+ 存储过程名称:@item.Key + 返回目录 +
+
+
@item.Value
+
+
+ } + } +
+
+ + + + + + \ No newline at end of file diff --git a/DocTools/TplFile/pdf/msyh.ttf b/DocTools/TplFile/pdf/msyh.ttf new file mode 100644 index 0000000..ea3c090 Binary files /dev/null and b/DocTools/TplFile/pdf/msyh.ttf differ diff --git a/DocTools/app.config b/DocTools/app.config new file mode 100644 index 0000000..ef94230 --- /dev/null +++ b/DocTools/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/DocTools/lib/Aspose.Words.dll b/DocTools/lib/Aspose.Words.dll new file mode 100644 index 0000000..807bc9e Binary files /dev/null and b/DocTools/lib/Aspose.Words.dll differ diff --git a/DocTools/lib/EPPlus.dll b/DocTools/lib/EPPlus.dll new file mode 100644 index 0000000..39e6295 Binary files /dev/null and b/DocTools/lib/EPPlus.dll differ diff --git a/DocTools/packages.config b/DocTools/packages.config new file mode 100644 index 0000000..1bed050 --- /dev/null +++ b/DocTools/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a842010 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 51try.top + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MJTop.Data/BWofter.Converters/Collections/Generic/DictionaryConverter.cs b/MJTop.Data/BWofter.Converters/Collections/Generic/DictionaryConverter.cs new file mode 100644 index 0000000..8e26233 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Collections/Generic/DictionaryConverter.cs @@ -0,0 +1,116 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Collections.Generic +{ + using BWofter.Converters.Data; + using BWofter.Converters.Extensions; + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + /// A static class used to convert dictionaries into entities. + /// The entity type to convert to. + public static class DictionaryConverter where TEntity : class, new() + { + /// Iterates over the of keys and values in the + /// , mapping their fields to the entity type. + /// The instance of keys and values + /// to map to the entity type. + /// An instance of the entity type. + public static TEntity ToEntity(IDictionary dictionary) + { + if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); + return ToEntities(new[] { dictionary }).FirstOrDefault(); + } + /// Iterates over the of keys and values in the + /// , mapping their fields to the entity type. + /// The instance of keys and values + /// to map to the entity type. + /// The to map the keys to properties. + /// An instance of the entity type. + public static TEntity ToEntity(IDictionary dictionary, IDictionary columnToMemberMap) + { + if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + return ToEntities(new[] { dictionary }, columnToMemberMap).FirstOrDefault(); + } + /// Iterates over the of keys and values in the + /// , mapping their fields to the entity type. + /// The instances of keys and values + /// to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(IEnumerable> dictionaries) + { + IDictionary first = dictionaries?.FirstOrDefault(); + if (dictionaries == null) throw new ArgumentNullException(nameof(dictionaries)); + if (first == null) throw new ArgumentException($"Parameter {nameof(dictionaries)} should have at least 1 value, 0 given."); + //Get a table definition for the dictionaries then call the data table converter. + DataTable dataTable = first.AsDataTableDefinition(); + return DataTableConverter.ToEntities(dictionaries.Select(d => DictionaryToDataRow(d, dataTable))); + } + /// Iterates over the of keys and values in the + /// , mapping their fields to the entity type. + /// The instances of keys and values + /// to map to the entity type. + /// The to map the keys to properties. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(IEnumerable> dictionaries, IDictionary columnToMemberMap) + { + IDictionary first = dictionaries?.FirstOrDefault(); + if (dictionaries == null) throw new ArgumentNullException(nameof(dictionaries)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + if (first == null) throw new ArgumentException($"Parameter {nameof(dictionaries)} should have at least 1 value, 0 given."); + //Get a table definition for the dictionaries then call the data table converter. + DataTable dataTable = first.AsDataTableDefinition(); + //Converts the sting/string column to member map into a data column/string column to member map. + Dictionary translatedColumnToMemberMap = new Dictionary(); + foreach (KeyValuePair entry in columnToMemberMap) + { + translatedColumnToMemberMap.Add(dataTable.Columns[entry.Key], entry.Value); + } + return DataTableConverter.ToEntities(dictionaries.Select(d => DictionaryToDataRow(d, dataTable)), translatedColumnToMemberMap); + } + //Generates a new data row for each dictionary in the the ienumerable. + private static DataRow DictionaryToDataRow(IDictionary rowData, DataTable dataTable) + { + //Create a new data row and add it to the data table. + DataRow dataRow = dataTable.NewRow(); + dataTable.Rows.Add(dataRow); + //Iterate over the entries in the dictionary and add them to the row. + foreach (KeyValuePair entry in rowData) + { + dataRow[entry.Key] = entry.Value; + } + return dataRow; + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Data/DataTableConverter.cs b/MJTop.Data/BWofter.Converters/Data/DataTableConverter.cs new file mode 100644 index 0000000..c855f7c --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Data/DataTableConverter.cs @@ -0,0 +1,279 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Data +{ + using BWofter.Converters.EqualityComparers; + using BWofter.Converters.Expressions; + using BWofter.Converters.Extensions; + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + /// A static class used to convert data tables into entities. + /// The entity type to convert to. + public static class DataTableConverter where TEntity : class, new() + { + private static readonly Type type = typeof(TEntity); + private static readonly ConcurrentDictionary, Func> converters = new ConcurrentDictionary, Func>(StringCollectionComparer.GetInstance()); + /// Iterates over the values in the , mapping their fields + /// to the entity type. + /// The to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(DataTable dataTable) + { + if (dataTable == null) throw new ArgumentNullException(nameof(dataTable)); + return ToEntities(dataTable, new Dictionary(dataTable.Columns.Select(GetDataColumnKeyValuePair).ToDictionary())); + } + /// Iterates over the values in the , mapping their fields + /// to the entity type. + /// The to map to the entity type. + /// The to map the values to properties. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(DataTable dataTable, IDictionary columnToMemberMap) + { + if (dataTable == null) throw new ArgumentNullException(nameof(dataTable)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + Func converter = GetConverter(columnToMemberMap); + if (converter == null) throw new InvalidOperationException($"Unable to generate a converter for the data table. This could be due to a bug in the expression generator."); + //Iterate over the data rows in the data table, yielding the results back to the caller. + foreach (DataRow row in dataTable.Rows) + { + yield return converter(row); + } + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(DataRowCollection dataRows) + { + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (dataRows.Count == 0) throw new ArgumentException($"Parameter {nameof(dataRows)} should have at least 1 value, 0 given."); + return ToEntities(dataRows, dataRows[0].Table.Columns.Select(GetDataColumnKeyValuePair).ToDictionary()); + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The to map to the entity type. + /// The to map the values to properties. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(DataRowCollection dataRows, IDictionary columnToMemberMap) + { + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + Func converter = GetConverter(columnToMemberMap); + if (converter == null) throw new InvalidOperationException($"Unable to generate a converter for the data table. This could be due to a bug in the expression generator."); + //Iterate over the data rows in the data table, yielding the results back to the caller. + foreach (DataRow row in dataRows) + { + yield return converter(row); + } + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The of values to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(IEnumerable dataRows) + { + DataRow first = dataRows?.FirstOrDefault(); + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (first == null) throw new ArgumentException($"Parameter {nameof(dataRows)} should have at least 1 value, 0 given."); + return ToEntities(dataRows, first.Table.Columns.Select(GetDataColumnKeyValuePair).ToDictionary()); + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The of values to map to the entity type. + /// The to map the values to properties. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(IEnumerable dataRows, IDictionary columnToMemberMap) + { + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + Func converter = GetConverter(columnToMemberMap); + if (converter == null) throw new InvalidOperationException($"Unable to generate a converter for the data table. This could be due to a bug in the expression generator."); + //Iterate over the data rows in the data table, yielding the results back to the caller. + foreach (DataRow row in dataRows) + { + yield return converter(row); + } + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The of values to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(params DataRow[] dataRows) + { + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (dataRows.Length == 0) throw new ArgumentException($"Parameter {nameof(dataRows)} should have at least 1 value, 0 given."); + return ToEntities(dataRows, dataRows[0].Table.Columns.Select(GetDataColumnKeyValuePair).ToDictionary()); + } + /// Iterates over the values in , mapping their fields + /// to the entity type. + /// The to map the values to properties. + /// The of values to map to the entity type. + /// A yielded instance of the entity type. + public static IEnumerable ToEntities(IDictionary columnToMemberMap, params DataRow[] dataRows) + { + if (dataRows == null) throw new ArgumentNullException(nameof(dataRows)); + if (columnToMemberMap == null) throw new ArgumentNullException(nameof(columnToMemberMap)); + Func converter = GetConverter(columnToMemberMap); + if (converter == null) throw new InvalidOperationException($"Unable to generate a converter for the data table. This could be due to a bug in the expression generator."); + //Iterate over the data rows in the data table, yielding the results back to the caller. + foreach (DataRow row in dataRows) + { + yield return converter(row); + } + } + //Creates a cached generator for the converter to use for all data tables with a matching set of data columns. Due to our assumption that data types are loose in + //data tables, this will generate an extremely generic converter that doesn't statically use the data table's data typing. + private static Func GetConverter(IDictionary columnToMemberMap) + { + List columnNames = columnToMemberMap.Select(k => k.Key.ColumnName).ToList(); + if (!converters.TryGetValue(columnNames, out Func value)) + { + NewExpression instantiate = Expression.New(type); + ParameterExpression dataRow = Expression.Parameter(typeof(DataRow), "dataRow"); + //Declare a dictionary to prevent creating more than 1 instance of a temporary parser type. + ConcurrentDictionary parameters = new ConcurrentDictionary(); + List memberBindings = new List(); + //Iterate over the column map and generate the expressions needed. + foreach (KeyValuePair columnToMember in columnToMemberMap) + { + if (TryGetMemberInfo(columnToMember.Value, columnToMember.Key.Table.CaseSensitive, out MemberInfo memberInfo)) + { + //Get a member expression. This is used for type resolution. + Type memberType = Expression.MakeMemberAccess(instantiate, memberInfo).Type, + realType = Nullable.GetUnderlyingType(memberType) ?? memberType; + //Get the data column expression used to access the data row field. + DataColumnExpression dataColumn = DataExpression.DataColumn(DataExpression.DataTable(dataRow), columnToMember.Key.ColumnName); + //Get the is null expression used to determine if the data field is null. + DataFieldIsNullExpression callDataFieldIsNull = DataExpression.DataFieldIsNull(dataRow, dataColumn); + ConditionalExpression dataFieldIsNull = Expression.Condition(callDataFieldIsNull, Expression.Default(memberType), + Expression.Default(memberType)); + //Get the is assignable from expression used to determine if the data column can be converted to the member type. + IsAssignableFromExpression callIsAssignableFrom = DataExpression.IsAssignableFrom(DataExpression.DataType(dataColumn), memberType); + //Get the is assignable from true result. If member type is string, then call the trim method on the string. + Expression isAssignableFromTrue = Expression.Convert(DataExpression.DataField(dataRow, dataColumn), memberType); + if (typeof(string).IsAssignableFrom(memberType)) + { + isAssignableFromTrue = Expression.Call(isAssignableFromTrue, "Trim", Type.EmptyTypes); + } + //Get the conditional expression used to process the data column type conversion. + ConditionalExpression isAssignableFrom = Expression.Condition(callIsAssignableFrom, + isAssignableFromTrue, Expression.Default(memberType)); + if (realType.GetMethods().Any(m => m.Name == "TryParse")) + { + //Get the is string parameter expression used for try parsing. + ParameterExpression stringLocal = parameters.GetOrAdd(typeof(string), Expression.Variable(typeof(string))), + outLocal = parameters.GetOrAdd(realType, Expression.Variable(realType)); + //Get the type is assign and try parse expressions used for try parsing. + TypeIsAssignExpression typeIsAssign = DataExpression.TypeIsAssign( + DataExpression.DataField(dataRow, dataColumn), stringLocal); + TryParseExpression callTryParse = DataExpression.TryParse(stringLocal, outLocal); + //Get the conditional expression used to process the try parse conversion. + ConditionalExpression tryParse = Expression.Condition( + Expression.AndAlso(typeIsAssign, callTryParse), + Expression.Convert(outLocal, memberType), Expression.Default(memberType)); + //Update is assignable from with the try parse method. + isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, tryParse); + } + if (typeof(IConvertible).IsAssignableFrom(realType)) + { + //Get the change type expression. + ChangeTypeExpression callChangeType = DataExpression.ChangeType( + DataExpression.DataField(dataRow, dataColumn), realType); + //Get the conversion expression for the change type. If member type is string, then call the trim method on the string. + Expression changeType = Expression.Convert(callChangeType, memberType); + if (typeof(string).IsAssignableFrom(memberType)) + { + changeType = Expression.Call(changeType, "Trim", Type.EmptyTypes); + } + //Determine if try parse is set. If so, update it and is assignable from. Otherwise, add change type to is assignable from. + if (isAssignableFrom.IfFalse is ConditionalExpression tryParse) + { + tryParse = tryParse.Update(tryParse.Test, tryParse.IfTrue, changeType); + isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, tryParse); + } + else + { + isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, changeType); + } + } + dataFieldIsNull = dataFieldIsNull.Update(dataFieldIsNull.Test, dataFieldIsNull.IfTrue, isAssignableFrom); + //Silently ignore conversion errors with the try catch expression. This is mostly for testing purposes and might be removed + //in the future. + memberBindings.Add(Expression.Bind(memberInfo, Expression.TryCatch(dataFieldIsNull, + Expression.Catch(typeof(Exception), Expression.Default(memberType))))); + } + } + //Get the converter lambda expression, creating the initialization block with its parameters. + Expression> converter = Expression.Lambda>( + Expression.Block(parameters.Values, Expression.MemberInit(instantiate, memberBindings)), dataRow); + //Use expression reducer to reduce the conversion lambda expression, then compile and return the delegate. + converter = (Expression>)new ExpressionReducer().Visit(converter); + value = converters.GetOrAdd(new HashSet(columnNames), converter.Compile()); + } + return value; + } + //Attempts to get a member info object from the entity type the converter is converting to, returning true if one is found. + private static bool TryGetMemberInfo(string memberName, bool caseSensitive, out MemberInfo memberInfo) + { + BindingFlags flags = BindingFlags.Instance | BindingFlags.Public; + //Determine if the case sensitivite bool is false and add the ignore case flag to the binding search. + if (!caseSensitive) + { + flags |= BindingFlags.IgnoreCase; + } + //Determine if a property exists with the name member name and has the appropriate flags and set the out variable to this property info. + if (type.GetProperty(memberName, flags) is PropertyInfo propertyInfo) + { + memberInfo = propertyInfo; + } + //Determine if a field exists with the name member name and has the appropriate flags and set the out variable to this field info. + else if (type.GetField(memberName, flags) is FieldInfo fieldInfo) + { + memberInfo = fieldInfo; + } + //If no property or field exists in the type with the name given, assume that there is no valid target and set the out variable to null. + else + { + memberInfo = null; + } + return memberInfo != null; + } + //Creates a key value pair for the data column/column's name. This is used whenever no dictionary is provided to seed the converter's data column => property map. + private static KeyValuePair GetDataColumnKeyValuePair(DataColumn dataColumn) => + new KeyValuePair(dataColumn, dataColumn.ColumnName); + } +} \ No newline at end of file diff --git a/MJTop.Data/BWofter.Converters/EqualityComparers/StringCollectionComparer.cs b/MJTop.Data/BWofter.Converters/EqualityComparers/StringCollectionComparer.cs new file mode 100644 index 0000000..acc7f53 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/EqualityComparers/StringCollectionComparer.cs @@ -0,0 +1,112 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.EqualityComparers +{ + using System; + using System.Collections.Generic; + /// A utility class that is used to compare string collections within the cache object. + public class StringCollectionComparer : IEqualityComparer> + { + private static readonly Lazy instance = new Lazy(true); + /// Returns the singleton of . + /// The singleton of . + public static IEqualityComparer> GetInstance() => instance.Value; + /// Determines whether the specified objects are equal. + /// The first object of type of values to compare. + /// The second object of type of values to compare. + /// if the specified objects are equal; otherwise, . + public bool Equals(ICollection x, ICollection y) + { + //Determines if x and y are the same instance and returns true. + if (x == y) + { + return true; + } + //Determines if either x or y is null and returns false. + else if (x == null || y == null) + { + return false; + } + //Determines if x is a different length from y and returns false. + else if (x.Count != y.Count) + { + return false; + } + //Determines if x is a hash set and uses it as the contains target. + else if (x is HashSet) + { + //Iterate over the values in y and determine if x contains them. If any are not contained in x, return false. + foreach (string s in y) + { + if (!x.Contains(s)) + { + return false; + } + } + } + //The default option. + else + { + //Iterate over the values in x and determine if y contains them. If any are not contained in y, return false. + foreach (string s in x) + { + if (!y.Contains(s)) + { + return false; + } + } + } + return true; + } + /// Returns a hash code for the specified object. + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(ICollection obj) + { + //Determine if obj is null and return 0. + if (obj == null) + { + return 0; + } + int hashCode = 2; + //Enter an overflow unchecked areaa, iterate over the strings in the collection, and create a compound hash code using the hash code of each string. + unchecked + { + foreach (string s in obj) + { + hashCode <<= (obj?.GetHashCode() ?? 10) * 230182; + } + } + return hashCode; + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/ChangeTypeExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/ChangeTypeExpression.cs new file mode 100644 index 0000000..bae89f7 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/ChangeTypeExpression.cs @@ -0,0 +1,60 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + using System.Reflection; + + public sealed class ChangeTypeExpression : DataExpression + { + public Expression Operand { get; } + public override Type Type { get; } + private static readonly MethodInfo changeType = convertType.GetMethod(nameof(System.Convert.ChangeType), new[] { objectType, typeType }); + public ChangeTypeExpression(Expression operand, Type type) + { + Operand = operand ?? throw new ArgumentNullException(nameof(operand)); + Type = type ?? throw new ArgumentNullException(nameof(operand)); + } + public override Expression Reduce() + { + if (convertType.GetMethod($"To{Type.Name}", new[] { Operand.Type }) is MethodInfo methodInfo) + { + return Call(methodInfo, Operand); + } + else + { + return Convert(Call(changeType, Operand, Constant(Type)), Type); + } + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataColumnExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataColumnExpression.cs new file mode 100644 index 0000000..729dfb7 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataColumnExpression.cs @@ -0,0 +1,61 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class DataColumnExpression : DataExpression + { + public override Type Type => dataColumnType; + public new Expression DataTable { get; } + public Expression ColumnName { get; } + + internal DataColumnExpression(Expression dataTable, Expression columnName) + { + DataTable = dataTable ?? throw new ArgumentNullException(nameof(dataTable)); + ColumnName = columnName ?? throw new ArgumentNullException(nameof(columnName)); + } + + public override Expression Reduce() + { + if (!dataTableType.IsAssignableFrom(DataTable.Type)) + { + throw new InvalidOperationException($"{nameof(DataTable)} should return a value that inherits from {dataTableType.FullName}, {DataTable.Type.FullName} given instead."); + } + if (!intType.IsAssignableFrom(ColumnName.Type) && !stringType.IsAssignableFrom(ColumnName.Type)) + { + throw new InvalidOperationException($"{nameof(ColumnName)} should return a value that is either {intType.FullName} or {stringType.FullName}, {ColumnName.Type.FullName} given instead."); + } + return Property(Property(DataTable, nameof(System.Data.DataTable.Columns)), "Item", ColumnName); + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataExpression.cs new file mode 100644 index 0000000..1df49f1 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataExpression.cs @@ -0,0 +1,104 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Data; + using System.Linq.Expressions; + using System.Reflection; + + public abstract class DataExpression : Expression + { + public override bool CanReduce => true; + public override ExpressionType NodeType => ExpressionType.Extension; + protected static readonly Type objectType = typeof(object); + protected static readonly Type boolType = typeof(bool); + protected static readonly Type typeType = typeof(Type); + protected static readonly Type typeInfoType = typeof(TypeInfo); + protected static readonly Type dataColumnType = typeof(DataColumn); + protected static readonly Type dataTableType = typeof(DataTable); + protected static readonly Type dataRowType = typeof(DataRow); + protected static readonly Type intType = typeof(int); + protected static readonly Type stringType = typeof(string); + protected static readonly Type dataSetType = typeof(DataSet); + protected static readonly Type convertType = typeof(Convert); + + public static IsAssignableFromExpression IsAssignableFrom(Type target, Type other) => + new IsAssignableFromExpression(Constant(target), Constant(other)); + public static IsAssignableFromExpression IsAssignableFrom(Expression other, Type target) => + new IsAssignableFromExpression(Constant(target), other); + public static IsAssignableFromExpression IsAssignableFrom(Expression target, Expression other) => + new IsAssignableFromExpression(target, other); + public static DataColumnExpression DataColumn(Expression dataTable, int columnNumber) => + new DataColumnExpression(dataTable, Constant(columnNumber)); + public static DataColumnExpression DataColumn(Expression dataTable, string columnName) => + new DataColumnExpression(dataTable, Constant(columnName)); + public static DataColumnExpression DataColumn(Expression dataTable, Expression columnName) => + new DataColumnExpression(dataTable, columnName); + public static DataTableExpression DataTable(Expression dataMember, int tableNumber) => + new DataTableExpression(dataMember, Constant(tableNumber)); + public static DataTableExpression DataTable(Expression dataMember, string tableName) => + new DataTableExpression(dataMember, Constant(tableName)); + public static DataTableExpression DataTable(Expression dataMember, string tableName, string tableNamespace) => + new DataTableExpression(dataMember, Constant(tableName), Constant(tableNamespace)); + public static DataTableExpression DataTable(Expression dataMember, Expression tableName, string tableNamespace) => + new DataTableExpression(dataMember, tableName, Constant(tableNamespace)); + public static DataTableExpression DataTable(Expression dataMember, Expression tableName = null, Expression tableNamespace = null) => + new DataTableExpression(dataMember, tableName, tableNamespace); + public static DataTypeExpression DataType(Expression dataColumn) => + new DataTypeExpression(dataColumn); + public static DataFieldExpression DataField(Expression dataRow, int columnNumber) => + new DataFieldExpression(dataRow, DataColumn(DataTable(dataRow), columnNumber)); + public static DataFieldExpression DataField(Expression dataRow, string columnName) => + new DataFieldExpression(dataRow, DataColumn(DataTable(dataRow), columnName)); + public static DataFieldExpression DataField(Expression dataRow, Expression dataColumn) => + new DataFieldExpression(dataRow, dataColumn); + public static TypeIsAssignExpression TypeIsAssign(Expression left, ParameterExpression right) => + new TypeIsAssignExpression(left, right, right.Type); + public static TypeIsAssignExpression TypeIsAssign(Expression left, Expression right, Type type) => + new TypeIsAssignExpression(left, right, type); + public static TryParseExpression TryParse(Expression inParameter, ParameterExpression outParameter) => + new TryParseExpression(inParameter, outParameter, outParameter.Type); + public static TryParseExpression TryParse(Expression inParameter, Expression outParameter, Type type) => + new TryParseExpression(inParameter, outParameter, type); + public static ChangeTypeExpression ChangeType(Expression operand, Type type) => + new ChangeTypeExpression(operand, type); + public static DataFieldIsNullExpression DataFieldIsNull(Expression dataRow, int columnNumber) => + new DataFieldIsNullExpression(dataRow, DataColumn(DataTable(dataRow), columnNumber)); + public static DataFieldIsNullExpression DataFieldIsNull(Expression dataRow, string columnName) => + new DataFieldIsNullExpression(dataRow, DataColumn(DataTable(dataRow), columnName)); + public static DataFieldIsNullExpression DataFieldIsNull(Expression dataRow, Expression dataColumn) => + new DataFieldIsNullExpression(dataRow, dataColumn); + + public override abstract Expression Reduce(); + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataFieldExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataFieldExpression.cs new file mode 100644 index 0000000..cf080c5 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataFieldExpression.cs @@ -0,0 +1,59 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class DataFieldExpression : DataExpression + { + public Expression DataRow { get; } + public new Expression DataColumn { get; } + public override Type Type => objectType; + public DataFieldExpression(Expression dataRow, Expression dataColumn) + { + DataRow = dataRow ?? throw new ArgumentNullException(nameof(dataRow)); + DataColumn = dataColumn ?? throw new ArgumentNullException(nameof(dataColumn)); + } + public override Expression Reduce() + { + if (!dataRowType.IsAssignableFrom(DataRow.Type)) + { + throw new InvalidOperationException($""); + } + if (!dataColumnType.IsAssignableFrom(DataColumn.Type)) + { + throw new InvalidOperationException($""); + } + return Property(DataRow, "Item", DataColumn); + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataFieldIsNullExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataFieldIsNullExpression.cs new file mode 100644 index 0000000..c372d18 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataFieldIsNullExpression.cs @@ -0,0 +1,59 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class DataFieldIsNullExpression : DataExpression + { + public Expression DataRow { get; } + public new Expression DataColumn { get; } + public override Type Type => boolType; + public DataFieldIsNullExpression(Expression dataRow, Expression dataColumn) + { + DataRow = dataRow ?? throw new ArgumentNullException(nameof(dataRow)); + DataColumn = dataColumn ?? throw new ArgumentNullException(nameof(dataColumn)); + } + public override Expression Reduce() + { + if (!dataRowType.IsAssignableFrom(DataRow.Type)) + { + throw new InvalidOperationException($""); + } + if (!dataColumnType.IsAssignableFrom(DataColumn.Type)) + { + throw new InvalidOperationException($""); + } + return Call(DataRow, nameof(System.Data.DataRow.IsNull), Type.EmptyTypes, DataColumn); + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataTableExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataTableExpression.cs new file mode 100644 index 0000000..609947e --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataTableExpression.cs @@ -0,0 +1,86 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class DataTableExpression : DataExpression + { + public override Type Type => dataTableType; + public Expression DataMember { get; } + public Expression TableName { get; } + public Expression TableNamespace { get; } + + internal DataTableExpression(Expression dataMember, Expression tableName = null, Expression tableNamespace = null) + { + DataMember = dataMember ?? throw new ArgumentNullException(nameof(dataMember)); + TableName = tableName; + TableNamespace = tableNamespace; + } + + public override Expression Reduce() + { + if (dataTableType.IsAssignableFrom(DataMember.Type)) + { + return DataMember; + } + else if (dataRowType.IsAssignableFrom(DataMember.Type)) + { + return Property(DataMember, nameof(System.Data.DataRow.Table)); + } + else if (dataColumnType.IsAssignableFrom(DataMember.Type)) + { + return Property(DataMember, nameof(System.Data.DataColumn.Table)); + } + else if (dataSetType.IsAssignableFrom(DataMember.Type)) + { + MemberExpression tables = Property(DataMember, "Tables"); + if ((stringType.IsAssignableFrom(TableName.Type) || intType.IsAssignableFrom(TableName.Type)) && TableNamespace == null) + { + return Property(tables, "Item", TableName); + } + else if (stringType.IsAssignableFrom(TableName.Type) && stringType.IsAssignableFrom(TableNamespace.Type)) + { + return Property(tables, "Item", TableName, TableNamespace); + } + else + { + throw new InvalidOperationException($""); + } + } + else + { + throw new InvalidOperationException($""); + } + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/DataTypeExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/DataTypeExpression.cs new file mode 100644 index 0000000..5fecf31 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/DataTypeExpression.cs @@ -0,0 +1,56 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class DataTypeExpression : DataExpression + { + public override Type Type => typeType; + public new Expression DataColumn { get; } + + public DataTypeExpression(Expression dataColumn) => + DataColumn = dataColumn ?? throw new ArgumentNullException(nameof(dataColumn)); + + public override Expression Reduce() + { + if (dataColumnType.IsAssignableFrom(DataColumn.Type)) + { + return Property(DataColumn, nameof(System.Data.DataColumn.DataType)); + } + else + { + throw new InvalidOperationException($""); + } + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/ExpressionReducer.cs b/MJTop.Data/BWofter.Converters/Expressions/ExpressionReducer.cs new file mode 100644 index 0000000..2080585 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/ExpressionReducer.cs @@ -0,0 +1,45 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System.Linq.Expressions; + public sealed class ExpressionReducer : ExpressionVisitor + { + public override Expression Visit(Expression node) + { + if (node != null && !node.NodeType.HasFlag(ExpressionType.MemberInit)) + while (node.CanReduce) + node = node.Reduce(); + return base.Visit(node); + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/IsAssignableFromExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/IsAssignableFromExpression.cs new file mode 100644 index 0000000..5e92d9e --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/IsAssignableFromExpression.cs @@ -0,0 +1,71 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class IsAssignableFromExpression : DataExpression + { + public override Type Type => boolType; + public Expression TargetType { get; } + public Expression OtherType { get; } + internal IsAssignableFromExpression(Expression target, Expression other) + { + TargetType = target ?? throw new ArgumentNullException(nameof(target)); + OtherType = other ?? throw new ArgumentNullException(nameof(other)); + } + public IsAssignableFromExpression Update(Expression target, Expression other) => + new IsAssignableFromExpression(target, other); + public override Expression Reduce() + { + Expression target, + other; + if (typeType.IsAssignableFrom(TargetType.Type) || typeInfoType.IsAssignableFrom(TargetType.Type)) + { + target = TargetType; + } + else + { + target = Call(TargetType, nameof(object.GetType), Type.EmptyTypes); + } + if (typeType.IsAssignableFrom(OtherType.Type) || typeInfoType.IsAssignableFrom(OtherType.Type)) + { + other = OtherType; + } + else + { + other = Call(OtherType, nameof(object.GetType), Type.EmptyTypes); + } + return Call(TargetType, nameof(Type.IsAssignableFrom), Type.EmptyTypes, other); + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/TryParseExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/TryParseExpression.cs new file mode 100644 index 0000000..06ecb8b --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/TryParseExpression.cs @@ -0,0 +1,51 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class TryParseExpression : DataExpression + { + public Expression InParameter { get; } + public Expression OutParameter { get; } + public override Type Type => boolType; + public Type TargetType { get; } + public TryParseExpression(Expression inParameter, Expression outParameter, Type type) + { + InParameter = inParameter ?? throw new ArgumentNullException(nameof(inParameter)); + OutParameter = outParameter ?? throw new ArgumentNullException(nameof(outParameter)); + TargetType = type ?? throw new ArgumentNullException(nameof(type)); + } + public override Expression Reduce() => + Call(TargetType.GetMethod("TryParse", new[] { stringType, TargetType.MakeByRefType() }), InParameter, OutParameter); + } +} diff --git a/MJTop.Data/BWofter.Converters/Expressions/TypeIsAssignExpression.cs b/MJTop.Data/BWofter.Converters/Expressions/TypeIsAssignExpression.cs new file mode 100644 index 0000000..0071368 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Expressions/TypeIsAssignExpression.cs @@ -0,0 +1,51 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Expressions +{ + using System; + using System.Linq.Expressions; + public sealed class TypeIsAssignExpression : DataExpression + { + public Expression Left { get; } + public Expression Right { get; } + public override Type Type => boolType; + public Type TargetType { get; } + public TypeIsAssignExpression(Expression left, Expression right, Type type) + { + Left = left ?? throw new ArgumentNullException(nameof(left)); + Right = right ?? throw new ArgumentNullException(nameof(right)); + TargetType = type ?? throw new ArgumentNullException(nameof(type)); + } + public override Expression Reduce() => + NotEqual(Assign(Right, TypeAs(Left, TargetType)), Constant(null, TargetType)); + } +} diff --git a/MJTop.Data/BWofter.Converters/Extensions/DataColumnCollectionExtensions.cs b/MJTop.Data/BWofter.Converters/Extensions/DataColumnCollectionExtensions.cs new file mode 100644 index 0000000..f35fa5c --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Extensions/DataColumnCollectionExtensions.cs @@ -0,0 +1,82 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Extensions +{ + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + /// A static class that provides LINQ extensions for the type. + public static class DataColumnCollectionExtensions + { + /// Returns an of values that represents . + /// The target of the extension method. + /// of values or . + public static IEnumerable Select(this DataColumnCollection collection) => + collection.Select(dc => dc); + /// Returns an of values that represents . + /// The target of the extension method. + /// The selector of the extension method. + /// of values or . + public static IEnumerable Select(this DataColumnCollection collection, Func selector) + { + if (collection == null) throw new ArgumentNullException(nameof(collection)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + //Determines if there are no entries in the collection. If not, return an empty enumerable. Otherwise, process the selector. + if (collection.Count == 0) + { + return Enumerable.Empty(); + } + else + { + return collection.Cast().Select(selector); + } + } + /// Returns an of values that represents . + /// The target of the extension method. + /// The predicate of the extension method. + /// of values or . + public static IEnumerable Where(this DataColumnCollection collection, Func predicate) + { + if (collection == null) throw new ArgumentNullException(nameof(collection)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (collection.Count == 0) + { + return Enumerable.Empty(); + } + else + { + return collection.Cast().Where(predicate); + } + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Extensions/DictionaryStringObjectExtensions.cs b/MJTop.Data/BWofter.Converters/Extensions/DictionaryStringObjectExtensions.cs new file mode 100644 index 0000000..5a0a8fe --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Extensions/DictionaryStringObjectExtensions.cs @@ -0,0 +1,55 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Extensions +{ + using System; + using System.Collections.Generic; + using System.Data; + /// A static class that provides LINQ extensions for of keys and values. + public static class DictionaryStringObjectExtensions + { + /// Returns a that represents the columns and types of . This does not copy the values. + /// The target of the extension method. + /// with columns that match the key and type of the entries. + public static DataTable AsDataTableDefinition(this IDictionary dictionary) + { + if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); + DataTable dataTable = new DataTable(); + //Iterate over the entries in the dictionary and add a matching column to the new data table. + foreach (KeyValuePair keyValuePair in dictionary) + { + dataTable.Columns.Add(keyValuePair.Key, keyValuePair.Value?.GetType() ?? typeof(object)); + } + return dataTable; + } + } +} diff --git a/MJTop.Data/BWofter.Converters/Extensions/IEnumerableKeyValuePairExtensions.cs b/MJTop.Data/BWofter.Converters/Extensions/IEnumerableKeyValuePairExtensions.cs new file mode 100644 index 0000000..01c6183 --- /dev/null +++ b/MJTop.Data/BWofter.Converters/Extensions/IEnumerableKeyValuePairExtensions.cs @@ -0,0 +1,60 @@ +/* +BSD 3-Clause License + +Copyright (c) 2017, B. Wofter +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +namespace BWofter.Converters.Extensions +{ + using System; + using System.Collections.Generic; + using System.Linq; + /// A static class that provides LINQ extensions for of values. + public static class IEnumerableKeyValuePairExtensions + { + /// Returns a with the key type and value type , + /// initialized with the values in . + /// The key type. + /// The value type. + /// An of values that contain the initial data of the dictionary. + /// with the key type and value type . + public static Dictionary ToDictionary(this IEnumerable> dictionarySource) + { + if (dictionarySource == null) throw new ArgumentNullException(nameof(dictionarySource)); + //Instantiate a new dictionary with a number of entries equal to the length of the source, then sets gD equal to the reference of dictionary. This is to prevent extra casting operations. + Dictionary dictionary = new Dictionary(dictionarySource.Count()); + ICollection> gD = dictionary; + foreach (KeyValuePair entry in dictionarySource) + { + //Add the key value pair to gD. + gD.Add(entry); + } + return dictionary; + } + } +} diff --git a/MJTop.Data/DB.Ext.cs b/MJTop.Data/DB.Ext.cs new file mode 100644 index 0000000..d074040 --- /dev/null +++ b/MJTop.Data/DB.Ext.cs @@ -0,0 +1,401 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Collections.Specialized; +using MJTop.Data.DatabaseInfo; +using System.Reflection; +using System.Threading; +using System.Text.RegularExpressions; + +namespace MJTop.Data +{ + public partial class DB + { + #region 实体相关 + public virtual TEntity GetById(string tableName, object pkValue) where TEntity : class, new() + { + TableInfo tabInfo = Info.TableInfoDict[tableName]; + ColumnInfo colInfo = Info[tableName, tabInfo.PriKeyColName]; + string strSql = "select * from " + tableName + " where " + colInfo.ColumnName + "=" + ParameterSql(colInfo.ColumnName); + DataTable data = GetDataTable(strSql, CreateParameter(colInfo.ColumnName, pkValue, colInfo).TransArray()); + return BWofter.Converters.Data.DataTableConverter.ToEntities(data).ToList()?.FirstOrDefault(); + } + + public virtual List GetByIds(string tableName, object[] pkValues) where TEntity : class, new() + { + TableInfo tabInfo = Info.TableInfoDict[tableName]; + ColumnInfo colInfo = Info[tableName, tabInfo.PriKeyColName]; + string strSql = "select * from " + tableName + " where " + colInfo.ColumnName + " in ({0})"; + if (pkValues.Length <= 0) + { + return new List(); + } + strSql = string.Format(strSql, ParameterSql(colInfo.ColumnName).Repeater(pkValues.Length, ",")); + + List lstpara = new List(); + foreach (var value in pkValues) + { + lstpara.Add(CreateParameter(colInfo.ColumnName, value, colInfo)); + } + return BWofter.Converters.Data.DataTableConverter.ToEntities(GetDataTable(strSql, lstpara)).ToList(); + } + + public TEntity Get(string tableName, object whereParameters = null) + where TEntity : class, new() + + { + return GetList(tableName, whereParameters).FirstOrDefault(); + } + + public List GetList(string tableName, object whereParameters) + where TEntity : class, new() + + { + string strSql = "select * from " + tableName; + List lstPara = new List(); + List lstWhere = new List(); + if (whereParameters != null) + { + if (whereParameters is System.Collections.IDictionary) + { + IDictionary dict = whereParameters as IDictionary; + if (dict != null && dict.Count > 0) + { + foreach (DictionaryEntry kv in dict) + { + var parameter = CreateParameter(); + parameter.ParameterName = ParameterChar + kv.Key.ToString(); + parameter.Value = kv.Value; + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, kv.Key.ToString()]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + lstWhere.Add(kv.Key.ToString() + "=" + ParameterSql(kv.Key.ToString())); + lstPara.Add(parameter); + } + } + } + else if (whereParameters is NameValueCollection) + { + NameValueCollection nvc = whereParameters as NameValueCollection; + if (nvc != null && nvc.Count > 0) + { + foreach (var key in nvc.AllKeys) + { + var parameter = CreateParameter(); + parameter.ParameterName = ParameterChar + key; + parameter.Value = nvc[key]; + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, key]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + lstWhere.Add(key + "=" + ParameterSql(key)); + lstPara.Add(parameter); + } + } + } + else + { + Type ty = whereParameters.GetType(); + + if (ty.Name.Contains("AnonymousType")) + { + PropertyInfo[] props; + if (!Dict_Type_Props.TryGetValue(ty, out props)) + { + Dict_Type_Props[ty] = ty.GetProperties(); + props = Dict_Type_Props[ty]; + } + + if (props != null && props.Length > 0) + { + foreach (var prop in props) + { + var parameter = CreateParameter(); + parameter.ParameterName = ParameterChar + prop.Name; + parameter.Value = prop.GetValue(whereParameters, null); + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, prop.Name]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + lstWhere.Add(prop.Name + "=" + ParameterSql(prop.Name)); + lstPara.Add(parameter); + } + } + } + else + { + //其他类型 + throw new ArgumentException("不支持其他类型参数!", nameof(whereParameters)); + } + } + } + strSql = strSql + (lstWhere.Any() ? " where " : "") + string.Join(" and ", lstWhere); + DataTable data = GetDataTable(strSql, lstPara); + //return BWofter.Converters.Data.DataTableConverter.ToEntities(data).ToList(); + return data.ConvertToListObject(); + } + + public List GetList(string strSql) where TEntity : class, new() + { + DataTable data = GetDataTable(strSql); + //return BWofter.Converters.Data.DataTableConverter.ToEntities(data).ToList(); + return data.ConvertToListObject(); + } + + #endregion + + public virtual DataTable SelectTable(string joinTableName, string whereStr, string orderbyStr) + { + if (!string.IsNullOrWhiteSpace(whereStr)) + { + whereStr = Regex.Replace(whereStr, @"(\s)*(where)?(\s)*(.+)", "where 1=1 and $3$4", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + orderbyStr = Regex.Replace(orderbyStr, @"(\s)*(order)(\s)+(by)(.+)", "$5", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + else + { + throw new ArgumentNullException("orderbyStr"); + } + + string strSql = "select * from {0} {1} order by {2}"; + strSql = string.Format(strSql, joinTableName, whereStr, orderbyStr); + + return GetDataTable(strSql); + } + public virtual bool Exist(string tableName, string columnName, object columnValue) + { + return Exist(tableName, columnName, columnValue, null); + } + public virtual bool Exist(string tableName, string columnName, object columnValue, params object[] excludeValues) + { + string exist_sql = "select count(1) from " + tableName + " where " + columnName + "='" + columnValue + "' "; + if (excludeValues != null && excludeValues.Length > 0) + { + string in_sql = Script.SqlIn(columnName, excludeValues, true); + exist_sql += in_sql; + } + return Single(exist_sql, 0) > 0; + } + + public virtual bool UpSingle(string tableName, string columnName, object columnValue, object pkOrUniqueValue, string pkOrUniqueColName = "Id") + { + string upSql = string.Empty; + upSql = "update " + tableName + " set " + columnName + "=" + ParameterSql(columnName) + " where " + pkOrUniqueColName + "=" + ParameterSql(pkOrUniqueColName); + + var p1 = CreateParameter(columnName, columnValue, Info[tableName, columnName]); + var p2 = CreateParameter(pkOrUniqueColName, pkOrUniqueValue, Info[tableName, pkOrUniqueColName]); + + bool res = ExecSql(upSql, new DbParameter[] { p1, p2 }) > 0; + + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + public virtual int Delete(string tableName, string columnName, params object[] columnValues) + { + if (columnValues != null && columnValues.Length <= 0 + && !Info.IsExistColumn(tableName, columnName)) + { + return Delete(tableName, (object)columnName); + } + + if (!Info.IsExistTable(tableName)) + { + return -1; + } + + string delSql = "delete from " + tableName + " where 1=1 " + Script.SqlInByDBType(Info[tableName, columnName], columnValues, this.DBType); + int res = ExecSql(delSql); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + public virtual bool Delete(string tableName) + { + if (!Info.IsExistTable(tableName)) + { + return false; + } + string delSql = "delete from " + tableName; + //ExecSql(delSql, new DbParameter[] { CreateParameter(tableName, tableName) }); + ExecSql(delSql); + return true; + } + + public virtual int Delete(string tableName, object pkValue) + { + if (!Info.IsExistTable(tableName)) + { + return -1; + } + string delSql = string.Empty; + delSql = "delete from " + tableName + " where " + Info.TableInfoDict[tableName].PriKeyColName + "=" + ParameterSql(Info.TableInfoDict[tableName].PriKeyColName); + int res = ExecSql(delSql, new DbParameter[] { CreateParameter(Info.TableInfoDict[tableName].PriKeyColName, pkValue) }); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + /// + /// 根据表名查数据 + /// + /// 表名 + /// 排序字段+asc/desc + /// DataTable + public virtual DataTable SelectAll(string tableName, string orderbyStr = null) + { + if (!Info.IsExistTable(tableName)) + { + return null; + } + string selSql = string.Format("select * from {0} ", tableName); + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + selSql += " order by " + orderbyStr; + } + return GetDataTable(selSql); + } + + /// + /// 取表中的前top条数据 + /// + /// 表名 + /// 取多少条 + /// 排序字段+asc/desc + /// DataTable + public virtual DataTable SelectTop(string tableName, int top, string orderbyStr = null) + { + throw new NotImplementedException(DBType + "暂未支持"); + } + + /// + /// 获取数据条数 + /// + /// 表名 + /// and过滤条件 + /// 总条数 + public virtual long SelectCount(string tableName, string whereAndStr = null) + { + if (!Info.IsExistTable(tableName)) + { + return -1; + } + string selSql = string.Format("select count(1) from {0} ", tableName); + if (!string.IsNullOrWhiteSpace(whereAndStr)) + { + selSql += " where 1=1 " + whereAndStr; + } + return Scalar(selSql, 0); + } + } +} diff --git a/MJTop.Data/DB.cs b/MJTop.Data/DB.cs new file mode 100644 index 0000000..e34c21d --- /dev/null +++ b/MJTop.Data/DB.cs @@ -0,0 +1,1816 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Collections.Specialized; +using MJTop.Data.DatabaseInfo; +using System.Reflection; +using System.Threading; +using System.Text.RegularExpressions; +using System.Collections.Concurrent; + +namespace MJTop.Data +{ + public partial class DB : IDB + { + /// + /// 数据提供程序工厂 + /// + internal DbProviderFactory DBFactory + { + get; + set; + } + + /// + /// 连接字符串基类对象 + /// + public DbConnectionStringBuilder ConnectionStringBuilder { get; protected set; } + + /// + /// 数据库类型 + /// + public DBType DBType + { + get; protected set; + } + + /// + /// 数据库表列相关信息 + /// + public IDBInfo Info + { + get; + protected set; + } + + + /// + /// 超时时间 + /// + internal int CmdTimeout + { + get; + set; + } + + /// + /// 连接字符串 + /// + internal string ConnectionString { get; set; } + + #region AOP + /// + /// 执行前 + /// + public Action OnExecuting { get; set; } + + /// + /// 执行后,附带返回值 + /// + public Action OnExecuted { get; set; } + + /// + /// 报异常时 + /// + public Action OnError { get; set; } + #endregion + + /// + /// 对数据 增删改 时触发 + /// + public TableTrigger DataChangeTriggers + { + get; + internal set; + } + + #region 插入或更新排除列 设置与获取 + /// + /// 设置 插入时 要排除表列 集合 + /// + public ExcludeColumn InsertExcludeColumns + { + get; + internal set; + } + + /// + /// 获取 插入时 排除表列 集合 + /// + /// + public NameValueCollection GetInsertExcludeColumns() + { + return InsertExcludeColumns.Coll; + } + + /// + /// 设置 更新时 要排除表列 集合 + /// + public ExcludeColumn UpdateExcludeColumns + { + get; + internal set; + } + + /// + /// 获取 更新时 排除表列 集合 + /// + /// + public NameValueCollection GetUpdateExcludeColumns() + { + return InsertExcludeColumns.Coll; + } + + + public string ParameterChar + { + get + { + return DBType.ParameterChar(); + } + } + + + #endregion + + + internal string ParameterSql(string parameterName) + { + if (DBType == DBType.OracleDDTek) + { + return "?"; + } + return ParameterChar + parameterName; + } + + /// + /// 构建 参数化 对象 + /// 不同数据库 参数化 时所使用的字符:Global.ParameterCharMap + /// + /// 参数化 变量名 + /// 参数化 值 + /// 列信息 + /// + public DbParameter CreateParameter(string parameterName, object value, ColumnInfo colInfo = null) + { + DbParameter dbparameter = DBFactory.CreateParameter(); + dbparameter.ParameterName = DBType.ParameterChar() + parameterName; + dbparameter.Value = value; + + if (value == null || value == DBNull.Value) + { + dbparameter.Value = DBNull.Value; + } + else + { + DbType dbType; + if (colInfo != null) + { + dbType = colInfo.DbType; + object val = null; + val = Global.Dict_Convert_Type[dbType].Invoke(dbparameter.Value); + if (val is Exception) + { + throw val as Exception; + } + dbparameter.Value = val; + dbparameter.DbType = dbType; + } + else + { + Type tyValue = dbparameter.Value.GetType(); + if (Global.TypeMap.TryGetValue(tyValue, out dbType)) + { + dbparameter.DbType = dbType; + } + else + { + dbparameter.DbType = DbType.AnsiString; + } + } + } + return dbparameter; + } + + /// + /// 构建 参数化 对象 + /// + /// 参数化 变量名 + /// 参数化 值 + /// 数据类型 + /// 参数化的方式 + /// + public DbParameter CreateParameter(string parameterName, object value, DbType type, ParameterDirection direction = ParameterDirection.Input) + { + DbParameter dbparameter = DBFactory.CreateParameter(); + dbparameter.ParameterName = DBType.ParameterChar() + parameterName; + dbparameter.Value = value; + dbparameter.DbType = type; + dbparameter.Direction = direction; + return dbparameter; + } + + /// + /// 创建 Connection 对象 + /// + /// + public DbConnection CreateConn() + { + return CreateConn(this.ConnectionString); + } + + + #region internal ado.net的四大核心对象 + internal DbConnection CreateConn(string connectionString) + { + DbConnection _DBConn = DBFactory.CreateConnection(); + if (!string.IsNullOrWhiteSpace(connectionString)) + { + _DBConn.ConnectionString = connectionString; + } + return _DBConn; + } + + internal DbCommand CreateCmd() + { + DbCommand _DBCmd = DBFactory.CreateCommand(); + return _DBCmd; + } + + internal DbCommand CreateCmd(string commandText = null, DbConnection DbConn = null) + { + DbCommand _DBCmd = DBFactory.CreateCommand(); + if (DbConn != null) + { + _DBCmd.Connection = DbConn; + } + if (!string.IsNullOrWhiteSpace(commandText)) + { + _DBCmd.CommandText = commandText; + } + return _DBCmd; + } + + internal DbDataAdapter CreateAdapter(DbCommand dbCmd = null) + { + DbDataAdapter dbadapter = DBFactory.CreateDataAdapter(); + if (dbCmd != null) + { + dbadapter.SelectCommand = dbCmd; + } + return dbadapter; + } + + internal DbParameter CreateParameter() + { + DbParameter dbparameter = DBFactory.CreateParameter(); + return dbparameter; + } + + public void CheckTabStuct(string tableName, params string[] columnNames) + { + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentNullException("tableName", "不能为空!"); + } + + if (!Info.TableNames.Contains(tableName, StringComparer.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format("不存在该表!{0}", "[" + tableName + "]"), "tableName:" + tableName); + } + + if (columnNames != null && columnNames.Length > 0) + { + List lstAllColName = Info[tableName]; + + foreach (string columnName in columnNames) + { + if (!lstAllColName.Contains(columnName, StringComparer.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format("不存在该列!{0}", "[" + tableName + "." + columnName + "]"), "columnName:" + columnName, null); + } + } + } + } + + #endregion + + + public DB(DBType dbType, DbProviderFactory dbFactory, string connectionString) + { + this.DBType = dbType; + this.DBFactory = dbFactory; + this.ConnectionString = connectionString; + + this.ConnectionStringBuilder = DBFactory.CreateConnectionStringBuilder(); + this.ConnectionStringBuilder.ConnectionString = connectionString; + + this.InsertExcludeColumns = new ExcludeColumn(this); + this.UpdateExcludeColumns = new ExcludeColumn(this); + + this.DataChangeTriggers = new TableTrigger(this); + + {//查询数据库表列信息前,先验证数据库能否可以连接成功! + + try + { + using (DbConnection conn = CreateConn()) + { + conn.Open(); + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(null, ex); + + throw ex; + } + } + } + + internal DbCommand BuildCommand(DbConnection conn, string cmdText, int timeOut = 30, CommandType cmdType = CommandType.Text) + { + return BuildCommandByParam(conn, cmdText, null, timeOut, cmdType); + } + + static ConcurrentDictionary Dict_Type_Props = new ConcurrentDictionary(); + internal DbCommand BuildCommandByParam(DbConnection conn, string cmdText, object cmdParms, int timeOut = 60, CommandType cmdType = CommandType.Text, string tableName = null) + { + if (conn.State != ConnectionState.Open) + conn.Open(); + var cmd = conn.CreateCommand(); + cmd.CommandText = cmdText; + cmd.CommandType = cmdType; + + if (this.CmdTimeout != timeOut) + { + cmd.CommandTimeout = this.CmdTimeout; + } + else //以秒为单位)。默认为 30 秒 + { + cmd.CommandTimeout = timeOut; + } + + if (cmdParms != null) + { + if (cmdParms is DbParameter[]) + { + DbParameter[] arr = cmdParms as DbParameter[]; + cmd.Parameters.AddRange(arr); + } + else if (cmdParms is List) + { + List lstDp = cmdParms as List; + cmd.Parameters.AddRange(lstDp.ToArray()); + } + else if (cmdParms is System.Collections.IDictionary) + { + IDictionary dict = cmdParms as IDictionary; + if (dict != null && dict.Count > 0) + { + foreach (DictionaryEntry kv in dict) + { + var parameter = cmd.CreateParameter(); + parameter.ParameterName = ParameterChar + kv.Key.ToString(); + parameter.Value = kv.Value; + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, kv.Key.ToString()]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + cmd.Parameters.Add(parameter); + } + } + } + else if (cmdParms is NameValueCollection) + { + NameValueCollection nvc = cmdParms as NameValueCollection; + if (nvc != null && nvc.Count > 0) + { + foreach (var key in nvc.AllKeys) + { + var parameter = cmd.CreateParameter(); + parameter.ParameterName = ParameterChar + key; + parameter.Value = nvc[key]; + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, key]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + cmd.Parameters.Add(parameter); + } + } + } + else + { + Type ty = cmdParms.GetType(); + + if (ty.Name.Contains("AnonymousType")) + { + PropertyInfo[] props; + if (!Dict_Type_Props.TryGetValue(ty, out props)) + { + Dict_Type_Props[ty] = ty.GetProperties(); + props = Dict_Type_Props[ty]; + } + + if (props != null && props.Length > 0) + { + foreach (var prop in props) + { + var parameter = cmd.CreateParameter(); + parameter.ParameterName = ParameterChar + prop.Name; + parameter.Value = prop.GetValue(cmdParms, null); + + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + parameter.Value = DBNull.Value; + } + else + { + if (tableName != null) + { + ColumnInfo colInfo = Info[tableName, prop.Name]; + if (colInfo != null) + { + parameter.DbType = colInfo.DbType; + + parameter.Value = Global.Dict_Convert_Type[colInfo.DbType].Invoke(parameter.Value); + } + } + else + { + Type tyValue = parameter.Value.GetType(); + DbType tmpType; + if (Global.TypeMap.TryGetValue(tyValue, out tmpType)) + { + parameter.DbType = tmpType; + } + else + { + parameter.DbType = DbType.AnsiString; + } + } + } + cmd.Parameters.Add(parameter); + } + } + } + else + { + //其他类型 + } + + } + } + + if (OnExecuting != null) + { + OnExecuting.Invoke(cmd); + } + return cmd; + } + + /// + /// 尝试 连接字符串 能否 连接成功 + /// + /// + public virtual bool TryConnect() + { + try + { + using (DbConnection conn = CreateConn()) + { + conn.Open(); + } + return true; + } + catch + { + return false; + } + } + + /// + /// Sqlite3 收缩数据库(缓存数据写入到db文件中) + /// + public void Shrink() + { + ExecSql("pragma journal_mode=delete;"); + } + + + /// + /// 验证 执行语句 是否 能执行通过 + /// + /// + /// + /// + public virtual bool ValidateSql(string strSql, out Exception ex) + { + bool bResult = false; + ex = null; + DbConnection conn = null; + DbCommand cmd = null; + int res = -1; + try + { + conn = CreateConn(); + strSql = "explain " + strSql; + cmd = BuildCommand(conn, strSql); + res = cmd.ExecuteNonQuery(); + bResult = true; + } + catch (Exception e) + { + ex = e; + bResult = false; + } + finally + { + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, bResult); + } + return bResult; + } + + + public virtual int RunStoreProc(string storeProcName, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + int cnt = -1; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, storeProcName, parameters, this.CmdTimeout, CommandType.StoredProcedure); + cnt = cmd.ExecuteNonQuery(); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, cnt); + } + return cnt; + } + public virtual DataTable RunStoreProcGetDT(string storeProcName, object parameters = null) + { + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, storeProcName, parameters, this.CmdTimeout, CommandType.StoredProcedure); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + DataTable dt = null; + if (ds != null && ds.Tables.Count > 0) + { + dt = ds.Tables[0]; + dt.TableName = "data"; + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, dt); + } + return dt; + } + public virtual DataSet RunStoreProcGetDS(string storeProcName, object parameters = null) + { + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, storeProcName, parameters, this.CmdTimeout, CommandType.StoredProcedure); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, ds); + } + return ds; + } + + + + #region 基础查询 + + public virtual TRet Scalar(string strSql, TRet defRet, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + object obj = null; + TRet result; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + obj = cmd.ExecuteScalar(); + } + catch (Exception ex) + { + result = defRet; + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + } + finally + { + conn?.Close(); + } + + result = obj.ChangeType(default(TRet)); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result); + } + return result; + } + + public virtual NameValueCollection GetFirstRow(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + NameValueCollection dict = new NameValueCollection(); + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + if (myReader.HasRows) + { + myReader.Read(); + for (int j = 0; j < myReader.FieldCount; j++) + { + string columnName = myReader.GetName(j); + dict.Add(columnName, myReader[columnName] == null ? string.Empty : myReader[columnName].ToString()); + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, dict); + } + return dict; + } + + public virtual DataTable GetDataTable(string strSql, object parameters = null) + { + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + DataTable dt = null; + if (ds != null && ds.Tables.Count > 0) + { + dt = ds.Tables[0]; + dt.TableName = "data"; + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, dt); + } + return dt; + } + + public virtual List GetListTable(string strSql, object parameters = null) + { + List lstTabs = new List(); + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + if (ds != null && ds.Tables.Count > 0) + { + foreach (DataTable tb in ds.Tables) + { + lstTabs.Add(tb); + } + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, lstTabs); + } + return lstTabs; + } + + public virtual DataSet GetDataSet(string strSql, object parameters = null) + { + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, ds); + } + return ds; + } + + public virtual DbDataReader ExecReader(string commandText, object parameters = null, CommandType commandType = CommandType.Text) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, commandText, parameters, CmdTimeout, commandType); + myReader = cmd.ExecuteReader(CommandBehavior.CloseConnection); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, myReader); + } + return myReader; + } + + public virtual TRet Single(string strSql, TRet defRet, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + TRet result; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(CommandBehavior.SingleResult); + if (myReader.HasRows) + { + myReader.Read(); + // ret = myReader.GetFieldValue(0); + result = myReader.GetValue(0).ChangeType(default(TRet)); + } + else + { + result = defRet; + } + } + catch (Exception ex) + { + result = defRet; + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result); + } + return result; + } + + public virtual List> GetListDictionary(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + List> lstDict = new List>(); + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + if (myReader.HasRows) + { + while (myReader.Read()) + { + Dictionary dict = new Dictionary(); + for (int j = 0; j < myReader.FieldCount; j++) + { + string columnName = myReader.GetName(j); + dict.Add(columnName, myReader[columnName]); + } + lstDict.Add(dict); + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, lstDict); + } + return lstDict; + } + + public virtual DataTable ReadTable(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + DataTable data = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + data = new DataTable("data"); + if (myReader.HasRows) + { + bool isAddCol = false; + while (myReader.Read()) + { + DataRow dr = data.NewRow(); + for (int j = 0; j < myReader.FieldCount; j++) + { + string columnName = myReader.GetName(j); + if (!isAddCol) + { + data.Columns.Add(columnName, myReader.GetFieldType(j)); + } + + dr[columnName] = myReader[columnName]; + } + data.Rows.Add(dr); + isAddCol = true; + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, data); + } + return data; + } + + public virtual List ReadList(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + List lstVal = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + lstVal = new List(); + if (myReader.HasRows) + { + while (myReader.Read()) + { + lstVal.Add(myReader.GetFieldValue(0)); + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, lstVal); + } + return lstVal; + } + + public virtual NameValueCollection ReadNameValues(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + NameValueCollection nvc = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + nvc = new NameValueCollection(); + if (myReader.HasRows) + { + while (myReader.Read()) + { + nvc.Add((myReader.GetValue(0) ?? string.Empty).ToString(), (myReader.GetValue(1) ?? string.Empty).ToString()); + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, nvc); + } + return nvc; + } + + public virtual Dictionary ReadDictionary(string strSql, object parameters = null, IEqualityComparer comparer = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbDataReader myReader = null; + Dictionary dict = null; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + myReader = cmd.ExecuteReader(); + dict = new Dictionary(comparer); + if (myReader.HasRows) + { + while (myReader.Read()) + { + dict.Add((TKey)myReader.GetValue(0).ChangeType(typeof(TKey)), (TValue)myReader.GetValue(1).ChangeType(typeof(TValue))); + } + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + myReader?.Close(); + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, dict); + } + return dict; + } + + #endregion + + #region 执行 + + public virtual int ExecSql(string strSql, object parameters = null) + { + if (this.DBType == DBType.SQLite && !strSql.Contains("journal_mode=delete")) + { + return ExecSqlTran(strSql, parameters); + } + DbConnection conn = null; + DbCommand cmd = null; + int cnt = -1; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + cnt = cmd.ExecuteNonQuery(); + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, cnt); + } + return cnt; + } + + public int ExecSqlTran(string strSql, object parameters = null) + { + DbConnection conn = null; + DbCommand cmd = null; + DbTransaction tran = null; + int cnt = -1; + try + { + conn = CreateConn(); + cmd = BuildCommandByParam(conn, strSql, parameters); + tran = conn.BeginTransaction(); + cmd.Transaction = tran; + cnt = cmd.ExecuteNonQuery(); + tran.Commit(); + } + catch (Exception ex) + { + tran?.Rollback(); + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + } + finally + { + conn?.Close(); + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, cnt); + } + return cnt; + } + + public virtual int ExecSqlTran(params string[] sqlCmds) + { + if (sqlCmds == null || sqlCmds.Length == 0) + { + return -1; + } + + DbConnection conn = null; + DbCommand cmd = null; + DbTransaction tran = null; + int cnt = 0; + try + { + conn = CreateConn(); + conn.Open(); + tran = conn.BeginTransaction(); + cmd = conn.CreateCommand(); + cmd.Transaction = tran; + cmd.CommandTimeout = this.CmdTimeout; + + for (int n = 0; n < sqlCmds.Length; n++) + { + string strsql = sqlCmds[n]; + if (strsql.Trim().Length > 1) + { + cmd.CommandText = strsql; + + if (OnExecuting != null) + { + OnExecuting.Invoke(cmd); + } + int res = cmd.ExecuteNonQuery(); + cnt += res; + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, res); + } + } + } + tran.Commit(); + + } + catch (Exception ex) + { + tran.Rollback(); + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + } + finally + { + conn?.Close(); + } + return cnt; + } + public virtual int ExecSqlTran(List>> strSqlList) + { + if (strSqlList == null || strSqlList.Count <= 0) + { + return -1; + } + DbConnection conn = null; + DbCommand cmd = null; + DbTransaction tran = null; + int cnt = 0; + try + { + conn = CreateConn(); + conn.Open(); + tran = conn.BeginTransaction(); + cmd = conn.CreateCommand(); + cmd.Transaction = tran; + cmd.CommandTimeout = this.CmdTimeout; + + for (int n = 0; n < strSqlList.Count; n++) + { + var kv = strSqlList[n]; + if (!string.IsNullOrWhiteSpace(kv.Key)) + { + cmd.CommandText = kv.Key; + + if (kv.Value != null && kv.Value.Count > 0) + { + cmd.Parameters.AddRange(kv.Value.ToArray()); + } + + if (OnExecuting != null) + { + OnExecuting.Invoke(cmd); + } + int res = cmd.ExecuteNonQuery(); + cnt += res; + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, res); + } + } + } + tran.Commit(); + } + catch (Exception ex) + { + tran.Rollback(); + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + } + finally + { + conn?.Close(); + } + return cnt; + } + + + + #endregion + + + public virtual int BulkInsert(string tableName, DataTable data,int batchSize = 200000, int timeout = 60) + { + throw new NotImplementedException("该方法未支持!(" + this.DBType + ")"); + } + + public virtual int BulkInsert(string tableName, DbDataReader reader, int batchSize = 200000, int timeout = 60) + { + throw new NotImplementedException("该方法未支持!(" + this.DBType + ")"); + } + + public KeyValuePair> InsertScript
(DT data, string tableName, params string[] excludeColNames) + { + if (data == null) + throw new ArgumentException("data", "不能为null!"); + CheckTabStuct(tableName); + + StringBuilder sb_beforeSQL = new StringBuilder(); + sb_beforeSQL.Append("insert into " + tableName + " ("); + StringBuilder sb_afterSQl = new StringBuilder(); + sb_afterSQl.Append(") values ("); + + string insert_sql = string.Empty; + + List lstPara = new List(); + var lstColName = Info[tableName]; + + IEnumerable enumNames = new List(); + if (TypeInfo
.IsAnonymousType) + { + #region 匿名对象 + var curNames = TypeInfo
.PropNames; + enumNames = curNames.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + string[] columnNames = InsertExcludeColumns.Coll.GetValues(tableName); + + if (columnNames != null && columnNames.Length > 0) + { + enumNames = enumNames.Except(columnNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + if (colInfo.IsIdentity) + { + continue; + } + object obj = TypeInfo
.PropMapping[colName].GetValue(data, null); + if (!string.IsNullOrWhiteSpace(colInfo.DefaultVal) + && (obj == null || string.IsNullOrEmpty(obj.ToString()))) + { + continue; + } + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + sb_beforeSQL.Append(colName + ","); + sb_afterSQl.Append(ParameterSql(colName) + ","); + } + #endregion + } + else if (TypeInfo
.IsNameValueColl) + { + #region NameValueCollection + var curNames = (data as NameValueCollection).AllKeys; + enumNames = curNames.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + if (colInfo.IsIdentity) + { + continue; + } + object obj = (data as NameValueCollection)[colName]; + if (!string.IsNullOrWhiteSpace(colInfo.DefaultVal) + && (obj == null || string.IsNullOrEmpty(obj.ToString()))) + { + continue; + } + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + + sb_beforeSQL.Append(colName + ","); + sb_afterSQl.Append(ParameterSql(colName) + ","); + } + #endregion + } + else if (TypeInfo
.IsDict) + { + #region IDictionary + var curNames = (data as IDictionary).Keys; + + string[] arrKeys = new string[curNames.Count]; + + curNames.CopyTo(arrKeys, 0); + + enumNames = arrKeys.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + if (colInfo.IsIdentity) + { + continue; + } + object obj = (data as IDictionary)[colName]; + if (!string.IsNullOrWhiteSpace(colInfo.DefaultVal) + && (obj == null || string.IsNullOrEmpty(obj.ToString()))) + { + continue; + } + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + + sb_beforeSQL.Append(colName + ","); + sb_afterSQl.Append(ParameterSql(colName) + ","); + } + #endregion + } + else + { + throw new ArgumentException("未知数据类型插入!", "data"); + } + + if (!enumNames.Any()) + { + throw new ArgumentException("至少有1列的值,才能够插入!", "data"); + } + + insert_sql = sb_beforeSQL.ToString().TrimEnd(',') + sb_afterSQl.ToString().TrimEnd(',') + ")"; + + return new KeyValuePair>(insert_sql, lstPara); + } + + public virtual bool Insert
(DT data, string tableName, params string[] excludeColNames) + { + var kv = InsertScript(data, tableName, excludeColNames); + bool res = false; + res = ExecSql(kv.Key, kv.Value) > 0; + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + internal virtual Ret InsertGet(DT data, string tableName, params string[] excludeColNames) + { + var kv = InsertScript(data, tableName, excludeColNames); + string insert_Sql = kv.Key + ";" + Script.IdentitySql(DBType, tableName); + var res = Single(insert_Sql, default(Ret), kv.Value); + + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + public virtual int InsertGetInt
(DT data, string tableName, params string[] excludeColNames) + { + return InsertGet(data, tableName, excludeColNames); + } + + public virtual long InsertGetLong
(DT data, string tableName, params string[] excludeColNames) + { + return InsertGet(data, tableName, excludeColNames); + } + + public KeyValuePair> UpdateScript
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames) + { + if (data == null) + throw new ArgumentException("data", "不能为null!"); + CheckTabStuct(tableName, pkOrUniqueColName); + + string parameterChar = DBType.ParameterChar(); + + StringBuilder sb_beforeSQL = new StringBuilder(); + sb_beforeSQL.Append("update " + tableName + " set "); + + + string update_sql = string.Empty; + DbParameter paraPKOrUnique = null; + string paraPKOrUniqueName = string.Empty; + + List lstPara = new List(); + var lstColName = Info[tableName]; + + IEnumerable enumNames = new List(); + if (TypeInfo
.IsAnonymousType) + { + #region 匿名对象 + var curNames = TypeInfo
.PropNames; + enumNames = curNames.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + object obj = TypeInfo
.PropMapping[colName].GetValue(data, null); + if (!colName.Equals(pkOrUniqueColName, StringComparison.OrdinalIgnoreCase)) + { + sb_beforeSQL.Append(colName + "=" + ParameterSql(colName) + ","); + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + } + else + { + paraPKOrUnique = CreateParameter(colName, obj, colInfo); + paraPKOrUniqueName = colName; + } + } + #endregion + + } + else if (TypeInfo
.IsNameValueColl) + { + #region NameValueCollection + var curNames = (data as NameValueCollection).AllKeys; + enumNames = curNames.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + object obj = (data as NameValueCollection)[colName]; + if (!colName.Equals(pkOrUniqueColName, StringComparison.OrdinalIgnoreCase)) + { + sb_beforeSQL.Append(colName + "=" + ParameterSql(colName) + ","); + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + } + else + { + paraPKOrUnique = CreateParameter(colName, obj, colInfo); + paraPKOrUniqueName = colName; + } + } + #endregion + } + else if (TypeInfo
.IsDict) + { + #region IDictionary + var curNames = (data as IDictionary).Keys; + + string[] arrKeys = new string[curNames.Count]; + + curNames.CopyTo(arrKeys, 0); + + enumNames = arrKeys.Intersect(lstColName, StringComparer.OrdinalIgnoreCase); + + if (excludeColNames != null && excludeColNames.Length > 0) + { + enumNames = enumNames.Except(excludeColNames, StringComparer.OrdinalIgnoreCase); + } + + foreach (var colName in enumNames) + { + ColumnInfo colInfo = Info[tableName, colName]; + object obj = (data as IDictionary)[colName]; + if (!colName.Equals(pkOrUniqueColName, StringComparison.OrdinalIgnoreCase)) + { + sb_beforeSQL.Append(colName + "=" + ParameterSql(colName) + ","); + DbParameter dp = CreateParameter(colName, obj, colInfo); + lstPara.Add(dp); + } + else + { + paraPKOrUnique = CreateParameter(colName, obj, colInfo); + paraPKOrUniqueName = colName; + } + } + #endregion + } + else + { + throw new ArgumentException("未知数据类型插入!", "data"); + } + + if (!enumNames.Any()) + { + throw new ArgumentException("至少有1列的值才能够更新!", "data"); + } + + update_sql = sb_beforeSQL.ToString().TrimEnd(',') + (" where " + paraPKOrUniqueName + "=" + ParameterSql(pkOrUniqueColName)); + lstPara.Add(paraPKOrUnique); + return new KeyValuePair>(update_sql, lstPara); + } + + public virtual bool Update
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames) + { + var kv = UpdateScript(data, tableName, pkOrUniqueColName, excludeColNames); + var res = ExecSql(kv.Key, kv.Value) > 0; + + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + /// + /// 保存策略: + /// 1.如果表没有设置主键,则进行的是插入操作 + /// 2.如果表的主键是自增,如果data中主键有值,则进行更新操作,没有值则进行插入操作 + /// 3.如果表有主键,不是自增,则从数据查询,存在则更新,不存在则插入。注:如果主键列的值为空,则报异常! + /// + /// + /// + /// + /// + /// + /// + private KeyValuePair> SaveScript
(DT data, string tableName, out SaveType saveType, string pkOrUniqueColName = "Id", params string[] excludeColNames) + { + saveType = SaveType.Insert; + if (data == null) + throw new ArgumentException("data", "不能为null!"); + CheckTabStuct(tableName, pkOrUniqueColName); + + var tableInfo = Info.TableInfoDict[tableName]; + + if (tableInfo.PriKeyType == PrimaryKeyType.UNKNOWN) + { + return InsertScript
(data, tableName, pkOrUniqueColName); + } + else + { + object pkOrUniqueColValue = null; + + if (TypeInfo
.IsAnonymousType) + { + PropertyInfo pkOrUniqueColPy; + if (TypeInfo
.PropMapping.TryGetValue(pkOrUniqueColName, out pkOrUniqueColPy)) + { + pkOrUniqueColValue = pkOrUniqueColPy.GetValue(data, null); + } + } + else if (TypeInfo
.IsNameValueColl) + { + var nvc = (data as NameValueCollection); + pkOrUniqueColValue = nvc[pkOrUniqueColName]; + } + else if (TypeInfo
.IsDict) + { + var dict = (data as IDictionary); + dict.TryGetValue(pkOrUniqueColName, out pkOrUniqueColValue); + } + else + { + throw new ArgumentException("未知数据类型插入!", "data"); + } + + if (tableInfo.PriKeyType == PrimaryKeyType.AUTO) + { + if (pkOrUniqueColValue == null || string.IsNullOrWhiteSpace(pkOrUniqueColValue.ToString())) + { + saveType = SaveType.Insert; + return InsertScript
(data, tableName, pkOrUniqueColName); + } + else + { + saveType = SaveType.Update; + return UpdateScript
(data, tableName, pkOrUniqueColName, excludeColNames); + } + } + else//PrimaryKeyType.SET + { + if (pkOrUniqueColValue == null || string.IsNullOrWhiteSpace(pkOrUniqueColValue.ToString())) + { + throw new ArgumentException("主键列的值不能为空![" + pkOrUniqueColName + "]"); + } + + if (!Exist(tableName, pkOrUniqueColName, pkOrUniqueColValue, null)) + { + saveType = SaveType.Insert; + //return InsertScript
(data, tableName, pkOrUniqueColName); + return InsertScript
(data, tableName); + } + else + { + saveType = SaveType.Update; + return UpdateScript
(data, tableName, pkOrUniqueColName, excludeColNames); + } + } + } + } + + /// + /// 保存策略: + /// 1.如果表没有设置主键,则进行的是插入操作 + /// 2.如果表的主键是自增,如果data中主键有值,则进行更新操作,没有值则进行插入操作 + /// 3.如果表有主键,不是自增,则从数据查询,存在则更新,不存在则插入。注:如果主键列的值为空,则报异常! + /// + /// + /// + /// + /// + /// + /// + public virtual KeyValuePair Save
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames) + { + SaveType saveType; + var kv = SaveScript(data, tableName, out saveType, pkOrUniqueColName, excludeColNames); + bool res = ExecSql(kv.Key, kv.Value) > 0; + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return new KeyValuePair(saveType, res); + } + + + + public virtual KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr) + { + long totalCount; + DataTable data = GetDataTableByPager(currentPage, pageSize, selColumns, joinTableName, whereStr, orderbyStr, out totalCount); + return new KeyValuePair(data, totalCount); + } + + protected virtual DataTable GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr, out long totalCount) + { + throw new NotImplementedException(DBType + "暂未支持"); + } + + public virtual bool DeleteAll(string tableName) + { + string strSql = "delete from " + ParameterSql(tableName); + ExecSql(strSql, CreateParameter(tableName, tableName).TransArray()); + return true; + } + } +} diff --git a/MJTop.Data/DBExtend.cs b/MJTop.Data/DBExtend.cs new file mode 100644 index 0000000..4806ecf --- /dev/null +++ b/MJTop.Data/DBExtend.cs @@ -0,0 +1,457 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Reflection; + +namespace MJTop.Data +{ + public partial class DBExtend + { + + #region 将集合实体转为DataTable + + /// + /// 将集合实体转为DataTable + /// + /// 实体类型 + /// 集合 + /// + public static DataTable EntityToDataTable(IEnumerable lstEntity) + where T:class,new() + { + return EntityToDataTable(lstEntity, null); + } + + /// + /// 将集合实体转为DataTable + /// + /// 实体类型 + /// 集合 + /// DataTable表名 + /// + public static DataTable EntityToDataTable(IEnumerable lstEntity, string tableName) + where T : class, new() + { + DataTable data = new DataTable(); + Type ty = typeof(T); + if (!string.IsNullOrWhiteSpace(tableName)) + { + data.TableName = tableName; + } + else + { + data.TableName = ty.Name; + } + var props = ty.GetProperties(); + + foreach (var prop in props) + { + data.Columns.Add(prop.Name, prop.PropertyType); + } + foreach (var entity in lstEntity) + { + DataRow dr = data.NewRow(); + foreach (DataColumn dc in data.Columns) + { + var prop = ty.GetProperty(dc.ColumnName); + var value = prop.GetValue(entity, null); + dr[dc.ColumnName] = value; + } + data.Rows.Add(dr); + } + return data; + } + + #endregion + + #region DataTable =》 GetCreateSqlScript + + /// + /// 根据DataTable获取创建Sql语句 + /// + /// DataTable + /// 表名 + /// 数据库类型 + /// 创建表的Sql脚本 + public static string GetCreateSqlScript(DataTable data, string tableName, DBType DBType = DBType.SqlServer) + { + if (data == null) + { + throw new ArgumentException("数据表不能为null!", "data"); + } + data.TableName = tableName; + return GetCreateSqlScript(data, DBType); + } + + /// + /// 根据集合实体得到 Sql创建表脚本 + /// + /// 实体类型 + /// 集合 + /// 表名 + /// 数据库类型 + /// + public static string GetCreateSqlScript(IEnumerable lstEntity, string tableName, DBType DBType = DBType.SqlServer) + where T : class, new() + { + DataTable data = EntityToDataTable(lstEntity, tableName); + return GetCreateSqlScript(data, DBType); + } + + #region private + /// + /// 获取列的值的最大小数点占用位数 + /// + /// DataTable + /// + private static Dictionary GetColDecimals(DataTable data) + { + Dictionary dictDecimals = new Dictionary(); + List lstTy = new List() + { + typeof(double), + typeof(decimal), + typeof(float) + }; + var rowColl = data.AsEnumerable(); + var lstCol = data.Columns.ToArray().Where(t => lstTy.Contains(t.DataType));//获取小数类型的列 + foreach (DataColumn dc in lstCol) + { + long lngTemp; + var currArr = rowColl.Select(t => t[dc.ColumnName].ToString()); //得到当前列的 ToString() + currArr = currArr.Where(t => t.Length > 0 && !long.TryParse(t, out lngTemp));//不是空字符串,并且Parse失败则才算是真正的小数,过滤 小数点后都是0的数值 + if (currArr.Any()) + { + var currArrInt = currArr.Select(t => (Regex.Replace(t, @"(\d+)\.(\d+)", "$2", RegexOptions.Compiled)).Length); + int decimals = currArrInt.Max(); + dictDecimals[dc.ColumnName] = decimals; + } + } + return dictDecimals; + } + + /// + /// 根据DataTable获取创建Sql语句 + /// + /// DataTable + /// 数据库类型 + /// 创建表的Sql脚本 + private static string GetCreateSqlScript(DataTable data, DBType DBType = DBType.SqlServer) + { + if (data == null) + { + throw new ArgumentException("数据表不能为null!", "data"); + } + if (string.IsNullOrWhiteSpace(data.TableName)) + { + throw new ArgumentException("表名不能为空!"); + } + return GetCreateSqlScript(data, data.Columns, DBType); + } + + /// + /// 根据 表名,DataColumn[]获取创建Sql语句 + /// + /// 表名 + /// 所有列 + /// 数据库类型 + /// 创建表的Sql脚本 + private static string GetCreateSqlScript(DataTable data, DataColumnCollection dcs, DBType DBType = DBType.SqlServer) + { + //获取主键 + DataColumn[] primaryKey = data.PrimaryKey; + if (dcs == null) + { + throw new ArgumentException("列集合不能为null!"); + } + if (dcs.Count <= 0) + { + throw new ArgumentException("列集合个数必须大于0!"); + } + Dictionary dictDecimals = GetColDecimals(data); + + StringBuilder sbSql = new StringBuilder(); + sbSql.AppendFormat("create table {0}", data.TableName); + sbSql.Append("("); + for (int j = 0; j < dcs.Count; j++) + { + var dc = dcs[j]; + string colSqlType = GetColSqlType(dc, DBType, dictDecimals); + if (dc.AutoIncrement) //是自增列 + { + colSqlType += " identity(" + dc.AutoIncrementSeed + "," + dc.AutoIncrementStep + ") "; + } + if (primaryKey.Contains(dc)) + { + colSqlType += " primary key "; + } + colSqlType = colSqlType + ((j != dcs.Count - 1) ? "," : ""); + sbSql.Append(colSqlType); + } + sbSql.Append(")"); + string sql = sbSql.ToString(); + return sql; + } + + /// + /// 返回Sql列类型 + /// + /// 列 + /// 数据库类型 + /// 列的值的最大小数点占用位数 + /// Sql列类型 + private static string GetColSqlType(DataColumn dc, DBType DBType, Dictionary dictDecimals) + { + string res = string.Empty; + string columnName = dc.ColumnName; + Type typecol = Nullable.GetUnderlyingType(dc.DataType) ?? dc.DataType; + switch (typecol.FullName) + { + case "System.Guid": + if (DBType == DBType.SqlServer) + { + res = columnName + " uniqueidentifier"; + } + else + { + res = columnName + " char(36)"; + } + break; + case "System.Boolean": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + res = columnName + " bit"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " number(1,0)"; + } + else if (DBType == DBType.SQLite) + { + res = columnName + " integer"; + } + break; + case "System.Int32": + case "System.Int64": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + res = columnName + " bigint"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " number(38,0)"; + } + else if (DBType == DBType.SQLite) + { + res = columnName + " integer"; + } + break; + case "System.Decimal": + case "System.Double": + case "System.Single": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + if (dictDecimals != null && dictDecimals.ContainsKey(dc.ColumnName)) + { + res = columnName + " decimal(38," + dictDecimals[dc.ColumnName] + ")"; + } + else + { + res = columnName + " bigint"; + } + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + if (dictDecimals != null && dictDecimals.ContainsKey(dc.ColumnName)) + { + res = columnName + " number(38," + dictDecimals[dc.ColumnName] + ")"; + } + else + { + res = columnName + " number(38,0)"; + } + + } + else if (DBType == DBType.SQLite) + { + if (dictDecimals != null && dictDecimals.ContainsKey(dc.ColumnName)) + { + res = columnName + " real(38," + dictDecimals[dc.ColumnName] + ")"; + } + else + { + res = columnName + " integer"; + } + } + break; + case "System.DateTime": + if (DBType == DBType.SqlServer || DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " datetime"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " date"; + } + break; + case "System.Byte[]": + //先把数据存进去,使用max + if (DBType == DBType.SqlServer) + { + res = columnName + " varbinary(max)"; + } + else if (DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " blob"; + } + break; + default: + //先把数据存进去,使用max + if (DBType == DBType.SqlServer) + { + res = columnName + " nvarchar(max)"; + } + else if (DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " text"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " nclob"; + } + break; + } + + return res; + } + + #endregion + + #endregion + + #region 反射对象类型的属性生成创建脚本 + public static string GetCreateSqlScript(Type ty, DBType DBType = DBType.SqlServer) + { + var props = ty.GetProperties(); + StringBuilder sbSql = new StringBuilder(); + sbSql.AppendFormat("create table {0}", ty.Name); + sbSql.Append("("); + for (int j = 0; j < props.Length; j++) + { + PropertyInfo pInfo = props[j]; + string colType = GetColTypeByProperty(pInfo, DBType); + sbSql.Append(colType + " " + ((j == props.Length - 1) ? "" : ",")); + } + sbSql.Append(")"); + return sbSql.ToString(); + } + private static string GetColTypeByProperty(PropertyInfo pInfo, DBType DBType) + { + string res = string.Empty; + string columnName = pInfo.Name; + Type typecol = Nullable.GetUnderlyingType(pInfo.PropertyType) ?? pInfo.PropertyType; + switch (typecol.FullName) + { + case "System.Guid": + if (DBType == DBType.SqlServer) + { + res = columnName + " uniqueidentifier"; + } + else + { + res = columnName + " char(36)"; + } + break; + case "System.Boolean": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + res = columnName + " bit"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " number(1,0)"; + } + else if (DBType == DBType.SQLite) + { + res = columnName + " integer"; + } + break; + case "System.Int32": + case "System.Int64": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + res = columnName + " bigint"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " number(38,0)"; + } + else if (DBType == DBType.SQLite) + { + res = columnName + " integer"; + } + break; + case "System.Decimal": + case "System.Double": + case "System.Single": + if (DBType == DBType.SqlServer || DBType == DBType.MySql) + { + res = columnName + " decimal(38," + 2 + ")"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " number(38,," + 2 + ")"; + + } + else if (DBType == DBType.SQLite) + { + res = columnName + " real(38," + 2 + ")"; + } + break; + case "System.DateTime": + if (DBType == DBType.SqlServer || DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " datetime"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " date"; + } + break; + case "System.Byte[]": + //先把数据存进去,使用max + if (DBType == DBType.SqlServer) + { + res = columnName + " varbinary(max)"; + } + else if (DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " blob"; + } + break; + default: + //先把数据存进去,使用max + if (DBType == DBType.SqlServer) + { + res = columnName + " nvarchar(max)"; + } + else if (DBType == DBType.MySql || DBType == DBType.SQLite) + { + res = columnName + " text"; + } + else if (DBType == DBType.OracleDDTek || DBType == DBType.Oracle) + { + res = columnName + " nclob"; + } + break; + } + + return res; + } + #endregion + + } +} diff --git a/MJTop.Data/DBFactotry.cs b/MJTop.Data/DBFactotry.cs new file mode 100644 index 0000000..64e59fa --- /dev/null +++ b/MJTop.Data/DBFactotry.cs @@ -0,0 +1,142 @@ +using DDTek.Oracle; +using MySql.Data.MySqlClient; +using Npgsql; +using Oracle.ManagedDataAccess.Client; +using MJTop.Data.Database; +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Data.SQLite; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IBM.Data.DB2; +using System.Data.Common; + +namespace MJTop.Data +{ + internal class DBFactory + { + internal static DB CreateInstance(DBType dbType, string connectionString, int cmdTimeOut) + { + switch (dbType) + { + case DBType.SqlServer: + return new SqlServerDB(dbType, SqlClientFactory.Instance, connectionString,cmdTimeOut); + case DBType.MySql: + return new MySqlDB(dbType, MySqlClientFactory.Instance, connectionString, cmdTimeOut); + case DBType.Oracle: + return new OracleDB(dbType, OracleClientFactory.Instance, connectionString, cmdTimeOut); + case DBType.OracleDDTek: + return new OracleDDTekDB(dbType, OracleFactory.Instance, connectionString, cmdTimeOut); + case DBType.PostgreSql: + return new PostgreSqlDB(dbType, NpgsqlFactory.Instance, connectionString, cmdTimeOut); + case DBType.SQLite: + return new SQLiteDB(dbType, SQLiteFactory.Instance, connectionString, cmdTimeOut); + case DBType.DB2: + return new DB2DDTekDB(dbType, DB2Factory.Instance, connectionString, cmdTimeOut); + default: + throw new ArgumentException("未支持的数据库类型!"); + } + } + + internal static void TryConnect(DBType dbType, string connectionString, out List dbNames) + { + dbNames = new List(); + DbConnection conn = null; + DbCommand cmd = null; + var dbSql = string.Empty; + switch (dbType) + { + case DBType.SqlServer: + conn = SqlClientFactory.Instance.CreateConnection(); + cmd = SqlClientFactory.Instance.CreateCommand(); + cmd.CommandText = "select name from sys.sysdatabases where name not in ('master','tempdb','model','msdb') Order By name asc"; + cmd.Connection = conn; + break; + case DBType.MySql: + conn = MySqlClientFactory.Instance.CreateConnection(); + cmd = MySqlClientFactory.Instance.CreateCommand(); + cmd.CommandText = "select schema_name from information_schema.SCHEMATA where schema_name not in ('information_schema','performance_schema','mysql','sys') order by schema_name asc"; + cmd.Connection = conn; + break; + case DBType.Oracle: + conn = OracleClientFactory.Instance.CreateConnection(); + cmd = OracleClientFactory.Instance.CreateCommand(); + //cmd.CommandText = ""; + cmd.Connection = conn; + break; + case DBType.OracleDDTek: + conn = OracleFactory.Instance.CreateConnection(); + cmd = OracleFactory.Instance.CreateCommand(); + //cmd.CommandText = ""; + cmd.Connection = conn; + break; + case DBType.PostgreSql: + conn = NpgsqlFactory.Instance.CreateConnection(); + cmd = NpgsqlFactory.Instance.CreateCommand(); + cmd.CommandText = "select datname from pg_database where datistemplate = false and datname not in ('postgres') order by oid desc"; + cmd.Connection = conn; + break; + case DBType.SQLite: + conn = SQLiteFactory.Instance.CreateConnection(); + cmd = SQLiteFactory.Instance.CreateCommand(); + //cmd.CommandText = ""; + cmd.Connection = conn; + break; + case DBType.DB2: + conn = DB2Factory.Instance.CreateConnection(); + cmd = DB2Factory.Instance.CreateCommand(); + //cmd.CommandText = ""; + cmd.Connection = conn; + break; + default: + throw new ArgumentException("未支持的数据库类型!"); + } + + + try + { + conn.ConnectionString = connectionString; + conn.Open(); + + if (conn is DDTek.Oracle.OracleConnection oraConn) + { + dbNames.Add(oraConn.ServiceName); + } + else + { + dbNames.Add(conn.Database); + } + + if (cmd != null && !string.IsNullOrWhiteSpace(cmd.CommandText)) + { + try + { + var reader = cmd.ExecuteReader(); + if (reader.HasRows) + { + while (reader.Read()) + { + var name = reader.GetFieldValue(0); + if (!dbNames.Contains(name)) + { + dbNames.Add(name); + } + } + } + } + catch (Exception ex) + { + + } + } + } + finally + { + cmd?.Dispose(); + conn?.Close(); + } + } + } +} diff --git a/MJTop.Data/DBMgr.cs b/MJTop.Data/DBMgr.cs new file mode 100644 index 0000000..87b55ad --- /dev/null +++ b/MJTop.Data/DBMgr.cs @@ -0,0 +1,211 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Reflection; +using System.Data.SqlClient; +using MySql.Data.MySqlClient; +using Oracle.ManagedDataAccess.Client; +using System.Configuration; +using System.Text.RegularExpressions; +using System.Data.SQLite; +using DDTek.Oracle; +using Npgsql; +using System.Diagnostics; + +namespace MJTop.Data +{ + /// + /// 数据库连接管理 + /// + public static partial class DBMgr + { + static DBMgr() + { + InitDLL(); + } + static void InitDLL() + { + AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad; + // 框架加载dll失败后执行,手动加载dll + AppDomain.CurrentDomain.AssemblyResolve += (sender, senderArgs) => + { + // 当前程序集 + var executingAssembly = Assembly.GetExecutingAssembly(); + // 当前程序集名称 + var assemblyName = new AssemblyName(executingAssembly.FullName).Name; + // dll名称 + var dllName = new AssemblyName(senderArgs.Name).Name; + // 待加载dll路径,指向当前程序集资源文件中dll路径。* 根据程序结构调整,使其正确指向dll + var dllUri = assemblyName + ".lib." + dllName + ".dll"; + // 加载dll + var resourceStream = executingAssembly.GetManifestResourceStream(dllUri); + if (resourceStream == null) + { + if (!dllName.EndsWith(".resources")) + { + throw new ArgumentException(dllName + ".dll" + "未能引用!"); + } + else + { + Trace.WriteLine(dllName + ",未能找到,未能引用!"); + } + } + if (resourceStream != null) + { + using (resourceStream) + { + var assemblyData = new Byte[resourceStream.Length]; + resourceStream.Read(assemblyData, 0, assemblyData.Length); + return Assembly.Load(assemblyData); //加载dll + } + } + return null; + }; + } + + private static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) + { + + } + + /// + /// 连接数据库 + /// + /// 数据库类型(连接字符串默认:ConfigurationManager.ConnectionStrings[dbType.ToString()].ConnectionString) + /// 数据库操作实例对象 + public static DB UseDB(DBType dbType, int cmdTimeOut = 30) + { + string connectionString = string.Empty; + + if (ConfigurationManager.ConnectionStrings[dbType.ToString()] != null) + { + connectionString = ConfigurationManager.ConnectionStrings[dbType.ToString()].ConnectionString; + } + else + { + throw new ArgumentNullException("connectionString", dbType.ToString() + "的connectionString不能为空!"); + } + + //处理 如果Sqlite 连接字符串只提供 路径的情况 + if (dbType == DBType.SQLite + && Regex.IsMatch(connectionString, @"^(\w):\\(.*)(.+\.db)$", + RegexOptions.IgnoreCase | RegexOptions.Compiled)) + { + connectionString = string.Format("Data Source={0};Pooling=True;BinaryGUID=True;Enlist=N;Synchronous=Off;Journal Mode=WAL;Cache Size=5000;", connectionString); + } + + return DBFactory.CreateInstance(dbType, connectionString, cmdTimeOut); + } + + /// + /// 连接数据库 + /// + /// 数据库类型 + /// 连接字符串 + /// 执行超时时间(单位:秒) + /// 数据库操作实例对象 + public static DB UseDB(DBType dbType, string connectionString, int cmdTimeOut = 30) + { + //处理 如果Sqlite 连接字符串只提供 路径的情况 + if (dbType == DBType.SQLite + && Regex.IsMatch(connectionString, @"^(.*)(.+\.db)$", + RegexOptions.IgnoreCase | RegexOptions.Compiled)) + { + connectionString = string.Format("Data Source={0};Pooling=True;BinaryGUID=True;Enlist=N;Synchronous=Off;Journal Mode=WAL;Cache Size=5000;", connectionString); + } + + return DBFactory.CreateInstance(dbType, connectionString, cmdTimeOut); + } + + /// + /// 连接数据库(拼接连接字符串) + /// + /// 数据库类型 + /// host地址 + /// SqlServer默认端口:1433,MySql默认端口:3306,SqlServer默认端口:1433,Oracle默认端口:1521,PostgreSql默认端口:5432,DB2默认端口:50000 + /// 数据库名称 + /// 用户名 + /// 密码 + /// 执行超时时间(单位:秒) + /// 数据库操作实例对象 + public static DB UseDB(DBType dbType, string server, int? port, string databBase, string uid, string pwd, int connTimeOut = 60, int cmdTimeOut = 30, string extraParam = "") + { + return DBFactory.CreateInstance(dbType, GetConnectionString(dbType, server, port, databBase, uid, pwd, connTimeOut, extraParam), cmdTimeOut); + } + + public static DB UseSqlite(string dbPath, string password = null, int cmdTimeOut = 30) + { + return DBFactory.CreateInstance(DBType.SQLite, GetConnectionString(DBType.SQLite, null, null, dbPath, null, password), cmdTimeOut); + } + + + /// + /// 获取连接字符串 + /// + /// 数据库类型 + /// host地址 + /// SqlServer默认端口:1433,MySql默认端口:3306,SqlServer默认端口:1433,Oracle默认端口:1521,PostgreSql默认端口:5432,DB2默认端口:50000 + /// 数据库名称 + /// 用户名 + /// 密码 + /// 连接字符串 + public static string GetConnectionString(DBType dbType, string server, int? port, string databBase, string uid, string pwd, int connTimeOut = 60, string extraParam = "") + { + server = (server ?? string.Empty).Trim(); + databBase = (databBase ?? string.Empty).Trim(); + uid = (uid ?? string.Empty).Trim(); + + string connectionString = string.Empty; + switch (dbType) + { + case DBType.SqlServer: + connectionString = string.Format(@"server={0}{1};database={2};uid={3};pwd={4};connection timeout={5}", server, (port.HasValue ? ("," + port.Value) : string.Empty), databBase, uid, pwd, connTimeOut); + break; + case DBType.MySql: + connectionString = string.Format(@"Server={0};{1}Database={2};User={3};Password={4};OldGuids=True;connection timeout={5};" + extraParam, server, (port.HasValue ? ("Port=" + port.Value + ";") : string.Empty), databBase, uid, pwd, connTimeOut); + break; + case DBType.Oracle: + connectionString = string.Format("Data Source={0}:{1}/{2};User Id={3};password={4};Pooling=true;connection timeout={5}", server, (port ?? 1521), databBase, uid, pwd, connTimeOut); + break; + case DBType.OracleDDTek: + connectionString = string.Format("Host={0};Port={1};Service Name={2};User ID={3};Password={4};PERSIST SECURITY INFO=True;connection timeout={5}", server, (port ?? 1521), databBase, uid, pwd, connTimeOut); + break; + case DBType.PostgreSql: + connectionString = string.Format("host={0};{1}database={2};user id={3};password={4};timeout={5}", server, (port.HasValue ? ("port=" + port.Value + ";") : string.Empty), databBase, uid, pwd, connTimeOut); + break; + case DBType.SQLite: + //connectionString = string.Format("Data Source={0};Pooling=True;BinaryGUID=True;Enlist=N;Synchronous=Off;Journal Mode=WAL;Cache Size=5000;", databBase); + connectionString = string.Format("Data Source={0};", databBase); + if (!string.IsNullOrWhiteSpace(pwd)) + { + connectionString += "version=3;password=" + pwd; + } + break; + case DBType.DB2: + connectionString = string.Format(@"server={0}:{1};Database={2};Uid={3};Pwd={4};connection timeout={5}", server, (port ?? 50000), databBase, uid, pwd, connTimeOut); + break; + default: + throw new ArgumentException("未知数据库类型!"); + } + return connectionString; + } + + + /// + /// 测试连接是否成功 + /// + /// 数据库类型 + /// 连接字符串 + /// 数据库名称列表 + public static void TryConnect(DBType dbType, string connectionString, out List dbNames) + { + DBFactory.TryConnect(dbType, connectionString, out dbNames); + } + } +} diff --git a/MJTop.Data/Database/DB2DDTekDB.cs b/MJTop.Data/Database/DB2DDTekDB.cs new file mode 100644 index 0000000..88be585 --- /dev/null +++ b/MJTop.Data/Database/DB2DDTekDB.cs @@ -0,0 +1,20 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data.Database +{ + public class DB2DDTekDB:DB + { + public DB2DDTekDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new DB2DBInfo(this); + } + } +} diff --git a/MJTop.Data/Database/MySqlDB.cs b/MJTop.Data/Database/MySqlDB.cs new file mode 100644 index 0000000..f39e9d8 --- /dev/null +++ b/MJTop.Data/Database/MySqlDB.cs @@ -0,0 +1,204 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data; +using System.Text.RegularExpressions; +using System.IO; +using MySql.Data.MySqlClient; + +namespace MJTop.Data.Database +{ + public class MySqlDB : DB + { + public MySqlDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new MySqlDBInfo(this); + } + + public override KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr) + { + if (string.IsNullOrEmpty(selColumns)) + { + selColumns = "*"; + } + + if (currentPage <= 0) + { + currentPage = 1; + } + + if (pageSize <= 0) + { + pageSize = 50; + } + + string cntSQL = string.Empty, strPageSQL = string.Empty; + DataTable data = new DataTable(); + long totalCount = 0; + + if (!string.IsNullOrWhiteSpace(whereStr)) + { + whereStr = Regex.Replace(whereStr, @"(\s)*(where)?(\s)*(.+)", "and $3$4", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + orderbyStr = Regex.Replace(orderbyStr, @"(\s)*(order)(\s)+(by)(.+)", "$5", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + else + { + + throw new ArgumentNullException("orderbyStr"); + } + + cntSQL = "select count(1) from {0} where 1=1 {1}"; + cntSQL = string.Format(cntSQL, joinTableName, whereStr); + + string strSQL = "select {0} from {1} where 1=1 {2} order by {3} "; + strSQL = string.Format(strSQL, selColumns, joinTableName, whereStr, orderbyStr); + + strPageSQL = string.Format(@"SELECT * FROM ({0}) A limit {1},{2}", + strSQL, (currentPage - 1) * pageSize, pageSize); + + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + + try + { + conn = CreateConn(); + cmd = BuildCommand(conn, strPageSQL, 300); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + + if (ds.Tables.Count > 0) + { + data = ds.Tables[0]; + } + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, data); + } + + cmd.CommandText = cntSQL; + cmd.Parameters.Clear(); + + totalCount = cmd.ExecuteScalar().ChangeType(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, totalCount); + } + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + return new KeyValuePair(data, totalCount); + } + + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = "select * from {0} {1} limit 0,{2}"; + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql = string.Format(tableName, " order by " + orderbyStr, top); + } + else + { + strSql = string.Format(tableName, string.Empty, top); + } + return base.SelectTop(tableName, top, orderbyStr); + } + + ///将DataTable转换为标准的CSV + /// + /// 数据表 + /// 返回标准的CSV + private static string DataTableToCsv(DataTable table) + { + //以半角逗号(即,)作分隔符,列为空也要表达其存在。 + //列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。 + //列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。 + StringBuilder sb = new StringBuilder(); + DataColumn colum; + foreach (DataRow row in table.Rows) + { + for (int i = 0; i < table.Columns.Count; i++) + { + colum = table.Columns[i]; + if (i != 0) sb.Append(","); + if (colum.DataType == typeof(string) && row[colum].ToString().Contains(",")) + { + sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\""); + } + else sb.Append(row[colum].ToString()); + } + sb.AppendLine(); + } + return sb.ToString(); + } + + public override int BulkInsert(string tableName, DataTable data, int batchSize = 200000, int timeout = 60) + { + if (data.Rows.Count == 0) return 0; + int insertCount = 0; + string tmpPath = Path.GetTempFileName(); + string csv = DataTableToCsv(data); + File.WriteAllText(tmpPath, csv); + + List lstAllColName = this.Info[tableName]; + + MySqlConnection conn = null; + try + { + conn = CreateConn() as MySqlConnection; + conn.Open(); + MySqlBulkLoader bulk = new MySqlBulkLoader(conn) + { + FieldTerminator = ",", + FieldQuotationCharacter = '"', + EscapeCharacter = '"', + LineTerminator = "\r\n", + FileName = tmpPath, + NumberOfLinesToSkip = 0, + TableName = tableName, + Timeout=timeout + }; + + foreach (DataColumn dc in data.Columns) + { + if (lstAllColName.Contains(dc.ColumnName,StringComparer.OrdinalIgnoreCase)) + { + bulk.Columns.Add(dc.ColumnName); + } + } + insertCount = bulk.Load(); + } + catch (Exception) + { + + throw; + } + finally + { + File.Delete(tmpPath); + } + return insertCount; + } + } +} diff --git a/MJTop.Data/Database/OracleDB.cs b/MJTop.Data/Database/OracleDB.cs new file mode 100644 index 0000000..9a89997 --- /dev/null +++ b/MJTop.Data/Database/OracleDB.cs @@ -0,0 +1,237 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data; +using System.Text.RegularExpressions; + +namespace MJTop.Data.Database +{ + public class OracleDB : DB + { + public OracleDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new OracleDBInfo(this); + } + + public override bool ValidateSql(string strSql, out Exception ex) + { + bool bResult = false; + ex = null; + using (DbConnection conn = CreateConn()) + { + DbCommand cmd = conn.CreateCommand(); + conn.Open(); + try + { + cmd.CommandText = "explain plan for " + strSql; + cmd.ExecuteNonQuery(); + bResult = true; + } + catch (Exception e) + { + ex = e; + bResult = false; + } + finally + { + cmd?.Dispose(); + } + } + return bResult; + } + + internal override Ret InsertGet(DT data, string tableName, params string[] excludeColNames) + { + DbConnection conn = null; + DbCommand cmd = null; + try + { + var kv = InsertScript(data, tableName, excludeColNames); + + conn = CreateConn(); + cmd = BuildCommandByParam(conn, kv.Key, kv.Value, 30); + + var result1 = cmd.ExecuteNonQuery(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result1); + } + + OracleDBInfo OraInfo = Info as OracleDBInfo; + + string identitySql = Script.IdentitySql(DBType, tableName, OraInfo.IdentitySeqName(tableName)); + + cmd.CommandText = identitySql; + cmd.Parameters.Clear(); + + var result2 = cmd.ExecuteScalar().ChangeType(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result2); + } + + return result2; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override int InsertGetInt
(DT data, string tableName, params string[] excludeColNames) + { + int res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + try + { + act.Invoke(); + } + catch + { + continue; + } + } + } + return res; + } + + public override long InsertGetLong
(DT data, string tableName, params string[] excludeColNames) + { + long res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + try + { + act.Invoke(); + } + catch + { + continue; + } + } + } + return res; + } + + public override KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr) + { + if (string.IsNullOrEmpty(selColumns)) + { + selColumns = "*"; + } + + if (currentPage <= 0) + { + currentPage = 1; + } + + if (pageSize <= 0) + { + pageSize = 50; + } + + string cntSQL = string.Empty, strPageSQL = string.Empty; + DataTable data = new DataTable(); + long totalCount = 0; + + if (!string.IsNullOrWhiteSpace(whereStr)) + { + whereStr = Regex.Replace(whereStr, @"(\s)*(where|and)?(\s)*(.+)", "and $3$4", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + orderbyStr = Regex.Replace(orderbyStr, @"(\s)*(order)(\s)+(by)(.+)", "$5", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + else + { + throw new ArgumentNullException("orderbyStr"); + } + + cntSQL = "select count(1) from {0} where 1=1 {1}"; + cntSQL = string.Format(cntSQL, joinTableName, whereStr); + + string strSQL = "select {0} from {1} where 1=1 {2} order by {3}"; + strSQL = string.Format(strSQL, selColumns, joinTableName, whereStr, orderbyStr); + + + strPageSQL = string.Format(@"SELECT * FROM (SELECT A.*, ROWNUM RN FROM ({0}) A) WHERE RN BETWEEN {1} AND {2}", + strSQL, (currentPage - 1) * pageSize + 1, (currentPage) * pageSize); + + DataSet ds = new DataSet("ds"); + + DbConnection conn = null; + DbCommand cmd = null; + + try + { + conn = CreateConn(); + cmd = BuildCommand(conn, strPageSQL, 300); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + + if (ds.Tables.Count > 0) + { + data = ds.Tables[0]; + } + + cmd.CommandText = cntSQL; + cmd.Parameters.Clear(); + + totalCount = cmd.ExecuteScalar().ChangeType(); + + KeyValuePair res = new KeyValuePair(data, totalCount); + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, res); + } + return res; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = "select * from (select * from {0} {1})t where rownum <= {2}"; + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql = string.Format(tableName, " order by " + orderbyStr, top); + } + else + { + strSql = string.Format(tableName, string.Empty, top); + } + return base.SelectTop(tableName, top, orderbyStr); + } + } +} diff --git a/MJTop.Data/Database/OracleDDTekDB.cs b/MJTop.Data/Database/OracleDDTekDB.cs new file mode 100644 index 0000000..ce0b445 --- /dev/null +++ b/MJTop.Data/Database/OracleDDTekDB.cs @@ -0,0 +1,276 @@ +using DDTek.Oracle; +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.Database +{ + public class OracleDDTekDB : DB + { + public OracleDDTekDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new OracleDBInfo(this); + } + + public override bool ValidateSql(string strSql, out Exception ex) + { + bool bResult = false; + ex = null; + using (DbConnection conn = CreateConn()) + { + DbCommand cmd = conn.CreateCommand(); + conn.Open(); + try + { + cmd.CommandText = "explain plan for " + strSql; + cmd.ExecuteNonQuery(); + bResult = true; + } + catch (Exception e) + { + ex = e; + bResult = false; + } + finally + { + cmd?.Dispose(); + } + } + return bResult; + } + + internal override Ret InsertGet(DT data, string tableName, params string[] excludeColNames) + { + DbConnection conn = null; + DbCommand cmd = null; + try + { + var kv = InsertScript(data, tableName, excludeColNames); + + conn = CreateConn(); + cmd = BuildCommandByParam(conn, kv.Key, kv.Value, 30); + + var result1 = cmd.ExecuteNonQuery(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result1); + } + + OracleDBInfo OraInfo = Info as OracleDBInfo; + + string identitySql = Script.IdentitySql(DBType, tableName, OraInfo.IdentitySeqName(tableName)); + + cmd.CommandText = identitySql; + cmd.Parameters.Clear(); + + var result2 = cmd.ExecuteScalar().ChangeType(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result2); + } + + return result2; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override int InsertGetInt
(DT data, string tableName, params string[] excludeColNames) + { + int res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + public override long InsertGetLong
(DT data, string tableName, params string[] excludeColNames) + { + long res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + act.Invoke(); + } + } + return res; + } + + public override KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr) + { + if (string.IsNullOrEmpty(selColumns)) + { + selColumns = "*"; + } + + if (currentPage <= 0) + { + currentPage = 1; + } + + if (pageSize <= 0) + { + pageSize = 50; + } + + string cntSQL = string.Empty, strPageSQL = string.Empty; + DataTable data = new DataTable(); + long totalCount = 0; + + if (!string.IsNullOrWhiteSpace(whereStr)) + { + whereStr = Regex.Replace(whereStr, @"(\s)*(where|and)?(\s)*(.+)", "and $3$4", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + orderbyStr = Regex.Replace(orderbyStr, @"(\s)*(order)(\s)+(by)(.+)", "$5", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + else + { + throw new ArgumentNullException("orderbyStr"); + } + + cntSQL = "select count(1) from {0} where 1=1 {1}"; + cntSQL = string.Format(cntSQL, joinTableName, whereStr); + + string strSQL = "select {0} from {1} where 1=1 {2} order by {3}"; + strSQL = string.Format(strSQL, selColumns, joinTableName, whereStr, orderbyStr); + + + strPageSQL = string.Format(@"SELECT * FROM (SELECT A.*, ROWNUM RN FROM ({0}) A) WHERE RN BETWEEN {1} AND {2}", + strSQL, (currentPage - 1) * pageSize + 1, (currentPage) * pageSize); + + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + + try + { + conn = CreateConn(); + cmd = BuildCommand(conn, strPageSQL, 300); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + + if (ds.Tables.Count > 0) + { + data = ds.Tables[0]; + } + + cmd.CommandText = cntSQL; + cmd.Parameters.Clear(); + + totalCount = cmd.ExecuteScalar().ChangeType(); + + KeyValuePair res = new KeyValuePair(data, totalCount); + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, res); + } + return res; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override int BulkInsert(string tableName, DataTable data, int batchSize = 200000, int timeout = 60) + { + List lstAllColName = this.Info[tableName]; + OracleBulkCopy bulk = null; + bulk = new OracleBulkCopy(this.ConnectionString); + using (bulk) + { + int colCount = data.Columns.Count; + for (int j = 0; j < colCount; j++) + { + if (lstAllColName.Contains(data.Columns[j].ColumnName, StringComparer.OrdinalIgnoreCase)) + { + bulk.ColumnMappings.Add(new OracleBulkCopyColumnMapping(data.Columns[j].ColumnName, data.Columns[j].ColumnName)); + } + } + bulk.DestinationTableName = tableName; + bulk.BulkCopyTimeout = timeout; + bulk.BatchSize = batchSize; + bulk.WriteToServer(data); + } + return data.Rows.Count; + } + + public override int BulkInsert(string tableName, DbDataReader reader, int batchSize = 200000, int timeout = 60) + { + List lstAllColName = this.Info[tableName]; + OracleBulkCopy bulk = null; + bulk = new OracleBulkCopy(this.ConnectionString); + using (bulk) + { + int colCount = reader.FieldCount; + for (int j = 0; j < colCount; j++) + { + string currName = reader.GetName(j); + if (lstAllColName.Contains(currName, StringComparer.OrdinalIgnoreCase)) + { + bulk.ColumnMappings.Add(new OracleBulkCopyColumnMapping(currName, currName)); + } + } + bulk.DestinationTableName = tableName; + bulk.BulkCopyTimeout = timeout; + bulk.BatchSize = batchSize; + + bulk.WriteToServer(reader); + reader.Close(); + } + return reader.RecordsAffected; + } + + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = "select * from (select * from {0} {1})t where rownum <= {2}"; + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql = string.Format(tableName, " order by " + orderbyStr, top); + } + else + { + strSql = string.Format(tableName, string.Empty, top); + } + return base.SelectTop(tableName, top, orderbyStr); + } + + } +} diff --git a/MJTop.Data/Database/PostgreSqlDB.cs b/MJTop.Data/Database/PostgreSqlDB.cs new file mode 100644 index 0000000..5a5d46a --- /dev/null +++ b/MJTop.Data/Database/PostgreSqlDB.cs @@ -0,0 +1,113 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data; + +namespace MJTop.Data.Database +{ + public class PostgreSqlDB : DB + { + public PostgreSqlDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new PostgreSqlDBInfo(this); + } + + internal override Ret InsertGet(DT data, string tableName, params string[] excludeColNames) + { + DbConnection conn = null; + DbCommand cmd = null; + try + { + var kv = InsertScript(data, tableName, excludeColNames); + + conn = CreateConn(); + cmd = BuildCommandByParam(conn, kv.Key, kv.Value, 30); + + PostgreSqlDBInfo postgreInfo = Info as PostgreSqlDBInfo; + + cmd.CommandText = kv.Key + ";" + Script.IdentitySql(DBType, tableName, null, postgreInfo.IdentityColumnName(tableName)); + + var result2 = cmd.ExecuteScalar().ChangeType(); + + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, result2); + } + + return result2; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override int InsertGetInt
(DT data, string tableName, params string[] excludeColNames) + { + int res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + try + { + act.Invoke(); + } + catch + { + continue; + } + } + } + return res; + } + + public override long InsertGetLong
(DT data, string tableName, params string[] excludeColNames) + { + long res = InsertGet(data, tableName, excludeColNames); + var lstAct = DataChangeTriggers.GetActions(tableName); + if (lstAct.Any()) + { + foreach (var act in lstAct) + { + try + { + act.Invoke(); + } + catch + { + continue; + } + } + } + return res; + } + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = "select * from {0} {1} desc limit {2}"; + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql = string.Format(tableName, " order by " + orderbyStr, top); + } + else + { + strSql = string.Format(tableName, string.Empty, top); + } + return base.SelectTop(tableName, top, orderbyStr); + } + } +} diff --git a/MJTop.Data/Database/SQLiteDB.cs b/MJTop.Data/Database/SQLiteDB.cs new file mode 100644 index 0000000..a182244 --- /dev/null +++ b/MJTop.Data/Database/SQLiteDB.cs @@ -0,0 +1,36 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data; + +namespace MJTop.Data.Database +{ + public class SQLiteDB : DB + { + public SQLiteDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new SQLiteDBInfo(this); + } + + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = "select * from {0} {1} desc limit 0,{2}"; + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql = string.Format(tableName, " order by " + orderbyStr, top); + } + else + { + strSql = string.Format(tableName, string.Empty, top); + } + return base.SelectTop(tableName, top, orderbyStr); + } + } +} diff --git a/MJTop.Data/Database/SqlServerDB.cs b/MJTop.Data/Database/SqlServerDB.cs new file mode 100644 index 0000000..0e699be --- /dev/null +++ b/MJTop.Data/Database/SqlServerDB.cs @@ -0,0 +1,202 @@ +using MJTop.Data.DatabaseInfo; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data; +using System.Collections.Concurrent; +using System.Data.SqlClient; +using System.Collections; +using System.Collections.Specialized; +using System.Text.RegularExpressions; + +namespace MJTop.Data.Database +{ + public class SqlServerDB : DB + { + public SqlServerDB(DBType dbType, DbProviderFactory dbFactory, string connectionString, int cmdTimeOut) + : base(dbType, dbFactory, connectionString) + { + this.CmdTimeout = cmdTimeOut; + this.Info = new SqlServerDBInfo(this); + } + + public override bool ValidateSql(string strSql, out Exception ex) + { + bool bResult = false; + ex = null; + using (DbConnection conn = CreateConn()) + { + DbCommand cmd = conn.CreateCommand(); + conn.Open(); + try + { + cmd.CommandText = "set noexec on;"; + cmd.ExecuteNonQuery(); + + cmd.CommandText = strSql; + cmd.ExecuteNonQuery(); + + cmd.CommandText = "set noexec off;"; + cmd.ExecuteNonQuery(); + bResult = true; + } + catch (Exception e) + { + ex = e; + bResult = false; + } + finally + { + cmd?.Dispose(); + } + } + return bResult; + } + + public override KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr) + { + if (string.IsNullOrEmpty(selColumns)) + { + selColumns = "*"; + } + + if (currentPage <= 0) + { + currentPage = 1; + } + + if (pageSize <= 0) + { + pageSize = 50; + } + + string cntSQL = string.Empty, strPageSQL = string.Empty; + DataTable data = new DataTable(); + long totalCount = 0; + + if (!string.IsNullOrWhiteSpace(whereStr)) + { + whereStr = Regex.Replace(whereStr, @"(\s)*(where)?(\s)*(.+)", "where 1=1 and $3$4", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + orderbyStr = Regex.Replace(orderbyStr, @"(\s)*(order)(\s)+(by)(.+)", "$5", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + else + { + throw new ArgumentNullException("orderbyStr"); + } + + cntSQL = "select count(1) from {0} {1}"; + cntSQL = string.Format(cntSQL, joinTableName, whereStr); + + string strSQL = "select {0},ROW_NUMBER() OVER ( ORDER BY {3} ) RN from {1} {2} "; + strSQL = string.Format(strSQL, selColumns, joinTableName, whereStr, orderbyStr); + + strPageSQL = string.Format(@"SELECT * FROM ({0}) A WHERE RN BETWEEN {1} AND {2}", + strSQL, (currentPage - 1) * pageSize + 1, (currentPage) * pageSize); + + + DataSet ds = new DataSet("ds"); + DbConnection conn = null; + DbCommand cmd = null; + + try + { + conn = CreateConn(); + cmd = BuildCommand(conn, strPageSQL, 300); + DataAdapter adapter = CreateAdapter(cmd); + adapter.Fill(ds); + + if (ds.Tables.Count > 0) + { + data = ds.Tables[0]; + } + + cmd.CommandText = cntSQL; + cmd.Parameters.Clear(); + + totalCount = cmd.ExecuteScalar().ChangeType(); + + KeyValuePair res = new KeyValuePair(data, totalCount); + if (OnExecuted != null) + { + OnExecuted.Invoke(cmd, res); + } + return res; + } + catch (Exception ex) + { + if (this.OnError != null) + this.OnError.Invoke(cmd, ex); + throw ex; + } + finally + { + conn?.Close(); + } + } + + public override int BulkInsert(string tableName, DataTable data,int batchSize = 200000, int timeout = 60) + { + List lstAllColName = this.Info[tableName]; + SqlBulkCopy bulk = null; + bulk = new SqlBulkCopy(this.ConnectionString); + using (bulk) + { + int colCount = data.Columns.Count; + for (int j = 0; j < colCount; j++) + { + if (lstAllColName.Contains(data.Columns[j].ColumnName, StringComparer.OrdinalIgnoreCase)) + { + bulk.ColumnMappings.Add(new SqlBulkCopyColumnMapping(data.Columns[j].ColumnName, data.Columns[j].ColumnName)); + } + } + bulk.DestinationTableName = tableName; + bulk.BulkCopyTimeout = timeout; + bulk.BatchSize = batchSize; + bulk.WriteToServer(data); + } + return data.Rows.Count; + } + + public override int BulkInsert(string tableName, DbDataReader reader,int batchSize = 200000, int timeout = 60) + { + List lstAllColName = this.Info[tableName]; + SqlBulkCopy bulk = null; + bulk = new SqlBulkCopy(this.ConnectionString); + using (bulk) + { + int colCount = reader.FieldCount; + for (int j = 0; j < colCount; j++) + { + string currName = reader.GetName(j); + if (lstAllColName.Contains(currName, StringComparer.OrdinalIgnoreCase)) + { + bulk.ColumnMappings.Add(new SqlBulkCopyColumnMapping(currName, currName)); + } + } + bulk.DestinationTableName = tableName; + bulk.BulkCopyTimeout = timeout; + bulk.BatchSize = batchSize; + bulk.WriteToServer(reader); + reader.Close(); + } + return reader.RecordsAffected; + } + + public override DataTable SelectTop(string tableName, int top = 10, string orderbyStr = null) + { + string strSql = string.Format("select top {0} from {1} ", top, tableName); + if (!string.IsNullOrWhiteSpace(orderbyStr)) + { + strSql += " order by " + orderbyStr; + } + return base.SelectTop(tableName, top, orderbyStr); + } + } +} diff --git a/MJTop.Data/DatabaseInfo/DB2DBInfo.cs b/MJTop.Data/DatabaseInfo/DB2DBInfo.cs new file mode 100644 index 0000000..da5f570 --- /dev/null +++ b/MJTop.Data/DatabaseInfo/DB2DBInfo.cs @@ -0,0 +1,404 @@ +using IBM.Data.DB2; +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.DatabaseInfo +{ + public class DB2DBInfo : IDBInfo + { + private DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + + public DB2DBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + public string DBName + { + get { return (Db.ConnectionStringBuilder as DB2ConnectionStringBuilder).Database; } + } + + public string Version + { + get; + private set; + } + + // 8.0.19 => 8.0 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + private NameValueCollection TableSchemas { get; set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get; private set; } = new List(); + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + private DB2ConnectionStringBuilder ConnBuilder { get { return Db.ConnectionStringBuilder as DB2ConnectionStringBuilder; } } + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(KeyCase.Upper); + this.TableInfoDict = new IgCaseDictionary(KeyCase.Upper); + this.TableColumnNameDict = new IgCaseDictionary>(KeyCase.Upper); + this.TableColumnInfoDict = new IgCaseDictionary>(KeyCase.Upper); + this.TableColumnComments = new IgCaseDictionary(KeyCase.Upper); + + + string dbSql = string.Empty; + string strSql = string.Format("select tabschema,tabname,remarks from syscat.tables where type='T' and OWNER='{0}' order BY tabschema asc, tabname ASC", ConnBuilder.UserID.ToUpper()); + + string viewSql = string.Format("select viewname,text from syscat.views where OWNER='{0}' order by viewname asc", ConnBuilder.UserID.ToUpper()); + string procSql = string.Format("select procname,text from syscat.procedures where DEFINER='{0}' order by procname asc", ConnBuilder.UserID.ToUpper()); + + try + { + this.DBNames = new List() { this.DBName }; + + this.Version = Db.Scalar("SELECT SERVICE_LEVEL FROM SYSIBMADM.ENV_INST_INFO", string.Empty); + + var data = Db.GetDataTable(strSql); + foreach (DataRow dr in data.Rows) + { + this.TableComments[dr["tabname"].ToString()] = dr["remarks"].ToString(); + this.TableSchemas[dr["tabname"].ToString()] = dr["tabschema"].ToString(); + } + + this.Views = Db.ReadNameValues(viewSql); + + this.Procs = Db.ReadNameValues(procSql); + + if (this.TableComments != null && this.TableComments.Count > 0) + { + this.TableNames = this.TableComments.AllKeys.ToList(); + + List lstTask = new List(); + + foreach (var tableName in TableNames) + { + Task task = Task.Run(() => + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = TableComments[tableName]; + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + strSql = @"select (COLNO+1) as Colorder,COLNAME ColumnName,TypeName, +(case when TypeName = 'INTEGER' or TypeName = 'BIGINT'or TypeName='SMALLINT' then null else length end ) as length,(case when TypeName='NUMBER' or TypeName='DOUBLE' or TypeName='DECIMAL' or TypeName='FLOAT' or TypeName='REAL' then Scale else null end) as Scale, +(case when Identity='Y' then 1 else 0 end) as IsIdentity,(case when KEYSEQ=1 then 1 else 0 end) as IsPK, +(case when NULLS='N' then 0 else 1 end) as CanNull,default as DefaultVal,remarks as DeText from SYSCAT.COLUMNS where TABSCHEMA=? and TABNAME=? order by COLNO asc"; + + try + { + tabInfo.Colnumns = Db.GetDataTable(strSql, new { t = TableSchemas[tableName.ToUpper()], t1 = tableName.ToUpper() }).ConvertToListObject(); + foreach (ColumnInfo colInfo in tabInfo.Colnumns) + { + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); + + var strKey = (tableName + "@" + colInfo.ColumnName); + this.DictColumnInfo.Add(strKey, colInfo); + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + Global.Dict_DB2_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); + this.TableColumnComments.Add(tableName, nvcColDeText); + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + } + }); + lstTask.Add(task); + if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) + { + Task.WaitAny(lstTask.ToArray()); + lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); + } + } + Task.WaitAll(lstTask.ToArray()); + } + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + public Dictionary GetTableStruct_Modify() + { + string strSql = string.Format("select tabname,alter_time from syscat.tables where type='T' and tabschema='{0}' order by alter_time desc", ConnBuilder.UserID); + return Db.ReadDictionary(strSql); + } + + public bool IsExistTable(string tableName) + { + tableName = (tableName ?? string.Empty); + return TableNames.Contains(tableName); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName); + return DictColumnInfo.ContainsKey(strKey); + } + + public string GetColumnComment(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + ColumnInfo colInfo = null; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo?.DeText; + } + + public string GetTableComment(string tableName) + { + Db.CheckTabStuct(tableName); + return TableComments[tableName]; + } + + public List GetColumns(string tableName) + { + Db.CheckTabStuct(tableName); + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + public bool SetTableComment(string tableName, string comment) + { + Db.CheckTabStuct(tableName); + tableName = (tableName ?? string.Empty).ToUpper(); + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = string.Format("comment on table \"{0}\".\"{1}\" is '{2}' ", TableSchemas[tableName], tableName, comment); + Db.ExecSql(upsert_sql); + + TableComments[tableName] = comment; + var tabInfo = TableInfoDict[tableName]; + tabInfo.TabComment = comment; + TableInfoDict[tableName] = tabInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + Db.CheckTabStuct(tableName, columnName); + tableName = (tableName ?? string.Empty).ToUpper(); + columnName = (columnName ?? string.Empty).ToUpper(); + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = string.Format("comment on column \"{0}\".\"{1}\".\"{2}\" is '{3}'", TableSchemas[tableName], tableName, columnName, comment); + Db.ExecSql(upsert_sql); + + List lstColInfo = TableColumnInfoDict[tableName]; + + NameValueCollection nvcColDesc = new NameValueCollection(); + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + t.DeText = comment; + } + nvcColDesc.Add(t.ColumnName, t.DeText); + }); + + TableColumnInfoDict.Remove(tableName); + TableColumnInfoDict.Add(tableName, lstColInfo); + + TableColumnComments.Remove(tableName); + TableColumnComments.Add(tableName, nvcColDesc); + + var strKey = (tableName + "@" + columnName).ToUpper(); + ColumnInfo colInfo = DictColumnInfo[strKey]; + colInfo.DeText = comment; + DictColumnInfo[strKey] = colInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + string drop_sql = string.Empty; + try + { + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + tableName = (tableName ?? string.Empty).ToUpper(); + columnName = (columnName ?? string.Empty).ToUpper(); + + var strKey = (tableName + "@" + columnName); + string drop_sql = "alter table {0} drop column {1}"; + try + { + drop_sql = string.Format(drop_sql, tableName, columnName); + Db.ExecSql(drop_sql); + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + } +} diff --git a/MJTop.Data/DatabaseInfo/MySqlDBInfo.cs b/MJTop.Data/DatabaseInfo/MySqlDBInfo.cs new file mode 100644 index 0000000..8c7286a --- /dev/null +++ b/MJTop.Data/DatabaseInfo/MySqlDBInfo.cs @@ -0,0 +1,511 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.DatabaseInfo +{ + public class MySqlDBInfo : IDBInfo + { + private DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + + public MySqlDBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + + public string DBName + { + get { return (Db.ConnectionStringBuilder as MySql.Data.MySqlClient.MySqlConnectionStringBuilder).Database; } + } + + public string Version + { + get; + private set; + } + + // 8.0.19 => 8.0 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get; private set; } = new List(); + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(); + this.TableInfoDict = new IgCaseDictionary(); + this.TableColumnNameDict = new IgCaseDictionary>(); + this.TableColumnInfoDict = new IgCaseDictionary>(); + this.TableColumnComments = new IgCaseDictionary(); + + + string dbSql = "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA order by SCHEMA_NAME asc"; + string strSql = string.Format("SELECT table_name name,TABLE_COMMENT value FROM INFORMATION_SCHEMA.TABLES WHERE lower(table_type)='base table' and table_schema = '{0}' order by table_name asc ", DBName); + + string viewSql = string.Format("SELECT table_name,VIEW_DEFINITION FROM information_schema.views where TABLE_SCHEMA='{0}' order by table_name asc", DBName); + string procSql = string.Format("SELECT ROUTINE_NAME,ROUTINE_DEFINITION FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='{0}' AND ROUTINE_TYPE='PROCEDURE' ORDER BY ROUTINE_NAME ASC", DBName); + + try + { + this.DBNames = Db.ReadList(dbSql); + + this.Version = Db.Scalar("select @@version", string.Empty); + + this.TableComments = Db.ReadNameValues(strSql); + + this.Views = Db.ReadNameValues(viewSql); + + this.Procs = Db.ReadNameValues(procSql); + + if (this.TableComments != null && this.TableComments.Count > 0) + { + this.TableNames = this.TableComments.AllKeys.ToList(); + + List lstTask = new List(); + + foreach (var tableName in TableNames) + { + Task task = Task.Run(() => + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = TableComments[tableName]; + + //strSql = "SHOW FULL COLUMNS FROM " + tableName; + + strSql = @"select ORDINAL_POSITION as Colorder,Column_Name as ColumnName,data_type as TypeName,COLUMN_COMMENT as DeText, +(case when data_type = 'float' or data_type = 'double' or data_type = 'decimal' then NUMERIC_PRECISION else CHARACTER_MAXIMUM_LENGTH end ) as length, +(case when data_type = 'int' or data_type = 'bigint' then null else NUMERIC_SCALE end) as Scale,( case when EXTRA='auto_increment' then 1 else 0 end) as IsIdentity,(case when COLUMN_KEY='PRI' then 1 else 0 end) as IsPK, +(case when IS_NULLABLE = 'NO' then 0 else 1 end)as CanNull,COLUMN_DEFAULT as DefaultVal +from information_schema.columns where table_schema = ?DBName and table_name = ?tableName order by ORDINAL_POSITION asc"; + + try + { + tabInfo.Colnumns = Db.GetDataTable(strSql, new { DBName = DBName, tableName = tableName }).ConvertToListObject(); + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + foreach (ColumnInfo colInfo in tabInfo.Colnumns) + { + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); + + var strKey = (tableName + "@" + colInfo.ColumnName); + this.DictColumnInfo.Add(strKey, colInfo); + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + Global.Dict_MySql_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); + this.TableColumnComments.Add(tableName, nvcColDeText); + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + } + }); + lstTask.Add(task); + if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) + { + Task.WaitAny(lstTask.ToArray()); + lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); + } + } + Task.WaitAll(lstTask.ToArray()); + } + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + + #region MySql 获取列信息 + //private List MySqlReadColInfo(string strSql) + //{ + // List lstCols = new List(); + // DbDataReader reader = null; + // reader = Db.Reader(strSql); + // int colorder = 1; + // while (reader.Read()) + // { + // ColumnInfo colInfo = new ColumnInfo(); + // colInfo.Colorder = colorder; + // colInfo.ColumnName = reader["Field"].ToString(); + + // { + // string typename = reader["Type"].ToString(); + // string len = "", pre = "", scal = ""; + // TypeNameProcess(typename, out typename, out len, out pre, out scal); + + // colInfo.TypeName = typename; + // colInfo.Length = len.ChangeType((int?)null); + // colInfo.Scale = scal.ChangeType((int?)null); + // } + + // colInfo.IsPK = (reader["Key"].ToString() == "PRI") ? true : false; + + // colInfo.CanNull = (reader["Null"].ToString() == "YES") ? true : false; + + // colInfo.DefaultVal = reader["Default"].ToString(); + + // colInfo.DeText = reader["Comment"].ToString(); + + // colInfo.IsIdentity = (reader["Extra"].ToString() == "auto_increment") ? true : false; + + // lstCols.Add(colInfo); + + // colorder++; + // } + + // if (reader != null && !reader.IsClosed) + // { + // reader.Close(); + // } + + // return lstCols; + //} + + //对类型名称 解析 + private void TypeNameProcess(string strName, out string TypeName, out string Length, out string Preci, out string Scale) + { + TypeName = strName; + Length = string.Empty; + Preci = string.Empty; + Scale = string.Empty; + + if (strName.Contains("(")) + { + if (!strName.Contains(",")) + { + TypeName = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$1", RegexOptions.Compiled); + Length = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$2", RegexOptions.Compiled); + } + else + { + TypeName = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$1", RegexOptions.Compiled); + Length = Regex.Replace(strName, @"(\w+)\((\d+),(\d+)\)", "$2", RegexOptions.Compiled); + Scale = Regex.Replace(strName, @"(\w+)\((\d+),(\d+)\)", "$3", RegexOptions.Compiled); + } + } + } + #endregion + + + public bool IsAllMyISAM + { + get + { + string strSql = string.Format("select case when ((SELECT count(1) FROM information_schema.tables where table_type='base table' and TABLE_SCHEMA='{0}' )= (SELECT count(1) FROM information_schema.tables where table_type='base table' and TABLE_SCHEMA = '{0}' and ENGINE = 'MyISAM')) then true else FALSE end as res", DBName); + return Db.Single(strSql, false); + } + } + + + public Dictionary GetTableStruct_Modify() + { + string strSql = string.Format("SELECT TABLE_NAME name,UPDATE_TIME modify_date FROM information_schema.tables where TABLE_SCHEMA='{0}' and table_type='base table' and update_time is not null ORDER BY UPDATE_TIME asc", DBName); + return Db.ReadDictionary(strSql); + } + + public bool IsExistTable(string tableName) + { + tableName = (tableName ?? string.Empty); + return TableNames.Contains(tableName); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName); + return DictColumnInfo.ContainsKey(strKey); + } + + public string GetColumnComment(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + ColumnInfo colInfo = null; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo?.DeText; + } + + public string GetTableComment(string tableName) + { + Db.CheckTabStuct(tableName); + return TableComments[tableName]; + } + + public List GetColumns(string tableName) + { + Db.CheckTabStuct(tableName); + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + public bool SetTableComment(string tableName, string comment) + { + Db.CheckTabStuct(tableName); + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = "ALTER TABLE `" + tableName + "` COMMENT='" + comment + "';"; + Db.ExecSql(upsert_sql); + + TableComments[tableName] = comment; + + var tabInfo = TableInfoDict[tableName]; + tabInfo.TabComment = comment; + TableInfoDict[tableName] = tabInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + Db.CheckTabStuct(tableName, columnName); + + string selsql = string.Empty; + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + + try + { + var colInfo = this[tableName, columnName]; + string setSql = string.Empty; + if (!colInfo.CanNull) + { + setSql += " not null "; + } + + if (colInfo.IsIdentity) + { + setSql += " auto_increment "; + } + + if (!string.IsNullOrWhiteSpace(colInfo.DefaultVal)) + { + setSql += " default '" + colInfo.DefaultVal + "' "; + } + + selsql = "use information_schema;SELECT COLUMN_TYPE,EXTRA FROM COLUMNS WHERE TABLE_SCHEMA='" + DBName + "' and TABLE_NAME = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "';"; + var dict = Db.GetFirstRow(selsql); + if (colInfo.DefaultVal != null && colInfo.DefaultVal.Equals("CURRENT_TIMESTAMP", StringComparison.OrdinalIgnoreCase)) + { + upsert_sql = "USE `" + DBName + "`;ALTER TABLE `" + DBName + "`.`" + tableName + "` CHANGE `" + columnName + "` `" + columnName + "` TIMESTAMP DEFAULT CURRENT_TIMESTAMP " + dict["EXTRA"] + " COMMENT '" + comment + "'; "; + } + else + { + string col_type = dict["COLUMN_TYPE"].ToString(); + upsert_sql = "USE `" + DBName + "`;ALTER TABLE " + tableName + " MODIFY COLUMN `" + columnName + "` " + col_type + " " + setSql + " COMMENT '" + comment + "';"; + } + Db.ExecSql(upsert_sql); + + List lstColInfo = TableColumnInfoDict[tableName]; + + NameValueCollection nvcColDesc = new NameValueCollection(); + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + t.DeText = comment; + } + nvcColDesc.Add(t.ColumnName, t.DeText); + }); + + TableColumnInfoDict.Remove(tableName); + TableColumnInfoDict.Add(tableName, lstColInfo); + + TableColumnComments.Remove(tableName); + TableColumnComments.Add(tableName, nvcColDesc); + + var strKey = (tableName + "@" + columnName); + colInfo = DictColumnInfo[strKey]; + colInfo.DeText = comment; + DictColumnInfo[strKey] = colInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, selsql, upsert_sql); + return false; + } + return true; + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + + string drop_sql = string.Empty; + try + { + + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + + var strKey = (tableName + "@" + columnName); + + string drop_sql = "alter table `{0}` drop column `{1}`"; + try + { + + drop_sql = string.Format(drop_sql, tableName, columnName); + Db.ExecSql(drop_sql); + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + } +} diff --git a/MJTop.Data/DatabaseInfo/OracleDBInfo.cs b/MJTop.Data/DatabaseInfo/OracleDBInfo.cs new file mode 100644 index 0000000..77b279b --- /dev/null +++ b/MJTop.Data/DatabaseInfo/OracleDBInfo.cs @@ -0,0 +1,526 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.DatabaseInfo +{ + public class OracleDBInfo : IDBInfo + { + private DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + + public OracleDBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + + public string DBName + { + get + { + if (Db.ConnectionStringBuilder is Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder) + { + //127.0.0.1:1521/CTMS + string source = (Db.ConnectionStringBuilder as Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder).DataSource; + return Regex.Replace(source, @"(.+/)(.+)", "$2"); + } + else + { + return (Db.ConnectionStringBuilder as DDTek.Oracle.OracleConnectionStringBuilder).ServiceName; + } + } + } + + public string User + { + get + { + if (Db.ConnectionStringBuilder is Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder) + { + return (Db.ConnectionStringBuilder as Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder).UserID?.ToUpper(); + } + else + { + return (Db.ConnectionStringBuilder as DDTek.Oracle.OracleConnectionStringBuilder).UserID?.ToUpper(); + } + } + } + + public string Version + { + get; + private set; + } + + //Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production => 11.2 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } + + private IgCaseDictionary Dict_Table_Sequence { get; set; } = new IgCaseDictionary(KeyCase.Upper); + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get { return DBName.TransList(); } } + + public List Sequences { get; set; } = new List(); + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + + public string IdentitySeqName(string tableName) + { + string seqName; + if (Dict_Table_Sequence.TryGetValue(tableName, out seqName)) + { + return seqName; + } + return string.Empty; + } + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(KeyCase.Upper); + this.TableInfoDict = new IgCaseDictionary(KeyCase.Upper); + this.TableColumnNameDict = new IgCaseDictionary>(KeyCase.Upper); + this.TableColumnInfoDict = new IgCaseDictionary>(KeyCase.Upper); + this.TableColumnComments = new IgCaseDictionary(KeyCase.Upper); + + string sequence_Sql = string.Format("SELECT SEQUENCE_NAME FROM ALL_SEQUENCES WHERE SEQUENCE_OWNER = '{0}' ORDER BY SEQUENCE_NAME", User); + + string strSql = string.Format("SELECT T.TABLE_NAME as Name, TC.COMMENTS as Value FROM SYS.ALL_ALL_TABLES T, SYS.ALL_TAB_COMMENTS TC WHERE T.IOT_NAME IS NULL AND T.NESTED = 'NO' AND T.SECONDARY = 'N' AND NOT EXISTS ( SELECT 1 FROM SYS.ALL_MVIEWS MV WHERE MV.OWNER = T.OWNER AND MV.MVIEW_NAME = T.TABLE_NAME ) AND TC.OWNER ( + ) = T.OWNER AND TC.TABLE_NAME ( + ) = T.TABLE_NAME AND T.OWNER = '{0}' ORDER BY T.TABLE_NAME ASC", User); + + string viewSql = string.Format("select view_name,text from ALL_VIEWS WHERE OWNER = '{0}' order by view_name asc", User); + + //Oracle 11g 推出 LISTAGG 函数,有可能会报:ora-01489 字符串连接的结果过长 + string procSql = string.Format("select * from (SELECT name,LISTAGG(text,' ') WITHIN group (order by line asc) text FROM all_source where OWNER = '{0}' group by name ) order by name asc", User); + + //https://blog.csdn.net/rczrj/article/details/74977010 + procSql = string.Format("select * from (SELECT name,xmlagg(xmlparse(content text||' ' wellformed) order by line asc).getclobval() text FROM all_source where OWNER = '{0}' group by name ) order by name asc", User); + + try + { + //查询Oracle的所有序列 + this.Sequences = Db.ReadList(sequence_Sql); + + this.TableComments = Db.ReadNameValues(strSql); + + this.Version = Db.Scalar("select * from v$version where ROWNUM = 1", string.Empty); + + this.Views = Db.ReadNameValues(viewSql); + + try + { + this.Procs = Db.ReadNameValues(procSql); + } + catch (Exception e) + { + LogUtils.LogError("查询存储过程报错", Developer.SysDefault, e, this.Version, procSql); + this.Procs = new NameValueCollection(); + } + + if (this.TableComments != null && this.TableComments.Count > 0) + { + this.TableNames = this.TableComments.AllKeys.ToList(); + + List lstTask = new List(); + + foreach (string tableName in this.TableNames) + { + Task task = Task.Run(() => + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = this.TableComments[tableName]; + + /** 该语句,包含某列是否自增列,查询慢 **/ + + strSql = @"select a.COLUMN_ID As Colorder,a.COLUMN_NAME As ColumnName,a.DATA_TYPE As TypeName,b.comments As DeText,(Case When a.DATA_TYPE='NUMBER' Then a.DATA_PRECISION When a.DATA_TYPE='NVARCHAR2' Then a.DATA_LENGTH/2 Else a.DATA_LENGTH End )As Length,a.DATA_SCALE As Scale, + (Case When (select Count(1) from all_cons_columns aa, all_constraints bb where aa.OWNER = '{0}' and bb.OWNER = '{0}' and aa.constraint_name = bb.constraint_name and bb.constraint_type = 'P' and aa.table_name = '{1}' And aa.column_name=a.COLUMN_NAME)>0 Then 1 Else 0 End + ) As IsPK,( + case when (select count(1) from all_triggers tri INNER JOIN all_source src on tri.trigger_Name=src.Name + where tri.OWNER = '{0}' and src.OWNER = '{0}' and (triggering_Event='INSERT' and table_name='{1}') + and regexp_like(text,concat(concat('into\s*?:\s*?new\s*?\.\s*?',a.COLUMN_NAME),'\s+?'),'i'))>0 + then 1 else 0 end + ) As IsIdentity, + Case a.NULLABLE When 'Y' Then 1 Else 0 End As CanNull, + a.data_default As DefaultVal from all_tab_columns a Inner Join all_col_comments b On a.TABLE_NAME=b.table_name + Where a.OWNER = '{0}' and b.OWNER = '{0}' and b.COLUMN_NAME= a.COLUMN_NAME and a.Table_Name='{1}' order by a.column_ID Asc"; + + // 忽略 IsIdentity 查询 + strSql = @"select a.COLUMN_ID As Colorder,a.COLUMN_NAME As ColumnName,a.DATA_TYPE As TypeName,b.comments As DeText,(Case When a.DATA_TYPE='NUMBER' Then a.DATA_PRECISION When a.DATA_TYPE='NVARCHAR2' Then a.DATA_LENGTH/2 Else a.DATA_LENGTH End )As Length,a.DATA_SCALE As Scale, + (Case When (select Count(1) from all_cons_columns aa, all_constraints bb where aa.OWNER = '{0}' and bb.OWNER = '{0}' and aa.constraint_name = bb.constraint_name and bb.constraint_type = 'P' and aa.table_name = '{1}' And aa.column_name=a.COLUMN_NAME)>0 Then 1 Else 0 End + ) As IsPK,0 As IsIdentity, + Case a.NULLABLE When 'Y' Then 1 Else 0 End As CanNull, + a.data_default As DefaultVal from all_tab_columns a Inner Join all_col_comments b On a.TABLE_NAME=b.table_name + Where a.OWNER = '{0}' and b.OWNER = '{0}' and b.COLUMN_NAME= a.COLUMN_NAME and a.Table_Name='{1}' order by a.column_ID Asc"; + + strSql = string.Format(strSql, User, tableName); + + try + { + tabInfo.Colnumns = Db.GetDataTable(strSql).ConvertToListObject(); + + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + foreach (ColumnInfo colInfo in tabInfo.Colnumns) + { + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); + + var strKey = (tableName + "@" + colInfo.ColumnName); + this.DictColumnInfo.Add(strKey, colInfo); + + //自增的列,需要查询序列名称 + if (colInfo.IsIdentity) + { + AddColSeq(tableName, colInfo.ColumnName); + } + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + Global.Dict_Oracle_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); + this.TableColumnComments.Add(tableName, nvcColDeText); + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, strSql); + } + }); + + lstTask.Add(task); + if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) + { + Task.WaitAny(lstTask.ToArray()); + lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); + } + } + Task.WaitAll(lstTask.ToArray()); + } + + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + private void AddColSeq(string tableName, string colName) + { + tableName = (tableName ?? string.Empty); + colName = (colName ?? string.Empty); + string strSql = string.Empty; + if (Sequences != null && Sequences.Count > 0) + { + foreach (string seqName in Sequences) + { + strSql = @"select count(1) from all_triggers tri INNER JOIN all_source src on tri.trigger_Name=src.Name where tri.OWNER = '" + User + "' and src.OWNER = '" + User + "' and (triggering_Event='INSERT' and table_name='" + tableName + "') and regexp_like(text,concat(concat('" + seqName + @"\s*?\.\s*?nextval\s+into\s*?:\s*?new\s*?\.\s*?','" + colName + @"'),'\s+?'),'i')"; + int res = Db.Single(strSql, 0); + if (res > 0) + { + Dict_Table_Sequence[tableName] = seqName; + break; + } + } + } + } + + public Dictionary GetTableStruct_Modify() + { + string strSql = "select object_name as name ,last_ddl_time as modify_date from all_objects Where OWNER = '" + User + "' and object_Type='TABLE' Order By last_ddl_time Desc"; + return Db.ReadDictionary(strSql); + } + + + public bool IsExistTable(string tableName) + { + tableName = (tableName ?? string.Empty); + return TableNames.Contains(tableName); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName); + return DictColumnInfo.ContainsKey(strKey); + } + + + public string GetColumnComment(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + ColumnInfo colInfo = null; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo?.DeText; + } + + public string GetTableComment(string tableName) + { + Db.CheckTabStuct(tableName); + return TableComments[tableName]; + } + + public List GetColumns(string tableName) + { + Db.CheckTabStuct(tableName); + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + /// + /// 如果表名 或 列名 小写 则加双引号 + /// + /// 表名或列名 + /// + private string FormatName(string name) + { + if (Regex.IsMatch(name, ".*?[a-z].*?", RegexOptions.Compiled)) + { + return "\"" + name + "\""; + } + return name; + } + + public bool SetTableComment(string tableName, string comment) + { + Db.CheckTabStuct(tableName); + + //tableName = (tableName ?? string.Empty); + + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = "comment on table \"" + tableName + "\" is '" + comment + "'"; + Db.ExecSql(upsert_sql); + + TableComments[tableName] = comment; + + var tabInfo = TableInfoDict[tableName]; + tabInfo.TabComment = comment; + TableInfoDict[tableName] = tabInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + Db.CheckTabStuct(tableName, columnName); + + //tableName = (tableName ?? string.Empty); + //columnName = (columnName ?? string.Empty); + + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = "comment on column \"" + tableName + "\".\"" + columnName + "\" is '" + comment + "'"; + Db.ExecSql(upsert_sql); + + List lstColInfo = TableColumnInfoDict[tableName]; + + NameValueCollection nvcColDesc = new NameValueCollection(); + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName,StringComparison.OrdinalIgnoreCase)) + { + t.DeText = comment; + } + nvcColDesc.Add(t.ColumnName, t.DeText); + }); + + TableColumnInfoDict.Remove(tableName); + TableColumnInfoDict.Add(tableName, lstColInfo); + + TableColumnComments.Remove(tableName); + TableColumnComments.Add(tableName, nvcColDesc); + + var strKey = (tableName + "@" + columnName); + ColumnInfo colInfo = DictColumnInfo[strKey]; + colInfo.DeText = comment; + DictColumnInfo[strKey] = colInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + + tableName = (tableName ?? string.Empty); + string drop_sql = string.Empty; + try + { + + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + + tableName = (tableName ?? string.Empty); + columnName = (columnName ?? string.Empty); + + var strKey = (tableName + "@" + columnName); + + string drop_sql = "alter table {0} drop column {1}"; + try + { + drop_sql = string.Format(drop_sql, tableName, columnName); + Db.ExecSql(drop_sql); + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + } +} diff --git a/MJTop.Data/DatabaseInfo/PostgreSqlDBInfo.cs b/MJTop.Data/DatabaseInfo/PostgreSqlDBInfo.cs new file mode 100644 index 0000000..6328e49 --- /dev/null +++ b/MJTop.Data/DatabaseInfo/PostgreSqlDBInfo.cs @@ -0,0 +1,479 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.DatabaseInfo +{ + public class PostgreSqlDBInfo : IDBInfo + { + internal DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + public PostgreSqlDBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + + public string DBName + { + get { return (Db.ConnectionStringBuilder as Npgsql.NpgsqlConnectionStringBuilder).Database; } + } + + public string Version + { + get; + private set; + } + + // PostgreSQL 9.6.3, compiled by Visual C++ build 1800, 32-bit => 9.6 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public string User + { + get { return (Db.ConnectionStringBuilder as Npgsql.NpgsqlConnectionStringBuilder).Username; } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + private NameValueCollection TableSchemas { get; set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } + + private IgCaseDictionary Dict_Table_Identity_Column { get; set; } = new IgCaseDictionary(); + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get; set; } = new List(); + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(); + this.TableNames = new List(); + this.TableInfoDict = new IgCaseDictionary(); + this.TableColumnNameDict = new IgCaseDictionary>(); + this.TableColumnInfoDict = new IgCaseDictionary>(); + this.TableColumnComments = new IgCaseDictionary(); + + string dbSql = "select datname from pg_database where datistemplate=false order by oid desc"; + string strSql = @"select a.*,cast(obj_description(relfilenode,'pg_class') as varchar) as value from ( +select b.oid as schid, a.table_schema as scName,concat(a.table_schema,'.',a.table_name) as Name,a.table_name from information_schema.tables a +left join pg_namespace b on b.nspname = a.table_schema +where a.table_schema not in ('pg_catalog','information_schema') and a.table_schema=:schema and a.table_type='BASE TABLE' +) a inner join pg_class b on a.table_name = b.relname and a.schid = b.relnamespace +order by name asc"; + + string viewSql = $"SELECT viewname,definition FROM pg_views where schemaname='{User}' order by viewname asc"; + string procSql = $"select proname,prosrc from pg_proc where pronamespace=(SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = '{User}') order by proname asc"; + + try + { + + this.DBNames = Db.ReadList(dbSql); + + this.Version = Db.Scalar("select version()", string.Empty); + + var data = Db.GetDataTable(strSql, new { schema = User }); + + var dictGp = new Dictionary>(); + + foreach (DataRow dr in data.Rows) + { + this.TableComments[dr["name"].ToString()] = dr["value"].ToString(); + this.TableSchemas[dr["name"].ToString()] = dr["scname"].ToString(); + + dictGp.AddRange(dr["scname"].ToString(), dr["name"].ToString()); + } + + foreach (var item in dictGp) + { + this.TableNames.AddRange(item.Value.OrderBy(t => t)); + } + + this.Views = Db.ReadNameValues(viewSql); + + this.Procs = Db.ReadNameValues(procSql); + + if (this.TableComments != null && this.TableComments.Count > 0) + { + List lstTask = new List(); + + foreach (var tableName in this.TableNames) + { + Task task = Task.Run(() => + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = TableComments[tableName]; + + strSql = @"SELECT + col.ordinal_position AS Colorder, + col.table_schema AS SCHEMA_NAME, + col.TABLE_NAME, + col.COLUMN_NAME AS ColumnName, + col.udt_name AS TypeName, + col_description ( C.oid, col.ordinal_position ) AS DeText, + COALESCE(col.character_maximum_length, col.numeric_precision, -1) AS Length, + col.numeric_scale AS Scale, + (CASE col.is_identity WHEN 'YES' THEN 1 ELSE 0 END) AS IsIdentity, + (CASE col.is_nullable WHEN 'YES' THEN 1 ELSE 0 END) AS CanNull, + col.column_default AS DefaultVal +FROM + information_schema.COLUMNS AS col + LEFT JOIN pg_namespace ns ON ns.nspname = col.table_schema + LEFT JOIN pg_class C ON col.TABLE_NAME = C.relname + AND C.relnamespace = ns.oid + LEFT JOIN pg_attrdef A ON C.oid = A.adrelid + AND col.ordinal_position = A.adnum + LEFT JOIN pg_attribute b ON b.attrelid = C.oid + AND b.attname = col. + COLUMN_NAME LEFT JOIN pg_type et ON et.oid = b.atttypid + LEFT JOIN pg_collation coll ON coll.oid = b.attcollation + LEFT JOIN pg_namespace colnsp ON coll.collnamespace = colnsp.oid + LEFT JOIN pg_type bt ON et.typelem = bt.oid + LEFT JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid +WHERE + col.table_schema = :schema + AND col.TABLE_NAME = :tableName +ORDER BY + col.table_schema, + col.TABLE_NAME, + col.ordinal_position"; + var strKey = string.Empty; + try + { + var arr = tableName.Split('.'); + var scName = arr.FirstOrDefault(); + var tabName = arr.LastOrDefault(); + tabInfo.Colnumns = Db.GetDataTable(strSql, new { tableName = tabName, schema = scName }).ConvertToListObject(); + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + foreach (ColumnInfo colInfo in tabInfo.Colnumns) + { + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); + + strKey = (tableName + "@" + colInfo.ColumnName); + this.DictColumnInfo.Add(strKey, colInfo); + if (colInfo.IsIdentity) + { + this.Dict_Table_Identity_Column[tableName] = colInfo.ColumnName; + } + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + Global.Dict_PostgreSql_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); + this.TableColumnComments.Add(tableName, nvcColDeText); + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, strKey); + } + }); + lstTask.Add(task); + if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) + { + Task.WaitAny(lstTask.ToArray()); + lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); + } + } + Task.WaitAll(lstTask.ToArray()); + } + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + internal string IdentityColumnName(string tableName) + { + tableName = (tableName ?? string.Empty).ToLower(); + string colName; + if (Dict_Table_Identity_Column.TryGetValue(tableName, out colName)) + { + return colName; + } + return string.Empty; + } + + public Dictionary GetTableStruct_Modify() + { + throw new Exception("暂未提供查询PostgreSql表结构修改时间的功能!"); + } + + public bool IsExistTable(string tableName) + { + return TableNames.Contains(tableName); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName).ToLower(); + return DictColumnInfo.ContainsKey(strKey); + } + + public string GetColumnComment(string tableName, string columnName) + { + ColumnInfo colInfo = null; + var strKey = (tableName + "@" + columnName).ToLower(); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo?.DeText; + } + + public string GetTableComment(string tableName) + { + Db.CheckTabStuct(tableName); + return TableComments[tableName]; + } + + public List GetColumns(string tableName) + { + Db.CheckTabStuct(tableName); + tableName = (tableName ?? string.Empty).ToLower(); + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + public bool SetTableComment(string tableName, string comment) + { + Db.CheckTabStuct(tableName); + + tableName = TableInfoDict[tableName].TableName; + + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + var tabName = tableName.Split('.').LastOrDefault(); + //切换schema,更新表描述 + upsert_sql = "set search_path to " + TableSchemas[tableName] + ";comment on table \"" + tabName + "\" is '" + comment + "'"; + Db.ExecSql(upsert_sql); + + TableComments[tableName] = comment; + + var tabInfo = TableInfoDict[tableName]; + tabInfo.TabComment = comment; + TableInfoDict[tableName] = tabInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + Db.CheckTabStuct(tableName, columnName); + + tableName = TableInfoDict[tableName].TableName; + columnName = this[tableName, columnName].ColumnName; + + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + var tabName = tableName.Split('.').LastOrDefault(); + //切换schema,更新列描述 + upsert_sql = "set search_path to " + TableSchemas[tableName] + ";comment on column \"" + tabName + "\".\"" + columnName + "\" is '" + comment + "'"; + Db.ExecSql(upsert_sql); + + List lstColInfo = TableColumnInfoDict[tableName]; + + NameValueCollection nvcColDesc = new NameValueCollection(); + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + t.DeText = comment; + } + nvcColDesc.Add(t.ColumnName, t.DeText); + }); + + TableColumnInfoDict.Remove(tableName); + TableColumnInfoDict.Add(tableName, lstColInfo); + + TableColumnComments.Remove(tableName); + TableColumnComments.Add(tableName, nvcColDesc); + + var strKey = (tableName + "@" + columnName); + ColumnInfo colInfo = DictColumnInfo[strKey]; + colInfo.DeText = comment; + DictColumnInfo[strKey] = colInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + tableName = (tableName ?? string.Empty).ToLower(); + string drop_sql = string.Empty; + try + { + + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName).ToLower(); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + + tableName = (tableName ?? string.Empty).ToLower(); + columnName = (columnName ?? string.Empty).ToLower(); + + var strKey = (tableName + "@" + columnName); + + string drop_sql = "alter table {0} drop column {1}"; + try + { + drop_sql = string.Format(drop_sql, tableName, columnName); + Db.ExecSql(drop_sql); + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + } +} diff --git a/MJTop.Data/DatabaseInfo/SQLiteDBInfo.cs b/MJTop.Data/DatabaseInfo/SQLiteDBInfo.cs new file mode 100644 index 0000000..8dd4ef5 --- /dev/null +++ b/MJTop.Data/DatabaseInfo/SQLiteDBInfo.cs @@ -0,0 +1,376 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.SQLite; +using System.Linq; +using System.Text.RegularExpressions; + +namespace MJTop.Data.DatabaseInfo +{ + public class SQLiteDBInfo : IDBInfo + { + private DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + public SQLiteDBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + + public string DBName + { + get { return System.IO.Path.GetFileNameWithoutExtension((Db.ConnectionStringBuilder as SQLiteConnectionStringBuilder).DataSource); } + } + + public string Version + { + get; + private set; + } + + // 3.31.1 => 3.31 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get { return DBName.TransList(); } } + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName).ToLower(); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(); + this.TableInfoDict = new IgCaseDictionary(); + this.TableColumnNameDict = new IgCaseDictionary>(); + this.TableColumnInfoDict = new IgCaseDictionary>(); + this.TableColumnComments = new IgCaseDictionary(); + + string strSql = "SELECT name,'' as desc FROM sqlite_master WHERE type='table' order by name"; + string viewSql = "SELECT name,sql FROM sqlite_master WHERE type='view' order by name asc"; + string procSql = string.Empty; //Sqlite 没有存储过程功能! + try + { + this.TableComments = Db.GetDataTable(strSql).MapperNameValues("name", "desc"); + + this.Views = Db.ReadNameValues(viewSql); + + this.Procs = new NameValueCollection(); + + if (this.TableComments != null && this.TableComments.Count > 0) + { + this.TableNames = TableComments.AllKeys.ToList(); + + var dbConn = Db.CreateConn(); + dbConn.Open(); + + this.Version = dbConn.ServerVersion; + + foreach (string tableName in TableNames) + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = TableComments[tableName]; + + List lstColInfo = new List(); + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + + DataRow[] columns = dbConn.GetSchema("COLUMNS").Select("TABLE_NAME='" + tableName + "'"); + foreach (DataRow dr in columns) + { + ColumnInfo colInfo = new ColumnInfo(); + colInfo.Colorder = dr["ORDINAL_POSITION"].ToString().ChangeType(0) + 1; + colInfo.ColumnName = dr["COLUMN_NAME"].ToString(); + colInfo.Length = dr["CHARACTER_MAXIMUM_LENGTH"].ToString().ChangeType((int?)null); + //colInfo.Preci = dr["NUMERIC_PRECISION"].ToString().ChangeType(null); + colInfo.Scale = dr["NUMERIC_SCALE"].ToString().ChangeType((int?)null); + colInfo.IsPK = dr["PRIMARY_KEY"].ToString().ToLower() == "true" ? true : false; + colInfo.CanNull = dr["IS_NULLABLE"].ToString().ToLower() == "true" ? true : false; + colInfo.DefaultVal = dr["COLUMN_DEFAULT"].ToString(); + colInfo.TypeName = dr["DATA_TYPE"].ToString(); + if (colInfo.IsPK && string.Compare(colInfo.TypeName, "integer", true) == 0) + { + colInfo.IsIdentity = true; + } + + if (colInfo.TypeName == "integer" || colInfo.TypeName == "bigint") + { + colInfo.Scale = null; + } + + lstColInfo.Add(colInfo); + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, string.Empty); + + var strKey = (tableName + "@" + colInfo.ColumnName).ToLower(); + DictColumnInfo.Add(strKey, colInfo); + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + + Global.Dict_Sqlite_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + tabInfo.Colnumns = lstColInfo; + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, lstColInfo); + this.TableColumnComments.Add(tableName, nvcColDeText); + + } + dbConn.Close(); + } + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + #region Sqlite获取列信息 + //private List SqliteColInfo(string tableName) + //{ + // List lstCols = new List(); + // var dbConn = Db.CreateConn(); + // dbConn.Open(); + // DataRow[] columns = dbConn.GetSchema("COLUMNS").Select("TABLE_NAME='" + tableName + "'"); + // foreach (DataRow dr in columns) + // { + // ColumnInfo colInfo = new ColumnInfo(); + // colInfo.Colorder = dr["ORDINAL_POSITION"].ToString().ChangeType(0); + // colInfo.ColumnName = dr["COLUMN_NAME"].ToString(); + // colInfo.Length = dr["CHARACTER_MAXIMUM_LENGTH"].ToString().ChangeType((int?)null); + // //colInfo.Preci = dr["NUMERIC_PRECISION"].ToString().ChangeType(null); + // colInfo.Scale = dr["NUMERIC_SCALE"].ToString().ChangeType((int?)null); + // colInfo.IsPK = dr["PRIMARY_KEY"].ToString().ToLower() == "true" ? true : false; + // colInfo.CanNull = dr["IS_NULLABLE"].ToString().ToLower() == "true" ? true : false; + // colInfo.DefaultVal = dr["COLUMN_DEFAULT"].ToString(); + // colInfo.TypeName = dr["DATA_TYPE"].ToString(); + // if (colInfo.IsPK && string.Compare(colInfo.TypeName, "integer", true) == 0) + // { + // colInfo.IsIdentity = true; + // } + // lstCols.Add(colInfo); + // } + // dbConn.Close(); + // return lstCols; + //} + #endregion + + + public Dictionary GetTableStruct_Modify() + { + throw new Exception("SQLite不支持 记录表结构更改时间!"); + } + + + public bool IsExistTable(string tableName) + { + return TableNames.Contains(tableName, StringComparer.OrdinalIgnoreCase); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName); + return DictColumnInfo.ContainsKey(strKey); + } + + public string GetColumnComment(string tableName, string columnName) + { + throw new Exception("Sqlite不支持表列描述!"); + } + + public string GetTableComment(string tableName) + { + throw new Exception("Sqlite不支持表列描述!"); + } + + public List GetColumns(string tableName) + { + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + public bool SetTableComment(string tableName, string comment) + { + throw new Exception("Sqlite不支持表列描述!"); + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + throw new Exception("Sqlite不支持表列描述!"); + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + + string drop_sql = string.Empty; + try + { + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName).ToLower(); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + + if (Db.Single(string.Format("select count(1) from {0}", tableName), 0) > 0) + { + throw new Exception("Sqlite不支持对已有数据的表的结构进行更改!"); + } + + var strKey = (tableName + "@" + columnName); + + string ctor_sql = string.Empty; + string drop_sql = string.Empty; + string rename_sql = string.Empty; + try + { + + //Sqlite 不支持直接删除列 的 处理方式,这里先创查询建 对应表的结构(排除要删除的列),然后删除原来的表,最后重命名新表的 表名称。 + //该方法适用于 表中 没有数据的 情况。 + { + var lstColName = this[tableName].Where(t => !t.Equals(columnName, StringComparison.OrdinalIgnoreCase)); + string temp_Name = tableName + DateTime.Now.ToString("yyyMMddhhmmssfff"); + ctor_sql = "create table {0} as select {1} from {2}"; + ctor_sql = string.Format(ctor_sql, temp_Name, string.Join(",", lstColName), tableName); + drop_sql = string.Format("drop table if exists {0}", tableName); + rename_sql = string.Format("alter table {0} rename to {1}", temp_Name, tableName); + Db.ExecSqlTran(ctor_sql, drop_sql, rename_sql); + + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + } + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, ctor_sql, drop_sql, rename_sql); + return false; + } + return true; + } + } +} diff --git a/MJTop.Data/DatabaseInfo/SqlServerDBInfo.cs b/MJTop.Data/DatabaseInfo/SqlServerDBInfo.cs new file mode 100644 index 0000000..e243c26 --- /dev/null +++ b/MJTop.Data/DatabaseInfo/SqlServerDBInfo.cs @@ -0,0 +1,432 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MJTop.Data.DatabaseInfo +{ + public class SqlServerDBInfo : IDBInfo + { + internal DB Db { get; set; } + + /// + /// 数据库工具 + /// + public Tool Tools + { + get; + private set; + } + + public SqlServerDBInfo(DB db) + { + this.Db = db; + Refresh(); + this.Tools = new Tool(db, this); + } + + public string DBName + { + get { return (Db.ConnectionStringBuilder as System.Data.SqlClient.SqlConnectionStringBuilder).InitialCatalog; } + } + + public string Version + { + get; + private set; + } + + //Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (X64) => 2016 + public double VersionNumber + { + get + { + var mat = Regex.Match(Version, @"\D*(\d{4})\D*", RegexOptions.Compiled); + double.TryParse(mat?.Groups[1]?.Value, out var res); + return res; + } + } + + public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); + + private NameValueCollection TableSchemas { get; set; } = new NameValueCollection(); + + public List TableNames { get; private set; } = new List(); + + public IgCaseDictionary TableInfoDict { get; private set; } + + public IgCaseDictionary> TableColumnNameDict { get; private set; } + + public IgCaseDictionary> TableColumnInfoDict { get; private set; } + + public IgCaseDictionary TableColumnComments { get; private set; } + + private IgCaseDictionary DictColumnInfo { get; set; } = new IgCaseDictionary(); + + public NameValueCollection Views { get; private set; } + + public NameValueCollection Procs { get; private set; } + + public List DBNames { get; private set; } = new List(); + + public ColumnInfo this[string tableName, string columnName] + { + get + { + ColumnInfo colInfo; + var strKey = (tableName + "@" + columnName); + DictColumnInfo.TryGetValue(strKey, out colInfo); + return colInfo; + } + } + + public List this[string tableName] + { + get + { + List colNames; + TableColumnNameDict.TryGetValue(tableName, out colNames); + return colNames; + } + } + + public bool Refresh() + { + this.DictColumnInfo = new IgCaseDictionary(); + this.TableNames = new List(); + this.TableInfoDict = new IgCaseDictionary(); + this.TableColumnNameDict = new IgCaseDictionary>(); + this.TableColumnInfoDict = new IgCaseDictionary>(); + this.TableColumnComments = new IgCaseDictionary(); + + string dbSql = "select name from sys.sysdatabases Order By name asc"; + + //var schemaSql = "Select distinct a.name From sys.schemas a left join sys.objects b on a.schema_id = b.schema_id WHERE b.type = 'U'"; + + string strSql = "SELECT * FROM (SELECT (Select Top 1 c.name From sys.schemas c Where c.schema_id = a.schema_id) + '.' + a.Name + '' as Name,(SELECT TOP 1 Value FROM sys.extended_properties b WHERE b.major_id=a.object_id and b.minor_id=0) AS value,(Select top 1 c.name From sys.schemas c Where c.schema_id = a.schema_id) scName From sys.objects a WHERE a.type = 'U' AND a.name <> 'sysdiagrams' AND a.name <> 'dtproperties' )K WHERE K.scName <> 'cdc' ORDER BY K.name ASC;"; + + string viewSql = "SELECT TABLE_NAME,VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS Order By TABLE_NAME asc"; + string procSql = "select name,[definition] from sys.objects a Left Join sys.sql_modules b On a.[object_id]=b.[object_id] Where a.type='P' And a.is_ms_shipped=0 And b.execute_as_principal_id Is Null And name !='sp_upgraddiagrams' Order By a.name asc"; + try + { + this.DBNames = Db.ReadList(dbSql); + + this.Version = Db.Scalar("select @@version", string.Empty)?.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)?[0]; + + //var lstSechma = Db.ReadList(schemaSql); + + var data = Db.GetDataTable(strSql); + + var dictGp = new Dictionary>(); + + foreach (DataRow dr in data.Rows) + { + this.TableComments[dr["name"].ToString()] = dr["value"].ToString(); + this.TableSchemas[dr["name"].ToString()] = dr["scname"].ToString(); + + dictGp.AddRange(dr["scname"].ToString(), dr["name"].ToString()); + } + + foreach (var item in dictGp) + { + this.TableNames.AddRange(item.Value.OrderBy(t => t)); + } + + this.Views = Db.ReadNameValues(viewSql); + this.Procs = Db.ReadNameValues(procSql); + + if (this.TableComments != null && this.TableComments.Count > 0) + { + List lstTask = new List(); + foreach (var tableName in this.TableNames) + { + Task task = Task.Run(() => + { + TableInfo tabInfo = new TableInfo(); + tabInfo.TableName = tableName; + tabInfo.TabComment = TableComments[tableName]; + + var colSql = @"select s.* FROM (SELECT a.colorder Colorder,a.name ColumnName,b.name TypeName,row_number() over (partition by a.name order by b.name) as group_idx,(case when (SELECT count(*) FROM sysobjects WHERE (name in (SELECT name FROM sysindexes WHERE (id = a.id) AND (indid in (SELECT indid FROM sysindexkeys WHERE (id = a.id) AND (colid in (SELECT colid FROM syscolumns WHERE (id = a.id) AND (name = a.name))))))) AND (xtype = 'PK'))>0 then 1 else 0 end) IsPK,(case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then 1 else 0 end) IsIdentity, CASE When b.name ='uniqueidentifier' Then 36 WHEN (charindex('int',b.name)>0) OR (charindex('time',b.name)>0) THEN NULL ELSE COLUMNPROPERTY(a.id,a.name,'PRECISION') end as [Length], CASE WHEN ((charindex('int',b.name)>0) OR (charindex('time',b.name)>0)) THEN NULL ELSE isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),null) end as Scale,(case when a.isnullable=1 then 1 else 0 end) CanNull,Replace(Replace(IsNull(e.text,''),'(',''),')','') DefaultVal,isnull(g.[value], ' ') AS DeText FROM syscolumns a left join systypes b on a.xtype=b.xusertype inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' left join syscomments e on a.cdefault=e.id left join sys.extended_properties g on a.id=g.major_id AND a.colid=g.minor_id And g.class=1 left join sys.extended_properties f on d.id=f.class and f.minor_id=0 left join sys.schemas c on d.uid = c.schema_id where b.name is not NULL and c.name + '.' + d.name = @tableName ) s WHERE s.group_idx = 1 order by s.colorder"; + + try + { + tabInfo.Colnumns = Db.GetDataTable(colSql, new { tableName = tableName }).ConvertToListObject(); + List lstColName = new List(); + NameValueCollection nvcColDeText = new NameValueCollection(); + foreach (ColumnInfo colInfo in tabInfo.Colnumns) + { + lstColName.Add(colInfo.ColumnName); + nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); + + var strKey = (tableName + "@" + colInfo.ColumnName); + this.DictColumnInfo.Add(strKey, colInfo); + + if (colInfo.IsPK) + { + tabInfo.PriKeyColName = colInfo.ColumnName; + if (colInfo.IsIdentity) + { + tabInfo.PriKeyType = PrimaryKeyType.AUTO; + } + else + { + tabInfo.PriKeyType = PrimaryKeyType.SET; + } + } + + Global.Dict_SqlServer_DbType.TryGetValue(colInfo.TypeName, out DbType type); + colInfo.DbType = type; + } + this.TableInfoDict.Add(tableName, tabInfo); + this.TableColumnNameDict.Add(tableName, lstColName); + this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); + this.TableColumnComments.Add(tableName, nvcColDeText); + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, "查询过程出现失败,账号的权限可能不足!!!"); + } + }); + lstTask.Add(task); + if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) + { + Task.WaitAny(lstTask.ToArray()); + lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); + } + } + Task.WaitAll(lstTask.ToArray()); + } + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, "查询过程出现失败,账号的权限可能不足!!!"); + return false; + } + return this.TableComments.Count == this.TableInfoDict.Count; + } + + + public Dictionary GetTableStruct_Modify() + { + string strSql = "select name,modify_date from sys.objects where type='U' and name <> 'dtproperties' and name <>'sysdiagrams' order by modify_date desc"; + return Db.ReadDictionary(strSql); + } + + public bool IsExistTable(string tableName) + { + return TableNames.Contains(tableName, StringComparer.OrdinalIgnoreCase); + } + + public bool IsExistColumn(string tableName, string columnName) + { + var strKey = (tableName + "@" + columnName); + return DictColumnInfo.ContainsKey(strKey); + } + + public string GetColumnComment(string tableName, string columnName) + { + ColumnInfo colInfo = null; + var strKey = (tableName + "@" + columnName); + if (DictColumnInfo.TryGetValue(strKey, out colInfo)) + { + return colInfo.DeText; + } + else + { + throw new ArgumentException(tableName + "/" + columnName + ",表的列不存在!"); + } + } + + public string GetTableComment(string tableName) + { + Db.CheckTabStuct(tableName); + return TableComments[tableName]; + } + + public List GetColumns(string tableName) + { + Db.CheckTabStuct(tableName); + List colInfos = null; + TableColumnInfoDict.TryGetValue(tableName, out colInfos); + return colInfos; + } + + public bool SetTableComment(string tableName, string comment) + { + Db.CheckTabStuct(tableName); + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = @" if exists ( + SELECT case when a.colorder = 1 then d.name else '' end as 表名, case when a.colorder = 1 then isnull(f.value, '') else '' end as 表说明 + FROM syscolumns a + inner join sysobjects d + on a.id = d.id + and d.xtype = 'U' + and d.name <> 'sys.extended_properties' + left join sys.extended_properties f + on a.id = f.major_id + and f.minor_id = 0 + where a.colorder = 1 and d.name<>'sysdiagrams' and d.name=N'{0}' and f.value is not null + ) + exec sp_updateextendedproperty N'MS_Description', N'{1}', N'schema', N'{2}', N'table', N'{0}', NULL, NULL + else + exec sp_addextendedproperty N'MS_Description', N'{1}', N'schema', N'{2}', N'table', N'{0}', NULL, NULL"; + + var tabName = tableName.Split('.').LastOrDefault(); + upsert_sql = string.Format(upsert_sql, tabName, comment, TableSchemas[tableName]); + Db.ExecSql(upsert_sql); + + TableComments[tableName] = comment; + + var tabInfo = TableInfoDict[tableName]; + tabInfo.TabComment = comment; + TableInfoDict[tableName] = tabInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool SetColumnComment(string tableName, string columnName, string comment) + { + Db.CheckTabStuct(tableName, columnName); + + string upsert_sql = string.Empty; + comment = (comment ?? string.Empty).Replace("'", ""); + try + { + upsert_sql = @"if exists (select * from ::fn_listextendedproperty (NULL, 'schema', N'{3}', 'table', N'{0}', 'column', default) where objname = N'{1}') EXEC sp_updateextendedproperty 'MS_Description',N'{2}','schema',{3},'table',N'{0}','column',{1} else EXEC sp_addextendedproperty @name=N'MS_Description' , @value=N'{2}' ,@level0type=N'SCHEMA', @level0name=N'{3}', @level1type=N'TABLE', @level1name=N'{0}', @level2type=N'COLUMN', @level2name=N'{1}' "; + var tabName = tableName.Split('.').LastOrDefault(); + upsert_sql = string.Format(upsert_sql, tabName, columnName, comment, this.TableSchemas[tableName]); + Db.ExecSql(upsert_sql); + + List lstColInfo = TableColumnInfoDict[tableName]; + + NameValueCollection nvcColDesc = new NameValueCollection(); + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase)) + { + t.DeText = comment; + } + nvcColDesc.Add(t.ColumnName, t.DeText); + }); + + TableColumnInfoDict.Remove(tableName); + TableColumnInfoDict.Add(tableName, lstColInfo); + + TableColumnComments.Remove(tableName); + TableColumnComments.Add(tableName, nvcColDesc); + + ColumnInfo colInfo = DictColumnInfo[tableName + "@" + columnName]; + colInfo.DeText = comment; + DictColumnInfo[tableName + "@" + columnName] = colInfo; + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); + return false; + } + return true; + } + + public bool DropTable(string tableName) + { + Db.CheckTabStuct(tableName); + + string drop_sql = string.Empty; + try + { + + drop_sql = "drop table " + tableName; + Db.ExecSql(drop_sql); + + this.TableComments.Remove(tableName); + + this.TableNames = TableComments.AllKeys.ToList(); + + this.TableInfoDict.Remove(tableName); + this.TableColumnInfoDict.Remove(tableName); + this.TableColumnComments.Remove(tableName); + + var lstColName = TableColumnNameDict[tableName]; + + foreach (var colName in lstColName) + { + var strKey = (tableName + "@" + colName); + this.DictColumnInfo.Remove(strKey); + } + + this.TableColumnNameDict.Remove(tableName); + + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + public bool DropColumn(string tableName, string columnName) + { + Db.CheckTabStuct(tableName, columnName); + var strKey = (tableName + "@" + columnName); + + string drop_sql = string.Empty; + try + { + drop_sql = "alter table {0} drop column {1}"; + drop_sql = string.Format(drop_sql, tableName, columnName); + Db.ExecSql(drop_sql); + + this.DictColumnInfo.Remove(strKey); + + var nvc = TableColumnComments[tableName]; + nvc.Remove(columnName); + TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); + + var lstColInfo = TableColumnInfoDict[tableName]; + ColumnInfo curColInfo = null; + lstColInfo.ForEach(t => + { + if (t.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase)) + { + curColInfo = t; + + //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 + if (curColInfo.IsPK) + { + var tabInfo = TableInfoDict[tableName]; + tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; + tabInfo.PriKeyColName = null; + TableInfoDict[tableName] = tabInfo; + } + + return; + } + }); + lstColInfo.Remove(curColInfo); + TableColumnInfoDict[tableName] = lstColInfo; + } + catch (Exception ex) + { + LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); + return false; + } + return true; + } + + + } +} diff --git a/MJTop.Data/Entity/ColumnInfo.cs b/MJTop.Data/Entity/ColumnInfo.cs new file mode 100644 index 0000000..a1ed001 --- /dev/null +++ b/MJTop.Data/Entity/ColumnInfo.cs @@ -0,0 +1,156 @@ +using System; +using System.ComponentModel; +using System.Data; + +namespace MJTop.Data +{ + /// + /// 列信息 + /// + [Serializable] + public class ColumnInfo + { + /// + /// 序号 + /// + [DisplayName("序号")] + public int Colorder + { + get; + set; + } + + /// + /// 列名 + /// + [DisplayName("列名")] + public string ColumnName + { + get; + set; + } + + + /// + /// 数据类型 + /// + [DisplayName("数据类型")] + public string TypeName + { + get; + set; + } + + /// + /// 列说明 + /// + [DisplayName("列说明")] + public string DeText + { + get; + set; + } + + /// + /// 字段长度 max 或特殊数据类型 使用 -1 表示! + /// + [DisplayName("长度")] + public long? Length + { + get; + set; + } + + /// + /// 小数点后保留位数 + /// + [DisplayName("小数位数")] + public int? Scale + { + get; + set; + } + + + /// + /// 是否自增列 + /// + [DisplayName("是否为自增")] + public bool IsIdentity + { + get; + set; + } + + + + /// + /// 是否主键 + /// + [DisplayName("是否为主键")] + public bool IsPK + { + get; + set; + } + + /// + /// 是否可为Null + /// + [DisplayName("是否可为空")] + public bool CanNull + { + get; + set; + } + + /// + /// 默认值 + /// + [DisplayName("默认值")] + public string DefaultVal + { + get; + set; + } + + /// + /// 近似类型 + /// + [DisplayName("近似类型")] + public LikeType LikeType + { + get + { + if (this.DbType == DbType.Decimal || this.DbType == DbType.Double + || this.DbType == DbType.Int16 + || this.DbType == DbType.Int32 + || this.DbType == DbType.Int64 + ) + { + return LikeType.Number; + } + else if (this.DbType == DbType.Date || this.DbType == DbType.DateTime + || this.DbType == DbType.DateTime2 || this.DbType == DbType.DateTimeOffset) + { + return LikeType.DateTime; + } + else + { + return LikeType.String; + } + } + } + + /// + /// DbType类型 + /// + [DisplayName("DbType类型")] + public DbType DbType + { + get; + set; + } + + } +} diff --git a/MJTop.Data/Entity/TableInfo.cs b/MJTop.Data/Entity/TableInfo.cs new file mode 100644 index 0000000..9d53dfc --- /dev/null +++ b/MJTop.Data/Entity/TableInfo.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace MJTop.Data +{ + /// + /// 表信息 + /// + [Serializable] + public class TableInfo + { + /// + /// 表名称 + /// + public string TableName { get; set; } + + /// + /// 表描述 + /// + public string TabComment { get; set; } + + /// + /// 该表包含的所有列 + /// + public List Colnumns { get; set; } + + /// + /// 主键列名 + /// + public string PriKeyColName { get; set; } + + /// + /// 主键类型 + /// + public PrimaryKeyType PriKeyType { get; set; } + } +} diff --git a/MJTop.Data/Enums.cs b/MJTop.Data/Enums.cs new file mode 100644 index 0000000..878115e --- /dev/null +++ b/MJTop.Data/Enums.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 数据库类型 + /// + public enum DBType + { + /// + /// SqlServer数据库 + /// + SqlServer, + /// + /// MySql数据库 + /// + MySql, + /// + /// Oracle数据库(内置使用 Oracle.ManagedDataAccess 客户端) + /// + Oracle, + /// + /// Oracle数据库(内置使用 DDTek.Oracle 客户端) + /// + OracleDDTek, + /// + /// PostgreSql数据库 + /// + PostgreSql, + /// + /// SQLite数据库 + /// + SQLite, + /// + /// DB2数据库 + /// + DB2 + } + + /// + /// 主键类型 + /// + public enum PrimaryKeyType + { + /// + /// 没有主键或未知 + /// + UNKNOWN, + + /// + /// 主键的值是自增方式 + /// + AUTO, + + /// + /// 主键的值是插入前设置的方式 + /// + SET + } + + /// + /// 保存类型 + /// + public enum SaveType + { + /// + /// 插入 + /// + Insert, + /// + /// 更新 + /// + Update + } + + /// + /// 相似类型 + /// + public enum LikeType + { + /// + /// 数值类型 + /// + Number, + /// + /// 字符类型 + /// + String, + /// + /// 日期类型 + /// + DateTime + } + + + /// + /// 分组计算类型 + /// + //public enum GroupCalc + //{ + // /// + // /// 分组求每组总数 + // /// + // COUNT, + // /// + // /// 分组求每组总和 + // /// + // SUM, + // /// + // /// 分组求每组平均值 + // /// + // AVG, + // /// + // /// 分组求每组最大值 + // /// + // MAX, + // /// + // /// 分组求每组最小值 + // /// + // MIN + //} +} diff --git a/MJTop.Data/ExcludeColumn.cs b/MJTop.Data/ExcludeColumn.cs new file mode 100644 index 0000000..646041b --- /dev/null +++ b/MJTop.Data/ExcludeColumn.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 排除列 + /// + public class ExcludeColumn + { + /// + /// 表列集合 + /// + internal NameValueCollection Coll { get; set; } + + private DB Db { get; set; } + + internal ExcludeColumn(DB db) + { + this.Db = db; + this.Coll = new NameValueCollection(); + } + + + /// + /// 需排除的 表名与多个列名 + /// + /// 表名 + /// 一个或多个列名 + public void Add(string tableName, params string[] columnNames) + { + Db.CheckTabStuct(tableName, columnNames); + + if (columnNames != null && columnNames.Length > 0) + { + foreach (var columnName in columnNames) + { + this.Coll.Add(tableName, columnName); + } + } + } + + + /// + /// 需排除的 表名与列名集合 + /// + /// 表列集合 + public void AddRange(NameValueCollection tableColumns) + { + if (tableColumns == null) + { + foreach (var tableName in tableColumns.AllKeys) + { + string[] columnNames; + if (tableColumns.TryGetValues(tableName, out columnNames)) + { + Db.CheckTabStuct(tableName, columnNames); + + foreach (var columnName in columnNames) + { + this.Coll.Add(tableName, columnName); + } + + } + } + } + } + + /// + /// 清空排除项 + /// + public void Clear() + { + this.Coll.Clear(); + } + + /// + /// 删除对应表的 排除项 + /// + /// 表名 + public void Remove(string tableName) + { + Db.CheckTabStuct(tableName); + this.Coll.Remove(tableName); + } + + /// + /// 删除指定的排除项 + /// + /// 表名 + /// 列名 + /// 返回bool + public bool Remove(string tableName,string columnName) + { + Db.CheckTabStuct(tableName, columnName); + + return this.Coll.Remove(tableName, columnName); + } + } +} diff --git a/MJTop.Data/Ext.Data.cs b/MJTop.Data/Ext.Data.cs new file mode 100644 index 0000000..105802b --- /dev/null +++ b/MJTop.Data/Ext.Data.cs @@ -0,0 +1,555 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 数据对象扩展方法类 + /// + public static class ExtData + { + #region DataTable 转实体类型数据 方法1 + + + /// + /// 根据类型的属性集合,匹配修改DataTable列名(用来处理属性名与列名不一致的大小写问题) + /// + /// 对象类型 + /// 要修改列名的DataTable + //public static void CompareModify

(this DataTable data) + //{ + // var propNames = TypeInfo

.PropNames; + + // foreach (string propName in propNames) + // { + // if (data.Columns[propName] != null) + // { + // data.Columns[propName].ColumnName = propName; + // } + // } + //} + + ///

+ /// DataTable 转实体类型数据 + /// + /// 实体类型 + /// 数据源 + /// 是否匹配修改DataTable列名(用来处理属性名与列名不一致的大小写问题) + /// 对象集合 + //public static List ToList(this DataTable dt, bool IsCompareModify = false) where T : class + //{ + // if (dt == null || dt.Rows.Count <= 0) + // { + // return new List(); + // } + + // if (IsCompareModify) + // { + // CompareModify(dt); + // } + + // List list = new List(); + // if (dt == null) return list; + // DataTableEntityBuilder eblist = DataTableEntityBuilder.CreateBuilder(dt.Rows[0]); + // foreach (DataRow info in dt.Rows) + // list.Add(eblist.Build(info)); + // dt.Dispose(); + // dt = null; + // return list; + //} + + //internal class DataTableEntityBuilder + //{ + // private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) }); + // private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) }); + // private delegate T Load(DataRow dataRecord); + + // private Load handler; + // private DataTableEntityBuilder() { } + + // public T Build(DataRow dataRecord) + // { + // return handler(dataRecord); + // } + + // public static DataTableEntityBuilder CreateBuilder(DataRow dataRow) + // { + // DataTableEntityBuilder dynamicBuilder = new DataTableEntityBuilder(); + // DynamicMethod method = new DynamicMethod("DynamicCreateEntity", typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true); + // ILGenerator generator = method.GetILGenerator(); + // LocalBuilder result = generator.DeclareLocal(typeof(T)); + // generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); + // generator.Emit(OpCodes.Stloc, result); + + // for (int index = 0; index < dataRow.ItemArray.Length; index++) + // { + // PropertyInfo propertyInfo = typeof(T).GetProperty(dataRow.Table.Columns[index].ColumnName); + // Label endIfLabel = generator.DefineLabel(); + // if (propertyInfo != null && propertyInfo.GetSetMethod() != null) + // { + // generator.Emit(OpCodes.Ldarg_0); + // generator.Emit(OpCodes.Ldc_I4, index); + // generator.Emit(OpCodes.Callvirt, isDBNullMethod); + // generator.Emit(OpCodes.Brtrue, endIfLabel); + // generator.Emit(OpCodes.Ldloc, result); + // generator.Emit(OpCodes.Ldarg_0); + // generator.Emit(OpCodes.Ldc_I4, index); + // generator.Emit(OpCodes.Callvirt, getValueMethod); + // generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); + // generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod()); + // generator.MarkLabel(endIfLabel); + // } + // } + // generator.Emit(OpCodes.Ldloc, result); + // generator.Emit(OpCodes.Ret); + // dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load)); + // return dynamicBuilder; + // } + //} + #endregion + + #region DataTable 转实体类型数据 方法2 + + /// + /// 反射 将行数据转换为实体对象 + /// + /// 实体类型 + /// 行数据 + /// 实体对象 + public static T ConvertToObjectFromDR(this DataRow row) + { + if (row == null) + { + return default(T); + } + T obj = (T)Activator.CreateInstance(typeof(T)); + obj = ConvertToObjectFromDR(row, obj); + return obj; + } + + /// + /// 反射 将行数据转换为实体对象 + /// + /// 实体类型 + /// 行数据 + /// 对象 + /// + private static T ConvertToObjectFromDR(this DataRow row, T obj) + { + if (row == null) + { + return default(T); + } + Type type = TypeInfo.TyThis; + System.Reflection.PropertyInfo[] propInfo = TypeInfo.Props; + for (int i = 0; i < propInfo.Length; i++) + { + if (row.Table.Columns[propInfo[i].Name] != null && row[propInfo[i].Name] != System.DBNull.Value && propInfo[i].CanWrite) + { + object objVal = row[propInfo[i].Name]; + Type typeVal = Nullable.GetUnderlyingType(propInfo[i].PropertyType) ?? propInfo[i].PropertyType; + int mark = 0; + try + { + if (typeVal.Name == "Guid") + { + mark = 1; + propInfo[i].SetValue(obj, Guid.Parse(objVal.ToString()), null); + } + else + { + if (typeVal.IsEnum && objVal != null) + { + Type tyEnum = Enum.GetUnderlyingType(typeVal); + if (tyEnum.IsAssignableFrom(typeof(int))) + { + mark = 2; + propInfo[i].SetValue(obj, Enum.Parse(typeVal, objVal.ToString()), null); + } + else + { + mark = 3; + propInfo[i].SetValue(obj, Convert.ChangeType(objVal, typeVal), null); + } + } + else + { + if (objVal == null || string.IsNullOrWhiteSpace(objVal.ToString())) + { + mark = 4; + if (propInfo[i].PropertyType.IsNullableType()) + { + objVal = null; + } + propInfo[i].SetValue(obj, objVal, null); + } + else + { + mark = 5; + propInfo[i].SetValue(obj, Convert.ChangeType(objVal, typeVal), null); + } + } + } + } + catch (Exception ex) + { + throw new ArgumentException("SetValue出错!(" + mark + ")", propInfo[i].Name + ":" + objVal, ex); + } + } + } + return obj; + } + + + /// + /// 反射将datatable转换为List对象 + /// + /// 实体类型 + /// datatable数据 + /// List对象 + public static List ConvertToListObject(this DataTable @this) + { + if (@this == null || @this.Rows.Count <= 0) + { + return new List(); + } + List objs = new List(); + for (int i = 0; i < @this.Rows.Count; i++) + { + T obj = (T)Activator.CreateInstance(typeof(T)); + obj = ConvertToObjectFromDR(@this.Rows[i], obj); + objs.Add(obj); + } + return objs; + } + + + #endregion + + + #region DataTable 转实体类型数据 方法3 + + /// + /// DataTable生成实体 + /// + /// + /// + /// + public static IEnumerable ToListModel(this DataTable dataTable) where T : class, new() + { + return BWofter.Converters.Data.DataTableConverter.ToEntities(dataTable); + } + + #endregion + + #region DataTable DataRow DataColumn 扩展 + + /// + /// 改变:将DataTable的其中 某列(默认第一列)数据 存储为 List + /// + /// 数据类型 + /// 数据源 + /// 列名,默认第一列 + /// List数据 + public static List TransList(this DataTable @this, string columnName = null) + { + List lst = new List(); + if (@this == null || @this.Rows.Count < 0) + { + return lst; + } + else + { + foreach (DataRow dr in @this.Rows) + { + if (columnName == null) + { + lst.Add(dr[0].ChangeType()); + } + else + { + lst.Add(dr[columnName].ChangeType()); + } + } + } + return lst; + } + + + /// + /// 将DataTable的其中 两列数据 存储为 Dictionary + /// + /// 数据源 + /// 列1(键) + /// 列2(值) + /// Dictionary集合 + public static Dictionary TransDict(this DataTable @this, string ColumnNameKey, string ColumnVal) + { + Dictionary dict = new Dictionary(); + if (@this == null || @this.Rows.Count <= 0) + { + return dict; + } + else + { + foreach (DataRow dr in @this.Rows) + { + TKey k = (TKey)dr[ColumnNameKey].ChangeType(typeof(TKey)); + TVal v = (TVal)dr[ColumnVal].ChangeType(typeof(TVal)); + dict.Add(k, v); + } + } + return dict; + } + + + /// + /// 将DataTable的其中 两列数据 存储为 NameValueCollection + /// + /// 数据源 + /// 列1(键) + /// 列2(值) + /// NameValueCollection集合 + public static NameValueCollection MapperNameValues(this DataTable @this, string ColumnNameKey, string ColumnVal) + { + NameValueCollection nvc = new NameValueCollection(); + if (@this == null || @this.Rows.Count <= 0) + { + return nvc; + } + else + { + foreach (DataRow dr in @this.Rows) + { + nvc.Add(dr[ColumnNameKey].ToString(), dr[ColumnVal].ToString()); + } + } + return nvc; + } + + + /// + ///给当前DataTable增加列名 + /// + /// DataTable对象 + /// 列信息 + /// 增加列后的DataTable + public static DataTable AddColumns(this DataTable @this, params KeyValuePair[] columns) + { + if (@this == null) + { + @this = new DataTable(); + } + if (columns != null && columns.Length > 0) + { + foreach (var col in columns) + { + @this.Columns.Add(new DataColumn(col.Key, col.Value)); + } + } + return @this; + } + + /// + /// 获取首行数据 + /// + /// DataTable数据 + /// 首行数据 + public static DataRow FirstRow(this DataTable @this) + { + if (@this == null) + { + throw new ArgumentNullException("DataTable不能为null"); + } + if (@this.Rows.Count > 0) + { + return @this.Rows[0]; + } + return null; + } + + /// + /// 获取最后一行数据 + /// + /// DataTable数据 + /// 最后一行数据 + public static DataRow LastRow(this DataTable @this) + { + if (@this == null) + { + throw new ArgumentNullException("DataTable不能为null"); + } + + if (@this.Rows.Count > 0) + { + return @this.Rows[@this.Rows.Count - 1]; + } + return null; + } + + /// + /// 添加列数据 + /// + /// 行集合 + /// 行数据 + public static void AddRange(this DataRowCollection @this, IEnumerable drs) + { + foreach (DataRow dr in drs) + { + @this.Add(dr.ItemArray); + } + } + + /// + /// 获取所有DataColumn + /// + /// 列集合 + /// 列数组 + public static DataColumn[] ToArray(this DataColumnCollection columnCollection) + { + List lstDC = new List(); + foreach (DataColumn dc in columnCollection) + { + lstDC.Add(dc); + } + return lstDC.ToArray(); + } + + + + /// + /// 将DataRow转为 dynamic 类型对象 + /// + /// 行数据 + /// + public static dynamic ToExpandoObject(this DataRow @this) + { + dynamic entity = new ExpandoObject(); + var expandoDict = (IDictionary)entity; + foreach (DataColumn column in @this.Table.Columns) + { + expandoDict.Add(column.ColumnName, @this[column]); + } + return expandoDict; + } + + #endregion + + #region DbParameterCollection 扩展 + /// + /// DbParameterCollection 转数组 + /// + /// DbParameter集合 + /// 数组形式的 DbParameter + public static DbParameter[] ToArray(this DbParameterCollection parameterCollection) + { + if (parameterCollection == null || parameterCollection.Count <= 0) + { + return null; + } + + DbParameter[] paras = new DbParameter[parameterCollection.Count]; + + for (int j = 0; j < parameterCollection.Count; j++) + { + paras[j] = parameterCollection[j]; + } + + return paras; + } + #endregion + + #region DBType 扩展 + + /// + /// 获取数据库类型对应的 数据类型/Dbtype的字典 + /// + /// 数据库类型 + /// 数据类型/Dbtype的字典 + public static Dictionary DictDbType(this DBType dbType) + { + switch (dbType) + { + case DBType.SqlServer: + return Global.Dict_SqlServer_DbType; + case DBType.MySql: + return Global.Dict_MySql_DbType; + case DBType.Oracle: + return Global.Dict_Oracle_DbType; + case DBType.OracleDDTek: + return Global.Dict_Oracle_DbType; + case DBType.PostgreSql: + return Global.Dict_PostgreSql_DbType; + case DBType.SQLite: + return Global.Dict_Sqlite_DbType; + default: + throw new ArgumentException("未知数据库类型!"); + } + } + + /// + /// 数据库类型,列 对应的DbType + /// + /// + /// + /// DbType + //public static DbType GetDbType(this DBType dbType, ColumnInfo colInfo) + //{ + // DbType dType; + // if (dbType.DictDbType().TryGetValue(colInfo.TypeName, out dType)) + // { + // return dType; + // } + // return DbType.AnsiString; + //} + + /// + /// 获取当前数据库类型的参数化字符 + /// + /// 数据库类型 + /// 参数化字符 + public static string ParameterChar(this DBType dbType) + { + return Global.ParameterCharMap[dbType]; + } + + + #endregion + + /// + /// 引用类型对象的序列化(深度克隆) + /// 注:使用之前先将对象标记为 [Serializable] 可序列化。 + /// + /// 对象类型 + /// 对象 + /// 返回深度克隆后的新对象 + internal static T Clone(this T RealObject) + { + if (RealObject == null) + { + return default(T); + } + using (Stream objectStream = new MemoryStream()) + { + //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制 + IFormatter formatter = new BinaryFormatter(); + formatter.Serialize(objectStream, RealObject); + objectStream.Seek(0, SeekOrigin.Begin); + return (T)formatter.Deserialize(objectStream); + } + } + } +} diff --git a/MJTop.Data/Ext.Object.cs b/MJTop.Data/Ext.Object.cs new file mode 100644 index 0000000..bbeec33 --- /dev/null +++ b/MJTop.Data/Ext.Object.cs @@ -0,0 +1,530 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 对象扩展方法类 + /// + public static class ExtObject + { + #region 数据类型 + + /// + /// 对象类型转换 + /// + /// 返回的数据,数据的类型 + /// 当前值 + /// 转换后的对象 + internal static T ChangeType(this object @this) + { + object result = null; + + Type toType = typeof(T); + + if (@this == null || @this == DBNull.Value) + { + + if ((toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))) + { + return default(T); + } + else if (toType.IsValueType) + { + throw new Exception("不能将null值转换为" + toType.Name + "类型!"); + } + else + { + return default(T); + } + } + else + { + if ((toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))) + { + toType = Nullable.GetUnderlyingType(toType) ?? toType; + } + + if (toType.Name == "Object") + { + return (T)@this; + } + + if (toType.IsEnum) + { + result = Enum.Parse(toType, @this.ToString(), true); + } + else if (toType.IsAssignableFrom(typeof(Guid))) + { + result = Guid.Parse(@this.ToString()); + } + else + { + result = Convert.ChangeType(@this, toType); + } + return (T)result; + } + + } + + /// + /// 对象类型转换,转换失败,返回默认值 + /// + /// 返回类型 + /// 当前值 + /// 转换失败,返回的默认值 + /// 返回转换后的对象或转换失败后的默认值 + internal static T ChangeType(this object @this, T def) + { + try + { + if (@this == null || string.IsNullOrWhiteSpace(@this.ToString())) + { + return def; + } + return ChangeType(@this); + } + catch + { + return def; + } + } + + + /// + /// 对象类型转换 + /// + /// 当前值 + /// 指定类型的类型 + /// 转换后的对象 + internal static object ChangeType(this object @this, Type conversionType) + { + Type type = conversionType; + + type = Nullable.GetUnderlyingType(type) ?? type; + + if (type != null) + { + object result = null; + + if (@this != null && @this != DBNull.Value) + { + if (type.IsAssignableFrom(typeof(string))) + { + result = @this.ToString(); + } + else if (type.IsEnum) + { + result = Enum.Parse(type, @this.ToString(), true); + } + else if (type.IsAssignableFrom(typeof(Guid))) + { + result = Guid.Parse(@this.ToString()); + } + else + { + result = Convert.ChangeType(@this, type); + } + } + else + { + if (type.IsAssignableFrom(typeof(string)) || type.IsAssignableFrom(typeof(object))) + { + result = null; + } + else + { + throw new Exception("不能将null值转换为" + type.Name + "类型!"); + } + } + return result; + } + return Convert.ChangeType(@this, type); + } + + + /// + /// 对象类型转换 + /// + /// 当前值 + /// 指定类型的类型 + /// 转换失败,返回的默认值 + /// 转换后的对象 + internal static object ChangeType(this object @this, Type conversionType, object def) + { + try + { + if (@this == null) + { + return def; + } + + return ChangeType(@this, conversionType); + } + catch + { + return def; + } + } + + /// + /// 判定 对象的类型 是否是 可空类型 + /// + /// 对象类型 + /// 是否是 可空类型 + public static bool IsNullableType(this Type @this) + { + return (@this.IsGenericType && @this. + GetGenericTypeDefinition().Equals + (typeof(Nullable<>))); + } + + /// + /// 返回当前对象的数组形式 + /// + /// 当前数据类型 + /// 当前对象 + /// 当前对象的数组形式 + internal static T[] TransArray(this T @this) + { + return new T[] { @this }; + } + + /// + /// 返回当前对象的列表形式 + /// + /// 当前数据类型 + /// 当前对象 + /// 当前对象的列表形式 + internal static List TransList(this T @this) + { + List lst = new List(); + lst.Add(@this); + return lst; + } + + #endregion + + #region NameValueCollection 扩展 + + /// + /// 返回可写的 NameValueCollection集合 + /// + public static NameValueCollection NoReadonly(this NameValueCollection @this) + { + if (@this == null) + { + return new NameValueCollection(); + } + return new NameValueCollection(@this); + } + + /// + /// 是否包含某个键 + /// + /// NameValueCollection集合 + /// 键名 + /// value值 是否 允许 为空字符串 + /// 返回bool + public static bool ContainsKey(this NameValueCollection @this, string keyName, bool valueIsSpace = false) + { + if (@this == null || @this.Count <= 0) + { + return false; + } + + string[] values = @this.GetValues(keyName); + + if (values == null) + { + return false; + } + + if (valueIsSpace) + { + return true; + } + + int ct = 0; + foreach (var val in values) + { + if (string.IsNullOrWhiteSpace(val)) + { + ct++; + } + } + return !(ct == values.Length); + } + + /// + /// 是否包含某个值 + /// + /// NameValueCollection集合 + /// 值 + /// 是否忽略大小写 + /// 返回bool + public static bool ContainsValue(this NameValueCollection @this, string value, bool ignoreCase = true) + { + if (@this == null || @this.Count <= 0) + { + return false; + } + + StringComparison strCmp = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.CurrentCulture; + foreach (var keyName in @this.AllKeys) + { + foreach (string val in @this.GetValues(keyName)) + { + if (val.Equals(value, strCmp)) + { + return true; + } + } + } + return false; + } + + + /// + /// 尝试获取 某个键的值 + /// + /// NameValueCollection集合 + /// 键名 + /// values值数组 + /// 返回bool + public static bool TryGetValues(this NameValueCollection @this, string keyName, out string[] values) + { + values = null; + return TryGetValues(@this, keyName, false, out values); + } + + /// + /// 尝试获取 某个键的值 + /// + /// NameValueCollection集合 + /// 键名 + /// value值 是否 允许 为空字符串 + /// values值数组 + /// 返回bool + public static bool TryGetValues(this NameValueCollection @this, string keyName, bool valueIsSpace, out string[] values) + { + values = null; + + if (@this == null || @this.Count <= 0) + { + return false; + } + + values = @this.GetValues(keyName); + + if (values == null) + { + return false; + } + + if (valueIsSpace && values.Length > 0) + { + return true; + } + else + { + int ct = 0; + foreach (var val in values) + { + if (string.IsNullOrWhiteSpace(val)) + { + ct++; + } + } + return !(ct == values.Length); + } + } + + + + + + /// + /// 尝试获取 某个键的值 + /// + /// NameValueCollection集合 + /// 键名 + /// 对应的值 + /// 返回bool + public static bool TryGetValue(this NameValueCollection @this, string keyName, out string value) + { + value = null; + return TryGetValue(@this, keyName, false, out value); + } + + + /// + /// 尝试获取 某个键的值 + /// + /// NameValueCollection集合 + /// 键名 + /// value值 是否 允许 为空字符串 + /// value值/param> + /// 返回bool + public static bool TryGetValue(this NameValueCollection @this, string keyName, bool valueIsSpace, out string value) + { + value = null; + + if (@this == null || @this.Count <= 0) + { + return false; + } + + value = @this.Get(keyName); + + if (value == null) + { + return false; + } + + if (valueIsSpace && value.Length > 0) + { + return true; + } + else + { + if (string.IsNullOrWhiteSpace(value)) + { + return false; + } + return true; + } + } + + /// + /// 移除集合中的某个键的某个值 + /// + /// NameValueCollection集合 + /// 键名 + /// value值 + /// 返回bool + public static bool Remove(this NameValueCollection @this, string keyName,string value) + { + if (@this == null || @this.Count <= 0) + { + return false; + } + + var lstVals = @this.GetValues(keyName)?.ToList(); + + if (lstVals == null) + { + return false; + } + + int index = lstVals.IndexOfCompare(value); + + if (index <= -1) + { + return false; + } + + lstVals.RemoveAt(lstVals.IndexOfCompare(value)); + @this.Remove(keyName); + + for (int j = 0; j < lstVals.Count; j++) + { + @this.Add(keyName, lstVals[j]); + } + return true; + } + + + #endregion + + /// + /// 查询集合中的某个元素的索引 + /// + /// IEnumerable string集合 + /// 元素值 + /// 规则,默认忽略大小写 + /// 索引位置 + public static int IndexOfCompare(this IEnumerable @this, string item, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) + { + if (@this == null || !@this.Any()) + { + return -1; + } + + int j = -1; + foreach (string curStr in @this) + { + j++; + + if ( + (curStr == null && item == null) || + (curStr != null && curStr.Equals(item, comparisonType)) + ) + + { + return j; + } + } + return -1; + } + + + /// + /// 一个Key对应多个Value值的存储 + /// + /// key类型 + /// V类型 + /// 当前IDictionary + /// key值 + /// V类型值 + public static void AddRange(this IDictionary> @this, K key, params V[] values) + { + List lstValue = new List(); + + if (!@this.TryGetValue(key, out lstValue)) + { + lstValue = new List(); + lstValue.AddRange(values); + @this.Add(key, lstValue); + } + else + { + lstValue.AddRange(values); + @this.Remove(key); + @this.Add(key, lstValue); + } + } + + + /// + /// 打印 str 加入joinChar 进行cnt次打印 + /// + /// 当前字符串 + /// 连发次数 + /// 加入的字符 + /// + public static string Repeater(this string str, int cnt, string joinChar) + { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < cnt; j++) + { + if (j < cnt - 1) + { + sb.Append(str + joinChar); + } + else + { + sb.Append(str); + } + } + return sb.ToString(); + } + } +} diff --git a/MJTop.Data/Global.cs b/MJTop.Data/Global.cs new file mode 100644 index 0000000..87eae1e --- /dev/null +++ b/MJTop.Data/Global.cs @@ -0,0 +1,969 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 全局静态字典 + /// + public class Global + { + /// + /// Type类型对应的DbType类型 字典 + /// + public readonly static Dictionary TypeMap = new Dictionary + { + [typeof(byte)] = DbType.Byte, + [typeof(sbyte)] = DbType.SByte, + [typeof(short)] = DbType.Int16, + [typeof(ushort)] = DbType.UInt16, + [typeof(int)] = DbType.Int32, + [typeof(uint)] = DbType.UInt32, + [typeof(long)] = DbType.Int64, + [typeof(ulong)] = DbType.UInt64, + [typeof(float)] = DbType.Single, + [typeof(double)] = DbType.Decimal, + [typeof(decimal)] = DbType.Decimal, + [typeof(bool)] = DbType.Boolean, + [typeof(string)] = DbType.String, + [typeof(char)] = DbType.StringFixedLength, + [typeof(Guid)] = DbType.Guid, + [typeof(DateTime)] = DbType.DateTime, + [typeof(DateTimeOffset)] = DbType.DateTimeOffset, + [typeof(TimeSpan)] = DbType.Time, + [typeof(byte[])] = DbType.Binary, + [typeof(byte?)] = DbType.Byte, + [typeof(sbyte?)] = DbType.SByte, + [typeof(short?)] = DbType.Int16, + [typeof(ushort?)] = DbType.UInt16, + [typeof(int?)] = DbType.Int32, + [typeof(uint?)] = DbType.UInt32, + [typeof(long?)] = DbType.Int64, + [typeof(ulong?)] = DbType.UInt64, + [typeof(float?)] = DbType.Single, + [typeof(double?)] = DbType.Decimal, + [typeof(decimal?)] = DbType.Decimal, + [typeof(bool?)] = DbType.Boolean, + [typeof(char?)] = DbType.StringFixedLength, + [typeof(Guid?)] = DbType.Guid, + [typeof(DateTime?)] = DbType.DateTime, + [typeof(DateTimeOffset?)] = DbType.DateTimeOffset, + [typeof(TimeSpan?)] = DbType.Time, + [typeof(object)] = DbType.Object + }; + + /// + /// 不同数据库 参数化 时所使用的字符 + /// + public readonly static Dictionary ParameterCharMap = new Dictionary + { + { DBType.SqlServer,"@" }, + { DBType.MySql,"@" }, + { DBType.Oracle,":" }, + { DBType.PostgreSql,":" }, + { DBType.SQLite,"@" }, + { DBType.DB2,":" }, + }; + + + #region 各种数据库 + /// + /// SqlServer的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_SqlServer_DbType = new Dictionary() + { + { "bigint",DbType.Int64 }, + { "binary",DbType.Binary }, + { "bit",DbType.Boolean }, + { "char",DbType.AnsiStringFixedLength }, + { "date",DbType.Date }, + { "datetime",DbType.DateTime }, + { "datetime2",DbType.DateTime2 }, + { "datetimeoffset",DbType.DateTimeOffset }, + { "decimal",DbType.Decimal }, + { "float",DbType.Single }, + { "geography",DbType.Object }, + { "geometry",DbType.Object }, + { "hierarchyid",DbType.Object }, + { "image",DbType.Binary }, + { "int",DbType.Int32 }, + { "money",DbType.Currency }, + { "nchar",DbType.StringFixedLength }, + { "ntext",DbType.String }, + { "numeric",DbType.Decimal }, + { "nvarchar",DbType.StringFixedLength }, + { "real",DbType.Decimal }, + { "smalldatetime",DbType.DateTime }, + { "smallint",DbType.Int16 }, + { "smallmoney",DbType.Currency }, + { "sql_variant",DbType.Object }, + //{ "sysname",DbType.AnsiString }, + { "text",DbType.AnsiString }, + { "time",DbType.Time }, + { "timestamp",DbType.Binary }, + { "tinyint",DbType.Byte }, + { "uniqueidentifier",DbType.Guid }, + { "varbinary",DbType.Binary }, + { "varchar",DbType.AnsiStringFixedLength }, + { "xml",DbType.Xml } + }; + + /// + /// SqlServer的 数据类型对应CSharpType 字典 + /// + public readonly static Dictionary Dict_SqlServer_CSharpType = new Dictionary() + { + { "bigint","long" }, + { "binary","byte[]" }, + { "bit","bool" }, + { "char","string" }, + { "date","DateTime" }, + { "datetime","DateTime" }, + { "datetime2","DateTime" }, + { "datetimeoffset","DateTimeOffset" }, + { "decimal","decimal" }, + { "float","float" }, + { "geography","object" }, + { "geometry","object" }, + { "hierarchyid","object" }, + { "image","byte[]" }, + { "int","int" }, + { "money","decimal" }, + { "nchar","string" }, + { "ntext","string" }, + { "numeric","decimal" }, + { "nvarchar","string" }, + { "real","decimal" }, + { "smalldatetime","DateTime" }, + { "smallint","int"}, + { "smallmoney","decimal" }, + { "sql_variant","object" }, + //{ "sysname",DbType.AnsiString }, + { "text","string" }, + { "time","string" }, + { "timestamp","byte[]" }, + { "tinyint","int" }, + { "uniqueidentifier","Guid" }, + { "varbinary","byte[]" }, + { "varchar","string" }, + { "xml","string"} + }; + + /// + /// SqlServer的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_SqlServer_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "getdate","DateTime.Now" }, + { "newid","Guid.NewGuid()" } + }; + + + /// + /// Oracle的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_Oracle_DbType = new Dictionary() + { + { "BOOLEAN",DbType.Boolean }, + + { "CHAR",DbType.AnsiStringFixedLength }, + { "VARCHAR2",DbType.AnsiStringFixedLength }, + { "NCHAR",DbType.StringFixedLength }, + { "NVARCHAR2",DbType.StringFixedLength }, + + { "DATE",DbType.DateTime }, + { "TIMESTAMP",DbType.DateTime }, + + { "LONG",DbType.AnsiString },//用于存储可变长度字符串。 + { "RAW",DbType.Binary },//此数据类型用于存储二进制数据或字符串。字符变量是由Oracle在字符集之间自动转换的。 + { "LONG RAW",DbType.Binary },//此数据类型用户存储二进制数据或字符串。与RAW不同之处是它不在字符集之间进行转换。 + + { "BLOB",DbType.Binary },//将大型二进制对象存储在数据库中 + { "CLOB",DbType.AnsiString },//将大型字符数据存储在数据库中 + { "NCLOB",DbType.String },//存储大型UNICODE字符数据 + { "BFILE",DbType.Binary },//将大型二进制对象存储在操作系统文件中 + + { "ROWID",DbType.AnsiString }, + { "NROWID",DbType.String }, + + { "NUMBER",DbType.Decimal }, + { "DECIMAL",DbType.Decimal }, + { "INTEGER",DbType.Int32 }, + { "FLOAT",DbType.Decimal }, + { "REAL",DbType.Decimal } + }; + + /// + /// Oracle的 数据类型对应CSharpType 字典 + /// + public readonly static Dictionary Dict_Oracle_CSharpType = new Dictionary() + { + { "BOOLEAN","bool"}, + { "CHAR","string" }, + { "VARCHAR2","string" }, + { "NCHAR","string" }, + { "NVARCHAR2","string" }, + + { "DATE","DateTime" }, + { "TIMESTAMP","DateTime"}, + + { "LONG","string" },//用于存储可变长度字符串。 + { "RAW","byte[]" },//此数据类型用于存储二进制数据或字符串。字符变量是由Oracle在字符集之间自动转换的。 + { "LONG RAW","byte[]" },//此数据类型用户存储二进制数据或字符串。与RAW不同之处是它不在字符集之间进行转换。 + + { "BLOB","byte[]"},//将大型二进制对象存储在数据库中 + { "CLOB","string" },//将大型字符数据存储在数据库中 + { "NCLOB","string" },//存储大型UNICODE字符数据 + { "BFILE","byte[]" },//将大型二进制对象存储在操作系统文件中 + + { "ROWID","string" }, + { "NROWID","string" }, + + { "NUMBER","decimal"}, + { "DECIMAL","decimal" }, + { "INTEGER","int" }, + { "FLOAT","float" }, + { "REAL","decimal" } + }; + + /// + /// Oracle的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_Oracle_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + + }; + + /// + /// MySql的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_MySql_DbType = new Dictionary() + { + { "bit",DbType.Boolean }, + { "bool",DbType.Boolean }, + { "boolean",DbType.Boolean }, + + { "tinyint",DbType.SByte }, + { "smallint",DbType.Int16 }, + { "mediumint",DbType.Int32 }, + { "int",DbType.Int32 }, + { "integer",DbType.Int32 }, + { "bigint",DbType.Int64 }, + + { "enum",DbType.Int32 }, + { "set",DbType.Int32 }, + + { "binary",DbType.Binary }, + { "varbinary",DbType.Binary }, + { "tinyblob",DbType.Binary }, + { "mediumblob",DbType.Binary }, + { "blob",DbType.Binary }, + { "longblob",DbType.Binary }, + + { "date",DbType.DateTime }, + { "year",DbType.Int32 }, + { "time",DbType.Time }, + { "datetime",DbType.DateTime }, + { "timestamp",DbType.DateTime }, + + { "decimal",DbType.Decimal }, + { "dec",DbType.Decimal }, + { "double",DbType.Decimal }, + { "float",DbType.Single }, + { "real",DbType.Single }, + + { "char",DbType.AnsiStringFixedLength }, + { "varchar",DbType.AnsiStringFixedLength }, + { "nchar",DbType.StringFixedLength }, + { "nvarchar",DbType.StringFixedLength }, + { "tinytext",DbType.String }, + { "text",DbType.String }, + { "mediumtext",DbType.String }, + { "longtext",DbType.String } + + }; + + /// + /// MySql的 数据类型对应CSharpType 字典 + /// + public readonly static Dictionary Dict_MySql_CSharpType = new Dictionary() + { + { "bit","bool" }, + { "bool","bool" }, + { "boolean","bool" }, + + { "tinyint","int" }, + { "smallint","int" }, + { "mediumint","int" }, + { "int","int" }, + { "integer","int" }, + { "bigint","long" }, + + { "enum","int" }, + { "set","int"}, + + { "binary","byte[]" }, + { "varbinary","byte[]" }, + { "tinyblob","byte[]" }, + { "mediumblob","byte[]" }, + { "blob","byte[]" }, + { "longblob","byte[]" }, + + { "date","DateTime" }, + { "year","int" }, + { "time","string"}, + { "datetime","DateTime" }, + { "timestamp","DateTime"}, + + { "decimal","decimal" }, + { "dec","decimal" }, + { "double","double" }, + { "float","float"}, + { "real","decimal" }, + + { "char","string" }, + { "varchar","string" }, + { "nchar","string" }, + { "nvarchar","string" }, + { "tinytext","string" }, + { "text","string" }, + { "mediumtext","string" }, + { "longtext","string" } + + }; + + /// + /// MySql的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_MySql_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {"CURRENT_TIMESTAMP","DateTime.Now" }, + {"CURRENT_TIMESTAMP(6)","DateTime.Now" } + }; + + + /// + /// PostgreSQL的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_PostgreSql_DbType = new Dictionary() + { + { "smallint",DbType.Int16 }, + { "integer",DbType.Int32 }, + { "bigint",DbType.Int64 }, + { "decimal",DbType.Decimal }, + { "numeric",DbType.Decimal }, + { "real",DbType.Int32 }, + { "double precision",DbType.Decimal }, + { "smallserial",DbType.Int16 }, + { "serial",DbType.Int32 }, + { "bigserial",DbType.Int64 }, + + { "money",DbType.Decimal }, + + { "character varying",DbType.StringFixedLength }, + { "character",DbType.StringFixedLength }, + { "text",DbType.StringFixedLength }, + + { "bytea",DbType.Binary }, + + { "timestamp",DbType.DateTime }, + { "date",DbType.DateTime }, + { "time",DbType.Time }, + { "interval",DbType.Int32 }, + + { "boolean",DbType.Boolean }, + + { "enum",DbType.String }, + + { "point",DbType.Object}, + { "line",DbType.Object}, + { "lseg",DbType.Object}, + { "box",DbType.Object}, + { "path",DbType.Object}, + { "polygon",DbType.Object}, + { "circle",DbType.Object}, + + { "cidr",DbType.String},//IPv4和IPv6网络 + { "inet",DbType.String},//IPv4和IPv6主机以及网络 + { "macaddr",DbType.String},//MAC地址 + + { "bit",DbType.Boolean}, + { "bit varying",DbType.String}, + + { "tsvector",DbType.String}, + { "tsquery",DbType.String}, + + { "UUID",DbType.Guid}, + + { "Xml",DbType.Xml}, + + { "json",DbType.String}, + { "jsonb",DbType.String}, + + { "int4range",DbType.String}, + { "int8range",DbType.String}, + { "numrange",DbType.String}, + { "tsrange",DbType.String}, + { "tstzrange",DbType.String}, + { "daterange",DbType.String} + + }; + + /// + /// PostgreSQL的 数据类型对应CSharpype 字典 + /// + public readonly static Dictionary Dict_PostgreSql_CSharpType = new Dictionary() + { + { "smallint","int" }, + { "integer","int"}, + { "bigint","long" }, + { "decimal","decimal"}, + { "numeric","decimal" }, + { "real","decimal" }, + { "double precision","double" }, + { "smallserial","int" }, + { "serial","int" }, + { "bigserial","long" }, + + { "money","decimal" }, + + { "character varying","string" }, + { "character","string" }, + { "text","string" }, + + { "bytea","byte[]" }, + + { "timestamp","DateTime" }, + { "date","DateTime" }, + { "time","string" }, + { "interval","int"}, + + { "boolean","bool" }, + + { "enum","int" }, + + { "point","object"}, + { "line","object"}, + { "lseg","object"}, + { "box","object"}, + { "path","object"}, + { "polygon","object"}, + { "circle","object"}, + + { "cidr","string"},//IPv4和IPv6网络 + { "inet","string"},//IPv4和IPv6主机以及网络 + { "macaddr","string"},//MAC地址 + + { "bit","bool"}, + { "bit varying","string"}, + + { "tsvector","string"}, + { "tsquery","string"}, + + { "UUID","Guid"}, + + { "Xml","string"}, + + { "json","string"}, + { "jsonb","string"}, + + { "int4range","string"}, + { "int8range","string"}, + { "numrange","string"}, + { "tsrange","string"}, + { "tstzrange","string"}, + { "daterange","string"} + + }; + + /// + /// PostgreSql的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_PostgreSql_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + + }; + + /// + /// Sqlite的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_Sqlite_DbType = new Dictionary() + { + { "int",DbType.Int32}, + { "integer",DbType.Int32}, + { "tinyint",DbType.Byte}, + { "smallint",DbType.Int16}, + { "mediumint",DbType.Int32}, + { "bitint",DbType.Int64}, + { "unsigned big int",DbType.Int64}, + { "int2",DbType.Int32}, + { "int8",DbType.Int64}, + + { "character",DbType.AnsiStringFixedLength}, + { "varchar",DbType.AnsiStringFixedLength}, + { "varying character",DbType.AnsiStringFixedLength}, + { "nchar",DbType.StringFixedLength}, + { "native character",DbType.StringFixedLength}, + { "nvarchar",DbType.StringFixedLength}, + { "text",DbType.String}, + { "clob",DbType.String}, + + + { "blob",DbType.Binary}, + { "no datatype specified",DbType.Object}, + + { "real",DbType.Single}, + { "double",DbType.Decimal}, + { "double precision",DbType.Double}, + { "float",DbType.Single}, + + { "numeric",DbType.Decimal}, + { "decimal",DbType.Decimal}, + { "boolean",DbType.Boolean}, + { "date",DbType.Date}, + { "datetime",DbType.DateTime} + }; + + /// + /// Sqlite的 数据类型对应CSharpType 字典 + /// + public readonly static Dictionary Dict_Sqlite_CSharpType = new Dictionary() + { + { "int","int"}, + { "integer","int"}, + { "tinyint","int"}, + { "smallint","int"}, + { "mediumint","int"}, + { "bitint","long"}, + { "unsigned big int","long"}, + { "int2","int"}, + { "int8","long"}, + + { "character","string"}, + { "varchar","string"}, + { "varying character","string"}, + { "nchar","string"}, + { "native character","string"}, + { "nvarchar","string"}, + { "text","string"}, + { "clob","string"}, + + + { "blob","byte[]"}, + { "no datatype specified","object"}, + + { "real","decimal"}, + { "double","double"}, + { "double precision","double"}, + { "float","float"}, + + { "numeric","decimal"}, + { "decimal","decimal"}, + { "boolean","bool"}, + { "date","DateTime"}, + { "datetime","DateTime"} + }; + + + /// + /// Sqlite的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_Sqlite_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + + }; + + /// + /// DB2的 数据类型对应DbType字典 + /// + public readonly static Dictionary Dict_DB2_DbType = new Dictionary() + { + { "CHAR",DbType.AnsiStringFixedLength }, + { "CHARACTER",DbType.AnsiStringFixedLength }, + { "VARCHAR",DbType.AnsiStringFixedLength }, + { "LONG VARCHAR",DbType.AnsiStringFixedLength }, + + { "GRAPHIC",DbType.String }, + { "VARGRAPHIC",DbType.String }, + { "LONG GRAPHIC",DbType.String }, + + { "DATE",DbType.DateTime }, + { "TIMESTAMP",DbType.DateTime }, + + { "SMALLINT",DbType.Int32 }, + { "INTEGER",DbType.Int32 }, + { "BIGINT",DbType.Int64 }, + + { "BLOB",DbType.Binary },//保存2GB长度以内的二进制数据。 + { "CLOB",DbType.AnsiString },//保存2GB长度以内的单字节文本数据 + { "DBCLOB",DbType.String },//保存1GB长度以内的双字节文本数据。 + + { "NUMBER",DbType.Decimal }, + { "DOUBLE",DbType.Decimal }, + { "DECIMAL",DbType.Decimal }, + { "FLOAT",DbType.Decimal }, + { "REAL",DbType.Decimal } + }; + + /// + /// DB2的 数据类型对应CSharpType 字典 + /// + public readonly static Dictionary Dict_DB2_CSharpType = new Dictionary() + { + { "CHAR","string" }, + { "CHARACTER","string" }, + { "VARCHAR","string" }, + { "LONG VARCHAR","string" }, + + { "GRAPHIC","string" }, + { "VARGRAPHIC","string" }, + { "LONG GRAPHIC","string" }, + + { "DATE","DateTime" }, + { "TIMESTAMP","DateTime" }, + + { "SMALLINT","int" }, + { "INTEGER","int" }, + { "BIGINT","long" }, + + { "BLOB","byte[]" },//保存2GB长度以内的二进制数据。 + { "CLOB","string" },//保存2GB长度以内的单字节文本数据 + { "DBCLOB","string" },//保存1GB长度以内的双字节文本数据。 + + { "NUMBER","decimal" }, + { "DOUBLE","decimal" }, + { "DECIMAL","decimal" }, + { "FLOAT","decimal" }, + { "REAL","decimal" } + }; + + /// + /// DB2的 数据类型对应 默认值 字典 + /// + public readonly static Dictionary Dict_DB2_DefValue = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + + }; + + #endregion + + /// + /// DbType对应的转换方法 + /// + public readonly static Dictionary> Dict_Convert_Type = new Dictionary>() + { + { DbType.AnsiString, + (obj) => obj.ToString() + }, + { DbType.Binary, + (obj) => obj + }, + { DbType.Byte,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + byte res; + if (byte.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Boolean, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + bool res; + if (bool.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Currency, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + decimal res; + if (decimal.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Date, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + DateTime res; + if (DateTime.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.DateTime, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + DateTime res; + if (DateTime.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Decimal, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + decimal res; + if (decimal.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Double, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + decimal res; + if (decimal.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Guid, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + Guid res; + if (Guid.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Int16,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Int32,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Int64,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + long res; + if (long.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Object, + (obj)=>obj + }, + { DbType.SByte,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.Single,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.String, + (obj)=>obj.ToString() + }, + { DbType.Time, + (obj)=>obj.ToString() + }, + { DbType.UInt16,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.UInt32,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + int res; + if (int.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.UInt64,(obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + long res; + if (long.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + //{ DbType.VarNumeric, (obj) => + // { + // if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + // { + // return DBNull.Value; + // } + + // decimal res; + // if (decimal.TryParse(obj.ToString(), out res)) + // { + // return res; + // } + // return new ArgumentException(obj + "类型转换失败!"); + // } + //}, + { DbType.AnsiStringFixedLength, + (obj)=>obj.ToString() + }, + { DbType.StringFixedLength, + (obj)=>obj.ToString() + }, + { DbType.Xml, + (obj)=>obj.ToString() + }, + { DbType.DateTime2, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + DateTime res; + if (DateTime.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + { DbType.DateTimeOffset, (obj) => + { + if (obj == null || string.IsNullOrWhiteSpace(obj.ToString())) + { + return DBNull.Value; + } + + DateTime res; + if (DateTime.TryParse(obj.ToString(), out res)) + { + return res; + } + return new ArgumentException(obj + "类型转换失败!"); + } + }, + }; + } +} diff --git a/MJTop.Data/IgCaseDictionary.cs b/MJTop.Data/IgCaseDictionary.cs new file mode 100644 index 0000000..34a5a2d --- /dev/null +++ b/MJTop.Data/IgCaseDictionary.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 忽略Key值大小写的字典集合 + /// + /// Value类型 + public class IgCaseDictionary : Dictionary + { + /// + /// 大写/小写 + /// + public KeyCase Case { get; private set; } = KeyCase.Lower; + + private ReaderWriterLockSlim Locker { get; set; } + + /// + /// 构造函数,默认全部小写 + /// + public IgCaseDictionary() + { + this.Case = KeyCase.Lower; + this.Locker = new ReaderWriterLockSlim(); + } + + /// + /// 构造函数,指定存储 大写?小写 + /// + /// 大写/小写 + public IgCaseDictionary(KeyCase keyCase) + { + this.Case = keyCase; + this.Locker = new ReaderWriterLockSlim(); + } + + /// + /// 添加 + /// + /// + /// + public new void Add(string key, TValue value) + { + try + { + Locker.EnterWriteLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + base.Add((Case == KeyCase.Lower ? key.ToLower() : key.ToUpper()), value); + } + catch (Exception ex) + { + LogUtils.LogError("IgCaseDictionary", Developer.SysDefault, ex, "Key:" + key + "Value:" + value); + throw ex; + } + finally + { + Locker.ExitWriteLock(); + } + } + + + public new TValue this[string key] + { + get + { + try + { + Locker.EnterReadLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + return base[(Case == KeyCase.Lower ? key.ToLower() : key.ToUpper())]; + } + catch(Exception ex) + { + LogUtils.LogError("IgCaseDictionary", Developer.SysDefault, ex, "Key:" + key); + throw ex; + } + finally + { + Locker.ExitReadLock(); + } + } + set + { + try + { + Locker.EnterWriteLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + base[(Case == KeyCase.Lower ? key.ToLower() : key.ToUpper())] = value; + } + finally + { + Locker.ExitWriteLock(); + } + } + } + + public new bool ContainsKey(string key) + { + try + { + Locker.EnterReadLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + return base.ContainsKey((Case == KeyCase.Lower ? key.ToLower() : key.ToUpper())); + } + finally + { + Locker.ExitReadLock(); + } + } + + public new bool Remove(string key) + { + try + { + Locker.EnterWriteLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + return base.Remove((Case == KeyCase.Lower ? key.ToLower() : key.ToUpper())); + } + finally + { + Locker.ExitWriteLock(); + } + } + + public new bool TryGetValue(string key, out TValue value) + { + try + { + Locker.EnterReadLock(); + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("key不能为空![" + key + "]", "key"); + } + return base.TryGetValue((Case == KeyCase.Lower ? key.ToLower() : key.ToUpper()), out value); + } + finally + { + Locker.ExitReadLock(); + } + } + + public Dictionary Dictionary() + { + return this; + } + } + + /// + /// 键大小写枚举 + /// + public enum KeyCase + { + Lower, + Upper + } +} diff --git a/MJTop.Data/LogUtils.cs b/MJTop.Data/LogUtils.cs new file mode 100644 index 0000000..ccb628f --- /dev/null +++ b/MJTop.Data/LogUtils.cs @@ -0,0 +1,504 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Web; +using System.Text.RegularExpressions; +using System.Linq; +using System.Collections; +using System.Reflection; +using System.Collections.Specialized; + +namespace MJTop.Data +{ + /// + /// 日志操作类 + /// + internal class LogUtils + { + + private static object locker = new object(); + /// + /// 获取请求相关信息 + /// + /// 日志级别 + /// + private static List GetRequestData(LogLevel level) + { + List lstdata = new List(); + return lstdata; + } + + /// + /// 写入日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志级别 + /// 日志详情 + /// 记录时间 + public static void Write(string logName, Developer developer, LogLevel level, string detail, DateTime createtime) + { + Log log = new Log(); + log.LogName = logName; + log.Level = level; + log.Developer = developer; + log.CreateTime = createtime; + List lstDetails = GetRequestData(level); + lstDetails.Add(detail); + log.Detail = string.Join("\r\n\r\n", lstDetails.ToArray()); + + + //todo :可以将日志写入 文件、数据库、MongoDB + //这里写入根目录 log文件夹 + string logText = Log.GetModelData(log) + "\r\n----------------------------------------------------------------------------------------------------\r\n"; + string fileName = logName + DateTime.Now.ToString("yyyyMMdd") + ".log"; + string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log"); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + fileName = Path.Combine(dir, fileName); + File.AppendAllText(fileName, logText, Encoding.UTF8); + } + + /// + /// 写入Info 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogInfo(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + if (Info_objs != null && Info_objs.Length > 0) + { + List lstInfo = new List(); + foreach (var item in Info_objs) + { + lstInfo.Add(Log.GetModelData(item)); + } + lstDetails.Add("标记信息:" + string.Join(";", lstInfo.ToArray())); + } + Write(logName, developer, LogLevel.Info, string.Join("\r\n", lstDetails.ToArray()), DateTime.Now); + } + } + + + /// s + /// 写入带 堆栈执行 的Info 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogWrite(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true); + System.Diagnostics.StackFrame frame = stack.GetFrame(0); + string execFile = frame.GetFileName(); + string fullName = frame.GetMethod().DeclaringType.FullName; + string methodName = frame.GetMethod().Name; + int execLine = frame.GetFileLineNumber(); + lstDetails.Add("文件路径:" + execFile + "\r\n"); + lstDetails.Add("类全命名:" + fullName + "\r\n"); + lstDetails.Add("执行方法:" + methodName + "\r\n"); + lstDetails.Add("当前行号:" + execLine + "\r\n"); + + if (Info_objs != null && Info_objs.Length > 0) + { + List lstInfo = new List(); + foreach (var item in Info_objs) + { + lstInfo.Add(Log.GetModelData(item)); + } + lstDetails.Add("标记信息:" + string.Join(";", lstInfo.ToArray())); + } + Write(logName, developer, LogLevel.Info, string.Join("\r\n", lstDetails.ToArray()), DateTime.Now); + } + } + + /// + /// 写入Warn 日志 + /// + /// 日志名称 + /// 开发记录者 + /// 日志内容 + public static void LogWarn(string logName, Developer developer, params object[] Info_objs) + { + lock (locker) + { + List lstDetails = new List(); + System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true); + System.Diagnostics.StackFrame frame = stack.GetFrame(0); + string execFile = frame.GetFileName(); + string fullName = frame.GetMethod().DeclaringType.FullName; + string methodName = frame.GetMethod().Name; + int execLine = frame.GetFileLineNumber(); + lstDetails.Add("文件路径:" + execFile + "\r\n"); + lstDetails.Add("类全命名:" + fullName + "\r\n"); + lstDetails.Add("执行方法:" + methodName + "\r\n"); + lstDetails.Add("当前行号:" + execLine + "\r\n"); + + if (Info_objs != null && Info_objs.Length > 0) + { + List lstInfo = new List(); + foreach (var item in Info_objs) + { + lstInfo.Add(Log.GetModelData(item)); + } + lstDetails.Add("标记信息:" + string.Join(";", lstInfo.ToArray())); + } + Write(logName, developer, LogLevel.Warn, string.Join("\r\n", lstDetails.ToArray()), DateTime.Now); + } + } + + /// + /// 写入 Errorr日志 + /// + /// 日志名称 + /// 开发记录者 + /// 异常对象(可为null) + /// 日志内容 + public static void LogError(string logName, Developer developer, Exception ex, params object[] ext_InfoObjs) + { + lock (locker) + { + List lstDetails = new List(); + lstDetails.Add("异常信息1:" + Log.GetModelData(ex)); + if (ex.InnerException != null) + { + lstDetails.Add("异常信息2:" + Log.GetModelData(ex.InnerException)); + } + + StringBuilder sb_extInfo = new StringBuilder(); + if (ext_InfoObjs != null && ext_InfoObjs.Length > 0) + { + List lst_ext_Inf = new List(); + foreach (var item in ext_InfoObjs) + { + lst_ext_Inf.Add(Log.GetModelData(item)); + } + lstDetails.Add("标记信息:" + string.Join(";", lst_ext_Inf.ToArray())); + } + string detail = string.Join("\r\n\r\n", lstDetails.ToArray()); + Write(logName, developer, LogLevel.Error, detail, DateTime.Now); + } + } + } + + + /// + /// 程序日志 + /// + public class Log + { + public Guid Id { get { return Guid.NewGuid(); } } + + /// + /// 日志名称 + /// + public string LogName { get; set; } + + /// + /// 日志级别 + /// + public LogLevel Level { get; set; } + + /// + /// 当前记录日志者 + /// + public Developer Developer { get; set; } + + /// + /// 日志详细内容 + /// + public string Detail { get; set; } + + /// + /// 日志时间 + /// + public DateTime CreateTime { get; set; } + + + #region private 反射 对象 + /// + /// 得到对象的所有属性值 + /// + /// 对象 + /// + public static string GetModelData(object obj) + { + string valueParam = string.Empty; + StringBuilder sb = new StringBuilder(); + if (obj == null || string.IsNullOrEmpty(obj.ToString())) + { + return string.Empty; + } + Type objType = obj.GetType(); + + if (IsSimpleType(objType)) + { + valueParam = obj.ToString(); + } + else if (obj is NameValueCollection) + { + valueParam = GetCollectionData(obj as ICollection); + } + else + { + PropertyInfo[] proInfos = objType.GetProperties(); + foreach (PropertyInfo proInfo in proInfos) + { + string name = proInfo.Name; + object objvalue = null; + string value = string.Empty; + try + { + objvalue = proInfo.GetValue(obj, null); + } + catch + { } + if (objvalue == null) + { + value = string.Empty; + } + else + { + value = objvalue.ToString(); + } + sb.AppendLine(name + ":" + value + "\r\n"); + } + valueParam = sb.ToString().TrimEnd(); + } + return valueParam; + } + + /// + /// 得到集合 数组中所有值 + /// + /// 集合对象 + /// + public static string GetCollectionData(ICollection obj) + { + if (obj == null || string.IsNullOrEmpty(obj.ToString())) + { + return string.Empty; + } + string valueParam = string.Empty; + Type objType = obj.GetType(); + string typeName = objType.Name; + Type[] argumentsTypes = objType.GetGenericArguments(); + + #region isLstMark isDictMark + bool isLstMark = false; + if (argumentsTypes.Length == 1) + { + if (IsSimpleType(argumentsTypes[0])) + { + isLstMark = true; + } + } + else + { + isLstMark = (obj as IList) != null; + } + + + bool isDictMark = false; + if (argumentsTypes.Length == 2) + { + if (IsSimpleType(argumentsTypes[0]) && IsSimpleType(argumentsTypes[1])) + { + isDictMark = true; + } + } + else + { + isDictMark = ((obj as IDictionary) != null); + } + #endregion + + if (objType.IsArray) + { + #region 数组类型 + int arrRank = objType.GetArrayRank(); + if (arrRank == 1) + { + Array arr = (Array)obj; + if (arr != null && arr.LongLength > 0) + { + List lst = new List(); + foreach (var item in arr) + { + if (item != null) + { + lst.Add(item.ToString()); + } + } + valueParam = string.Join(",", lst.ToArray()); + } + } + #endregion + } + else if (isLstMark) + { + #region List + IEnumerable enumlst = obj as IEnumerable; + if (enumlst != null) + { + List lsts = new List(); + foreach (var item in enumlst) + { + if (item != null) + { + lsts.Add(item.ToString()); + } + } + if (lsts.Count > 0) + { + valueParam = string.Join(",", lsts.ToArray()); + } + } + #endregion + } + else if (isDictMark) + { + #region Dictionary + IDictionary dict = obj as IDictionary; + if (dict != null && dict.Count > 0) + { + StringBuilder sb = new StringBuilder(); + foreach (DictionaryEntry item in dict) + { + sb.AppendLine(item.Key + ":" + item.Value + "\r\n"); + } + valueParam = sb.ToString(); + } + #endregion + } + else if (obj is NameValueCollection) + { + #region NameValueCollection + NameValueCollection nvc = (NameValueCollection)obj; + if (nvc != null && nvc.Count > 0) + { + StringBuilder sb = new StringBuilder(); + foreach (string key in nvc.AllKeys) + { + sb.AppendLine(key + ":" + nvc[key] + "\r\n"); + } + valueParam = sb.ToString(); + } + #endregion + } + else if (obj is ICollection) + { + #region ICollection + ICollection coll = obj as ICollection; + if (coll != null) + { + List lstObjs = new List(); + foreach (var item in coll) + { + if (item != null) + { + lstObjs.Add(item.ToString()); + } + } + if (lstObjs.Count > 0) + { + valueParam = string.Join(",", lstObjs.ToArray()); + } + } + + #endregion + } + return valueParam.TrimEnd(); + } + + public static bool IsSimpleType(Type type) + { + //IsPrimitive 判断是否为基础类型。 + //基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 和 Single。 + Type t = Nullable.GetUnderlyingType(type) ?? type; + if (t.IsPrimitive || t.IsEnum || t == typeof(string)) return true; + return false; + } + + #endregion + + + #region 枚举 处理 + /// + /// 根据枚举对象得到 枚举键值对 + /// + /// 枚举 + /// + public static Dictionary GetAllEnums() + { + Dictionary dict = null; + Type type = typeof(T); + string[] enums = Enum.GetNames(type); + if (enums != null && enums.Length > 0) + { + dict = new Dictionary(); + foreach (string item in enums) + { + string str = Enum.Parse(typeof(T), item).ToString(); + T deve = (T)Enum.Parse(typeof(T), item); + string uid = Convert.ToInt32(deve).ToString(); + dict.Add(str, uid); + } + } + return dict; + } + + + /// + /// 根据枚举val获取枚举name + /// + /// 枚举类型 + /// 枚举val + /// 枚举name + public static T GetEnumName(int enumVal) + { + T t = (T)Enum.Parse(typeof(T), enumVal.ToString()); + return t; + } + #endregion + } + + /// + /// 日志级别 + /// + public enum LogLevel + { + Info = 0, + Warn = 1, + Error = 2 + } + + /// + /// 日志记录开发者 + /// + public enum Developer + { + /// + /// 系统默认 + /// + SysDefault = 0, + + /// + /// 其他用户 + /// + MJ = 115 + } + + + +} \ No newline at end of file diff --git a/MJTop.Data/MJTop.Data.csproj b/MJTop.Data/MJTop.Data.csproj new file mode 100644 index 0000000..78cb2c2 --- /dev/null +++ b/MJTop.Data/MJTop.Data.csproj @@ -0,0 +1,176 @@ + + + + + Debug + AnyCPU + {3D36CDC9-E989-465B-A9F1-AD85DC42F242} + Library + Properties + MJTop.Data + MJTop.Data + v4.5.2 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + false + AnyCPU + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + False + lib\DDTek.Oracle.dll + + + ..\packages\IBM.Data.DB.Provider.11.5.4000.4861\lib\net451\x64\IBM.Data.DB2.dll + + + ..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\packages\Npgsql.4.0.7\lib\net451\Npgsql.dll + + + ..\packages\Oracle.ManagedDataAccess.19.3.1\lib\net40\Oracle.ManagedDataAccess.dll + + + + ..\packages\System.Buffers.4.5.0\lib\netstandard1.1\System.Buffers.dll + + + + + + + + False + lib\System.Data.SQLite.dll + + + + + + ..\packages\System.Memory.4.5.3\lib\netstandard1.1\System.Memory.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + + + ..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + \ No newline at end of file diff --git a/MJTop.Data/Properties/AssemblyInfo.cs b/MJTop.Data/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5a7848a --- /dev/null +++ b/MJTop.Data/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("MJTop.Data")] +[assembly: AssemblyDescription("http://51try.top")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("51try.top")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +//将 ComVisible 设置为 false 将使此程序集中的类型 +//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("3d36cdc9-e989-465b-a9f1-ad85dc42f242")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MJTop.Data/SPI/IDB.cs b/MJTop.Data/SPI/IDB.cs new file mode 100644 index 0000000..cca6952 --- /dev/null +++ b/MJTop.Data/SPI/IDB.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.Linq; +using System.Text; + + +namespace MJTop.Data.SPI +{ + public interface IDB + { + //void BeginTran(); + //void BeginTran(IsolationLevel isolationLevel); + //void Commit(); + //void Rollback(); + + + + #region AOP拦截 + + Action OnExecuting { get; set; } + + Action OnExecuted { get; set; } + + Action OnError { get; set; } + + #endregion + + TableTrigger DataChangeTriggers { get; } + + NameValueCollection GetInsertExcludeColumns(); + + ExcludeColumn InsertExcludeColumns { get; } + + NameValueCollection GetUpdateExcludeColumns(); + + ExcludeColumn UpdateExcludeColumns { get; } + + #region Bool查询 + + bool TryConnect(); + + bool ValidateSql(string strSql, out Exception ex); + + void CheckTabStuct(string tableName, params string[] columnNames); + + #endregion + + + int RunStoreProc(string storeProcName, object parms = null); + + DataTable RunStoreProcGetDT(string storeProcName, object parms = null); + + DataSet RunStoreProcGetDS(string storeProcName, object parms = null); + + + #region 基础查询 + + TRet Scalar(string strSql, TRet defRet, object parms = null); + + NameValueCollection GetFirstRow(string strSql, object parms = null); + + DataTable GetDataTable(string strSql, object parms = null); + + List GetListTable(string strSql, object parms = null); + + DataSet GetDataSet(string strSql, object parms = null); + + DbDataReader ExecReader(string commandText, object parms = null, CommandType commandType = CommandType.Text); + + TRet Single(string strSql, TRet defRet, object parms = null); + + List> GetListDictionary(string strSql, object parms = null); + + DataTable ReadTable(string strSql, object parms = null); + + List ReadList(string strSql, object parms = null); + + NameValueCollection ReadNameValues(string strSql, object parms = null); + + Dictionary ReadDictionary(string strSql, object parms = null, IEqualityComparer comparer = null); + + #endregion + + #region 执行 + + int ExecSql(string strSql, object parms = null); + int ExecSqlTran(string strSql, object parms = null); + int ExecSqlTran(params string[] sqlCmds); + int ExecSqlTran(List>> strSqlList); + + + #endregion + + int BulkInsert(string tableName, DataTable data, int batchSize = 200000, int timeout = 60); + + int BulkInsert(string tableName, DbDataReader reader, int batchSize = 200000, int timeout = 60); + + + #region 根据表名,列名,列相关操作 + + bool Exist(string tableName, string columnName, object columnValue, params object[] excludeValues); + + bool Insert
(DT data, string tableName, params string[] excludeColNames); + + int InsertGetInt
(DT data, string tableName, params string[] excludeColNames); + + long InsertGetLong
(DT data, string tableName, params string[] excludeColNames); + + bool Update
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + KeyValuePair Save
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + bool UpSingle(string tableName, string columnName, object columnValue, object pkOrUniqueValue, string pkOrUniqueColName = "Id"); + + bool DeleteAll(string tableName); + + int Delete(string tableName, object pkOrUniqueColName); + + int Delete(string tableName, string columnName, params object[] columnValues); + + #endregion + + T Get(string tableName, object parms) where T : class, new(); + + T GetById(string tableName, object pkValue) where T : class, new(); + + List GetByIds(string tableName, object[] pkValues) where T : class, new(); + + List GetList(string tableName, object parms) where T : class, new(); + + List GetList(string strSql) where T : class, new(); + + DataTable SelectAll(string tableName, string orderbyStr = null); + + DataTable SelectTop(string tableName, int top, string orderbyStr = null); + + long SelectCount(string tableName, string whereAndStr = null); + + DataTable SelectTable(string joinTableName, string whereStr, string orderbyStr); + + KeyValuePair GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr); + } +} diff --git a/MJTop.Data/SPI/IDBEntity.cs b/MJTop.Data/SPI/IDBEntity.cs new file mode 100644 index 0000000..da4b82e --- /dev/null +++ b/MJTop.Data/SPI/IDBEntity.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; + +namespace Top._51Try.Data.SPI +{ + public interface IDBEntity + { + //用一个算法,解析出一种插入的解决方案,然后进行缓存,下次插入时直接调用 + /** + * 1. 先用 表达式树的 方式 能够将字段 高效的处理好 + * 2. + * + */ + + bool Insert
(DT data, string tableName, params string[] excludeColNames); + + bool Update
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + bool Upsert
(DT data, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + bool UpSingle(string tableName, string columnName, object columnValue, object pkOrUniqueValue, string pkOrUniqueColName = "Id"); + + int Delete

(string tableName, string columnName, params P[] columnValues); + + DataTable GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr, out int totalCount); + + T GetById(P IdValue, string pkOrUniqueColName = "Id"); + + List GetByIds(string pkOrUniqueColName = "Id", params P[] ids); + + + //获取某个表的 某条数据的 某列的值 + TReturn QuerySingleById(string tableName, string retColumnName, object Idvalue, string pkOrUniqueColName = "Id"); + + + //新增、编辑的时候,判断唯一键值 使用 + bool ExistByColVal

(string tableName, string columnName, object columnValue, params P[] excludeValues); + + //List GetList(string whereStr, string orderByStr = null); + + //List GetAll(string orderByStr = null); + + } +} diff --git a/MJTop.Data/SPI/IDBExt.cs b/MJTop.Data/SPI/IDBExt.cs new file mode 100644 index 0000000..0dc2d2d --- /dev/null +++ b/MJTop.Data/SPI/IDBExt.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; + +namespace SharpDB.SPI +{ + public interface IDBExt + { + + bool Insert(object obj, string tableName, params string[] excludeColNames); + + bool Update(object obj, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + bool Upsert(object obj, string tableName, string pkOrUniqueColName = "Id", params string[] excludeColNames); + + bool UpSingle(string tableName, string columnName, object columnValue, object pkOrUniqueValue, string pkOrUniqueColName = "Id"); + + int Delete(string tableName, string columnName, params object[] columnValues); + + DataTable GetDataTableByPager(int currentPage, int pageSize, string selColumns, string joinTableName, string whereStr, string orderbyStr, out int totalCount); + + T GetEntity(object IdValue, string pkOrUniqueColName = "Id"); + + //获取某个表的 某条数据的 某列的值 + TRet QuerySingle(string retColumnName, object Idvalue, string pkOrUniqueColName = "Id"); + + //新增、编辑的时候,判断唯一键值 使用 + bool ExistByColVal(string tableName, string columnName, object columnValue, params object[] excludeValues); + + List GetList(string whereStr = null, string orderByStr = null); + + } +} diff --git a/MJTop.Data/SPI/IDBInfo.cs b/MJTop.Data/SPI/IDBInfo.cs new file mode 100644 index 0000000..1ce6dd4 --- /dev/null +++ b/MJTop.Data/SPI/IDBInfo.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data.SPI +{ + public interface IDBInfo + { + Tool Tools { get; } + + string DBName { get; } + + string Version { get; } + + double VersionNumber { get; } + + ///

+ /// 注意:刷新数据失败的情况,根据返回值做对应处理。 + /// + /// + bool Refresh(); + + List TableNames { get; } + + NameValueCollection TableComments { get; } + + IgCaseDictionary TableInfoDict { get; } + + IgCaseDictionary> TableColumnNameDict { get; } + + IgCaseDictionary> TableColumnInfoDict { get; } + + IgCaseDictionary TableColumnComments { get; } + + NameValueCollection Views { get; } + + NameValueCollection Procs { get; } + + [Obsolete("注意:Oralce暂不支持查询所有数据库名称。", false)] + List DBNames { get; } + + List this[string tableName] { get; } + + ColumnInfo this[string tableName, string columnName] { get; } + + bool IsExistTable(string tableName); + + bool IsExistColumn(string tableName, string columnName); + + string GetColumnComment(string tableName, string columnName); + + string GetTableComment(string tableName); + + List GetColumns(string tableName); + + bool SetTableComment(string tableName, string comment); + + bool SetColumnComment(string tableName, string columnName, string comment); + + bool DropTable(string tableName); + + bool DropColumn(string tableName, string columnName); + + [Obsolete("注意:MySql的所有表存储引擎必须为MyISAM 方才查询支持,方才查询准确。", false)] + Dictionary GetTableStruct_Modify(); + + } +} diff --git a/MJTop.Data/Script.cs b/MJTop.Data/Script.cs new file mode 100644 index 0000000..2d37c55 --- /dev/null +++ b/MJTop.Data/Script.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 拼接Sql脚本类 + /// + public class Script + { + #region SqlIn/SqlLike + + /// + /// 拼接in sql语句 + /// + /// 元素类型 + /// 列名 + /// 元素值 + /// not in 或 in + /// and开头的 in sql语句 + public static string SqlIn(string columnName, T[] values, bool isNotIn = false) + { + string result = string.Empty; + if (values == null || values.Length <= 0) + { + return string.Empty; + } + bool isValueType = TypeInfo.IsValueType; + List lst = new List(); + foreach (T obj in values) + { + if (obj != null) + { + string val = obj.ToString(); + if (val.StartsWith("'") && val.EndsWith("'")) + { + val = val.Replace("'", "'''"); + lst.Add(val); + continue; + } + if (!isValueType) + { + val = "'" + val + "'"; + } + lst.Add(val); + } + } + if (lst.Count > 0) + { + result = " and " + columnName + " " + (isNotIn ? "not" : "") + " in (" + string.Join(",", lst) + ") "; + } + return result; + } + + + /// + /// 拼接in sql语句 + /// + /// 元素类型 + /// 列名 + /// 元素值 + /// not in 或 in + /// and开头的 in sql语句 + public static string SqlInByDBType(ColumnInfo columnInfo, object[] values, DBType dBType = DBType.SqlServer, bool isNotIn = false) + { + string result = string.Empty; + if (values == null || values.Length <= 0) + { + return string.Empty; + } + List lst = new List(); + foreach (object obj in values) + { + if (obj != null) + { + string val = obj.ToString(); + if (val.StartsWith("'") && val.EndsWith("'")) + { + val = val.Replace("'", "'''"); + lst.Add(val); + continue; + } + if (columnInfo.LikeType != LikeType.Number) + { + val = "'" + val + "'"; + } + lst.Add(val); + } + } + if (lst.Count > 0) + { + result = " and " + columnInfo.ColumnName + " " + (isNotIn ? "not" : "") + " in (" + string.Join(",", lst) + ") "; + } + return result; + } + + + /// + /// 拼接多条 like 语句 + /// + /// 实体类型 + /// 列名 + /// 元素值 + /// or like 或 and like + /// and开头的 多条 like 语句 + public static string SqlLike(string columnName, T[] values, bool isOrLike = true) + { + string result = string.Empty; + if (values == null || values.Length <= 0) + { + return string.Empty; + } + List lst = new List(); + foreach (T obj in values) + { + if (obj != null) + { + string like_sql = columnName + " like '%{0}%' "; + string temp_sql = string.Empty; + string val = obj.ToString(); + if (val.StartsWith("'") && val.EndsWith("'")) + { + val = val.Replace("'", "''"); + temp_sql = string.Format(like_sql, val); + lst.Add(temp_sql); + continue; + } + temp_sql = string.Format(like_sql, val); + lst.Add(temp_sql); + } + } + if (lst.Count > 0) + { + result = " and (" + (string.Join((isOrLike ? " or" : " and "), lst)) + ") "; + } + return result; + } + + #endregion + + + /// + /// 插入后查询自增列的值 + /// + /// 数据库类型 + /// 表名 + /// 序列名称 + /// 自增列的列名 + /// 查询自增Id的脚本 + public static string IdentitySql(DBType type, string tableName = null, string sequenceName = null, string identityColName = null) + { + switch (type) + { + case DBType.SqlServer: + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentException(type + ",查询自增Id的值,不能为空!", "tableName"); + } + return string.Format("select Ident_Current('{0}')", tableName); + + case DBType.MySql: + return string.Format("select @@Identity"); + + case DBType.Oracle: + case DBType.OracleDDTek: + + if (string.IsNullOrWhiteSpace(sequenceName)) + { + throw new ArgumentException(type + ",查询自增Id的值,不能为空!", "sequenceName"); + } + return string.Format("select {0}.currval from dual", sequenceName); + + case DBType.PostgreSql: + + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentException(type + ",查询自增Id的值,不能为空!", "tableName"); + } + + if (string.IsNullOrWhiteSpace(identityColName)) + { + throw new ArgumentException(type + ",查询自增Id的值,不能为空!", "identityColName"); + } + //只支持表定义时定义的:smallserial,serial,bigserial + //不支持创建 序列+触发器 时的实现。 + return string.Format("select currval(pg_get_serial_sequence('{0}', '{1}'));", tableName.ToLower(), identityColName.ToLower()); + + case DBType.SQLite: + return string.Format("select last_insert_rowid()"); + default: + throw new ArgumentException("不支持的数据库类型!"); + } + } + } +} diff --git a/MJTop.Data/Setting.cs b/MJTop.Data/Setting.cs new file mode 100644 index 0000000..faf6ea0 --- /dev/null +++ b/MJTop.Data/Setting.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// DB配置类,暂时不用 + /// + public class Setting + { + + } +} diff --git a/MJTop.Data/StringPlus.cs b/MJTop.Data/StringPlus.cs new file mode 100644 index 0000000..aed6727 --- /dev/null +++ b/MJTop.Data/StringPlus.cs @@ -0,0 +1,119 @@ +using System.Text; + +namespace MJTop.Data +{ + internal class StringPlus + { + private StringBuilder str = new StringBuilder(); + + public string Append(string Text) + { + this.str.Append(Text); + return this.str.ToString(); + } + /// + /// 添加空行 + /// + /// + public string AppendLine() + { + this.str.Append("\r\n"); + return this.str.ToString(); + } + /// + /// 添加一行字符串 + /// + /// + /// + public string AppendLine(string Text) + { + this.str.Append(Text + "\r\n"); + return this.str.ToString(); + } + /// + /// 添加若干个空格符后的文本内容 + /// + /// + /// + /// + public string AppendSpace(int SpaceNum, string Text) + { + this.str.Append(this.Space(SpaceNum)); + this.str.Append(Text); + return this.str.ToString(); + } + /// + /// 添加若干个空格符后的文本,并换行 + /// + /// 空格数 + /// + /// + public string AppendSpaceLine(int SpaceNum, string Text) + { + this.str.Append(this.Space(SpaceNum)); + this.str.Append(Text); + this.str.Append("\r\n"); + return this.str.ToString(); + } + /// + /// 删除末尾指定字符串 + /// + /// + public void DelLastChar(string strchar) + { + string str = this.str.ToString(); + int length = str.LastIndexOf(strchar); + if (length > 0) + { + this.str = new StringBuilder(); + this.str.Append(str.Substring(0, length)); + } + } + /// + /// 删除最后一个逗号 + /// + public void DelLastComma() + { + string str = this.str.ToString(); + int length = str.LastIndexOf(","); + if (length > 0) + { + this.str = new StringBuilder(); + this.str.Append(str.Substring(0, length)); + } + } + + public void Remove(int Start, int Num) + { + this.str.Remove(Start, Num); + } + /// + /// 空格串 + /// + /// + /// + public string Space(int SpaceNum) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < SpaceNum; i++) + { + builder.Append("\t"); //制表符 + } + return builder.ToString(); + } + + public override string ToString() + { + return this.str.ToString(); + } + + public string Value + { + get + { + return this.str.ToString(); + } + } + } +} + diff --git a/MJTop.Data/TS.cs b/MJTop.Data/TS.cs new file mode 100644 index 0000000..175f33a --- /dev/null +++ b/MJTop.Data/TS.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + public class TS + { + private DB Db { get; set; } + public TS(DB db) + { + this.Db = db; + this.IsTran = false; + } + internal DbConnection TranConn { get; set; } + internal DbTransaction Tran { get; set; } + private bool IsTran { get; set; } + + public virtual void Begin() + { + this.TranConn = Db.CreateConn(); + this.TranConn.Open(); + this.Tran = this.TranConn.BeginTransaction(); + this.IsTran = true; + } + + public virtual void Begin(IsolationLevel isolationLevel) + { + this.TranConn = Db.CreateConn(); + this.TranConn.Open(); + this.Tran = this.TranConn.BeginTransaction(isolationLevel); + this.IsTran = true; + } + + public virtual void Commit() + { + this.Tran.Commit(); + this.IsTran = false; + } + + public virtual void Rollback() + { + this.Tran.Rollback(); + this.IsTran = false; + } + } +} diff --git a/MJTop.Data/TableTrigger.cs b/MJTop.Data/TableTrigger.cs new file mode 100644 index 0000000..0872492 --- /dev/null +++ b/MJTop.Data/TableTrigger.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + /// + /// 表触发器 + /// + public class TableTrigger + { + /// + /// 表对应执行的Action集合 + /// + internal IgCaseDictionary> ExecActions { get; set; } + + private DB Db { get; set; } + + internal TableTrigger(DB db) + { + this.Db = db; + this.ExecActions = new IgCaseDictionary>(); + } + + /// + /// 添加 表对应要执行的Action + /// + /// 表名 + /// 多个Action,object:方法执行后传入的值 + public void Add(string tableName, params Action[] actions) + { + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentException("表名不能为空!"); + } + this.Db.CheckTabStuct(tableName); + this.ExecActions.AddRange(tableName, actions); + } + + + /// + /// 删除 表对应执行的Action集合数据 + /// + /// 表名 + /// 是否删除成功 + public bool Remove(string tableName) + { + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentException("表名不能为空!"); + } + Db.CheckTabStuct(tableName); + return this.ExecActions.Remove(tableName); + } + + /// + /// 清除 所有表的Action集合 + /// + public void Clear() + { + this.ExecActions.Clear(); + } + + /// + /// 获取当前表的Action集合 + /// + /// + /// 当前表的Action集合 + public List GetActions(string tableName) + { + if (string.IsNullOrWhiteSpace(tableName)) + { + throw new ArgumentException("表名不能为空!"); + } + List lstAct = null; + if (this.ExecActions.TryGetValue(tableName, out lstAct)) + { + return lstAct; + } + else + { + return new List(); + } + } + + } +} diff --git a/MJTop.Data/Tool.cs b/MJTop.Data/Tool.cs new file mode 100644 index 0000000..f979a1e --- /dev/null +++ b/MJTop.Data/Tool.cs @@ -0,0 +1,275 @@ +using MJTop.Data.SPI; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + public class Tool + { + private DB Db = null; + private DBType DbType = DBType.SqlServer; + private IDBInfo Info = null; + private NameValueCollection TableComments = null; + private NameValueCollection ExcludeTableCols = null; + /// + /// 处理 数据库数据类型 对应的 C#代码的数据类型 (不同数据库 对 默认值的写法不同) + /// + private Dictionary Dict_CSharpType = new Dictionary(); + /// + /// 处理 默认值 对应的 C#代码 值生成方式(不同数据库 对 默认值的写法不同) + /// + private Dictionary Dict_Data_DefValue = new Dictionary(); + internal Tool(DB db, IDBInfo info) + { + this.Db = db; + this.Info = info; + this.TableComments = info.TableComments; + this.DbType = db.DBType; + this.ExcludeTableCols = db.GetInsertExcludeColumns(); + switch (DbType) + { + case DBType.SqlServer: + this.Dict_CSharpType = Global.Dict_SqlServer_CSharpType; + this.Dict_Data_DefValue = Global.Dict_SqlServer_DefValue; + break; + case DBType.MySql: + this.Dict_CSharpType = Global.Dict_MySql_CSharpType; + this.Dict_Data_DefValue = Global.Dict_MySql_DefValue; + break; + case DBType.Oracle: + case DBType.OracleDDTek: + this.Dict_CSharpType = Global.Dict_Oracle_CSharpType; + this.Dict_Data_DefValue = Global.Dict_Oracle_DefValue; + break; + case DBType.PostgreSql: + this.Dict_CSharpType = Global.Dict_PostgreSql_CSharpType; + this.Dict_Data_DefValue = Global.Dict_PostgreSql_DefValue; + break; + case DBType.SQLite: + this.Dict_CSharpType = Global.Dict_Sqlite_CSharpType; + this.Dict_Data_DefValue = Global.Dict_Sqlite_DefValue; + break; + case DBType.DB2: + this.Dict_CSharpType = Global.Dict_DB2_CSharpType; + this.Dict_Data_DefValue = Global.Dict_DB2_DefValue; + break; + default: + throw new Exception("未指定Charp类型字典!"); + } + } + + /// + /// 生成实体类 + /// 注:当设置 InsertExcludeColumns 时,排除列 不会在成 实体中 的属性 + /// + /// 存放生成实体类的文件夹 + public void GenerateEntityCode(string codeDir) + { + GenerateEntityCode(codeDir, null); + } + + /// + /// 生成实体类 + /// 注:当设置 InsertExcludeColumns 时,排除列 不会在成 实体中 的属性 + /// + /// 存放生成实体类的文件夹 + /// 命名空间 + public void GenerateEntityCode(string codeDir, string strNamespace) + { + GenerateEntityCode(codeDir, strNamespace, false); + } + + /// + /// 生成实体类 + /// 注:当设置 InsertExcludeColumns 时,排除列 不会在成 实体中 的属性 + /// + /// 存放生成实体类的文件夹 + /// 命名空间 + /// 是否根据表名前缀,自动附加生成命名空间 + public void GenerateEntityCode(string codeDir, string strNamespace, bool AutoNameSpaceByTableNamePrefix) + { + if (string.IsNullOrWhiteSpace(codeDir)) + { + throw new ArgumentNullException("CodeDir"); + } + + if (!Directory.Exists(codeDir)) + { + Directory.CreateDirectory(codeDir); + } + + foreach (var item in Info.TableColumnInfoDict) + { + int index = Info.TableNames.FindIndex(t => t.Equals(item.Key, StringComparison.OrdinalIgnoreCase)); + string rowTableName = Info.TableNames[index]; + string filePath = Path.Combine(codeDir, rowTableName + ".cs"); + + string newCodeDir = codeDir, newNameSpace = strNamespace; + if (AutoNameSpaceByTableNamePrefix && rowTableName.Contains("_")) + { + string prefix = rowTableName.Split('_')[0].ToUpper(); + + newCodeDir = Path.Combine(codeDir, prefix); + + if (!Directory.Exists(newCodeDir)) + { + Directory.CreateDirectory(newCodeDir); + } + filePath = Path.Combine(newCodeDir, rowTableName + ".cs"); + + newNameSpace = strNamespace + "." + prefix; + } + Generate(newNameSpace, rowTableName, item.Value, filePath); + } + } + + + /// + /// 单表生成实体类 + /// + /// + /// + /// + /// + private void Generate(string strNamespace, string tableName, List columns, string filePath) + { + StringPlus plus = new StringPlus(); + plus.AppendLine("using System;"); + plus.AppendLine("using System.Collections.Generic;"); + plus.AppendLine("using System.Linq;"); + plus.AppendLine("using System.Text;"); + plus.AppendLine(); + + if (!string.IsNullOrWhiteSpace(strNamespace)) + { + plus.AppendLine("namespace " + strNamespace); + plus.AppendLine("{"); + } + + if (!string.IsNullOrWhiteSpace(TableComments[tableName])) + { + plus.AppendSpaceLine(1, "/// "); + + string comment = TableComments[tableName]; + string[] strs = comment.Split(new string[] { "\r\n" }, StringSplitOptions.None); + foreach (var str in strs) + { + plus.AppendSpaceLine(1, "/// " + str.Trim()); + } + + plus.AppendSpaceLine(1, "/// "); + } + + plus.AppendSpaceLine(1, "[Serializable]"); + plus.AppendSpaceLine(1, "public partial class " + tableName); + plus.AppendSpaceLine(1, "{"); + + plus.AppendSpaceLine(2, "public " + tableName + "()"); + plus.AppendSpaceLine(2, "{"); + foreach (var colInfo in columns) + { + AppendDefaultVal(plus, 3, colInfo); + } + plus.AppendSpaceLine(2, "}"); + + plus.AppendLine(); + + foreach (var colInfo in columns) + { + if (ExcludeTableCols[tableName] != null + && ExcludeTableCols.ContainsValue(colInfo.ColumnName)) + { + continue; + } + AppendColumn(plus, 2, colInfo); + } + + plus.AppendSpaceLine(1, "}"); + + if (!string.IsNullOrWhiteSpace(strNamespace)) + { + plus.AppendLine("}"); + } + + File.WriteAllText(filePath, plus.Value, Encoding.UTF8); + } + + + /// + /// 处理单个 属性 列 + /// + /// + /// + /// + private void AppendColumn(StringPlus plus, int SpaceNum, ColumnInfo colInfo) + { + if (!string.IsNullOrWhiteSpace(colInfo.DeText)) + { + plus.AppendSpaceLine(SpaceNum, "/// "); + + string[] strs = colInfo.DeText.Split(new string[] { "\r\n" }, StringSplitOptions.None); + foreach (var str in strs) + { + plus.AppendSpaceLine(SpaceNum, "/// " + str.Trim()); + } + + plus.AppendSpaceLine(SpaceNum, "/// "); + } + + Dict_CSharpType.TryGetValue(colInfo.TypeName, out string strType); + strType = strType ?? "object"; + + //可以为空,并且默认值也为空 + if (strType != "string" && strType != "object" && colInfo.CanNull + && string.IsNullOrWhiteSpace(colInfo.DefaultVal)) + { + strType += "?"; + } + plus.AppendSpaceLine(SpaceNum, "public " + strType + " " + colInfo.ColumnName + " { get;set; }"); + plus.AppendLine(); + } + + /// + /// 默认值处理 + /// + /// + /// + /// + private void AppendDefaultVal(StringPlus plus, int SpaceNum, ColumnInfo colInfo) + { + if (!string.IsNullOrWhiteSpace(colInfo.DefaultVal)) + { + Dict_CSharpType.TryGetValue(colInfo.TypeName, out string strType); + strType = strType ?? "object"; + + //SqlServer中列的 默认值去除括号 + string defValue = colInfo.DefaultVal.Trim('(', ')'); + + string newValue; + Dict_Data_DefValue.TryGetValue(defValue, out newValue); + newValue = newValue ?? defValue; + + //string类型的默认值 加上 双引号 + if (strType == "string") + { + newValue = "\"" + newValue + "\""; + } + else if(strType == "decimal") + { + newValue = newValue + "m"; + } + else if (strType == "float") + { + newValue = newValue + "f"; + } + + plus.AppendSpaceLine(SpaceNum, "this." + colInfo.ColumnName + " = " + newValue + ";"); + } + } + } +} diff --git a/MJTop.Data/TypeInfo.cs b/MJTop.Data/TypeInfo.cs new file mode 100644 index 0000000..7ec0779 --- /dev/null +++ b/MJTop.Data/TypeInfo.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace MJTop.Data +{ + + internal class TypeInfo + { + static TypeInfo() + { + TyThis = typeof(T); + + Name = TyThis.Name; + + IsValueType = TyThis.IsValueType; + + IsAnonymousType = Name.Contains("AnonymousType"); + + if (Name == "DbParameter[]") + { + IsArrayParameter = true; + } + else if (Name == "List`1") + { + Type typeTmp = TyThis.GetGenericArguments()[0]; + if (typeTmp.Name == "DbParameter") + { + IsListParameter = true; + } + } + else if ((typeof(IDictionary)).IsAssignableFrom(TyThis)) // Dictionary 或 Hashtable + //不能是: + //|| (typeof(IDictionary)).IsAssignableFrom(TyThis) + //|| (typeof(IDictionary)).IsAssignableFrom(TyThis) + //|| (typeof(IDictionary)).IsAssignableFrom(TyThis)) + { + IsDict = true; + } + else if ((typeof(NameValueCollection)).IsAssignableFrom(TyThis)) + { + IsNameValueColl = true; + } + else //可能是 匿名对象,也可能时实体对象 + { + Props = TyThis.GetProperties(); + PropNames = new List(); + PropMapping = new Dictionary(); + foreach (var prop in Props) + { + PropMapping.Add(prop.Name, prop); + PropNames.Add(prop.Name); + } + } + + TableAttribute table = TyThis.GetCustomAttribute(); + TableName = (table?.Name) ?? Name; + } + + public static Type TyThis { get; set; } + + public static bool IsValueType { get; set; } + + public static PropertyInfo[] Props { get; set; } + + public static Dictionary PropMapping { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public static List PropNames { get; set; } = new List(); + + public static string Name { get; private set; } + + public static string TableName { get; private set; } + + public static bool IsDict { get; private set; } + + public static bool IsNameValueColl { get; private set; } + + public static bool IsAnonymousType { get; private set; } + + public static bool IsArrayParameter { get; private set; } + + public static bool IsListParameter { get; private set; } + + + public static IDataParameter[] CloneParameters(IDataParameter[] originalParameters) + { + IDataParameter[] parameterArray = new IDataParameter[originalParameters.Length]; + int index = 0; + int length = originalParameters.Length; + while (index < length) + { + parameterArray[index] = (IDataParameter)((ICloneable)originalParameters[index]).Clone(); + index++; + } + return parameterArray; + } + + } +} diff --git a/MJTop.Data/app.config b/MJTop.Data/app.config new file mode 100644 index 0000000..1c4048d --- /dev/null +++ b/MJTop.Data/app.config @@ -0,0 +1,36 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MJTop.Data/lib/DDTek.Oracle.dll b/MJTop.Data/lib/DDTek.Oracle.dll new file mode 100644 index 0000000..b5e5a4b Binary files /dev/null and b/MJTop.Data/lib/DDTek.Oracle.dll differ diff --git a/MJTop.Data/lib/System.Data.SQLite.dll b/MJTop.Data/lib/System.Data.SQLite.dll new file mode 100644 index 0000000..a7716d9 Binary files /dev/null and b/MJTop.Data/lib/System.Data.SQLite.dll differ diff --git a/MJTop.Data/packages.config b/MJTop.Data/packages.config new file mode 100644 index 0000000..4d481df --- /dev/null +++ b/MJTop.Data/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c27817 --- /dev/null +++ b/README.md @@ -0,0 +1,175 @@ +
+ DBCHM +

DBCHM

+
+
+

最简单、最实用的数据库文档生成工具

+
+ +

+visual studio 2019 +csharp +license +

+ +## 🚩 项目介绍 + +DBCHM 是一款数据库文档生成工具! +该工具从最初支持chm文档格式开始,通过开源,集思广益,不断改进,又陆续支持word、excel、pdf、html、xml、markdown等文档格式的导出。 + +### 🎯 本项目力求做最简单、最实用的数据库文档(字典)生成工具! + +## 🍀支持的数据库 +- [x] SqlServer +- [x] MySQL +- [x] Oracle +- [x] PostgreSQL +- [x] DB2 +- [x] SQLite + +## 🥝主要功能 + +### 文档的内容都包含什么? +- 序号 | 列名 | 数据类型 | 长度 | 小数位数 | 主键 | 自增 | 允许空 | 默认值 | 列说明 +- 视图 视图具体内容 +- 存储过程 存储过程具体内容 + +🔹注:Oracle在v1.8.0.3-beta版本及以后暂不会查询显示自增数据。 + +### 支持哪些文档格式的导出? +- [x] chm +- [x] word +- [x] excel +- [x] pdf +- [x] html +- [x] xml +- [x] markdown +### 更新表列的注释,有哪些方式? +- 通过 工具-批注上载,选择文件导入进行更新批注(注释): + - [x] pdm 由`powerdesigner`设计数据库时产生。 + - [x] xml 由`visual studio`设置 实体类库的项目属性,勾选 XML文档文件 后生成项目时产生。 + - [x] xml 由`dbchm`的 XML导出 而产生。 +- 列批注 在编辑前的选中状态下,可以从 选定行开始 粘贴多行文本内容 对多个列注释批量赋值。 + +## 🎉效果展示 + +### 1 数据库连接配置示例 +![数据库连接配置](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM000.png) + +### 2 数据库连接管理 +![数据库连接管理](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM001.png) + +### 3 表名模糊匹配 +![表名模糊搜索](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM002.png) + +### 4 执行批注更新 +![表批注更新](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM003.png) + +### 5 导出CHM文件 +![导出CHM文件](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM004.png) + +### 6 表结构信息 +![表结构信息](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/DBCHM005.png) + +### 7 更多格式的效果,请[下载体验](https://gitee.com/dotnetchina/DBCHM/releases)哈:wink:!! + +查看chm效果:[某微信开发框架表结构信息(示例).chm](https://gitee.com/dotnetchina/DBCHM/attach_files) + +## 📘发行历史 + +​ ReleaseNote + +## 💪贡献者 + +- @[trycache](https://gitee.com/trycache) 主要开发者 +- @[空无一物](https://gitee.com/dotnetchina/) 先驱者 + +## ⬇️下载使用 +- **[下载发行版本](https://gitee.com/dotnetchina/DBCHM/releases)**,下载解压后,双击运行 `DBCHM.exe`。 + +- **[下载最新功能的内测版本](http://shang.qq.com/wpa/qunwpa?idkey=43619cbe3b2a10ded01b5354ac6928b30cc91bda45176f89a191796b7a7c0e26)**,Bug修复早知道,最新功能尝鲜,请在[![加入QQ群](https://img.shields.io/badge/QQ群-132941648-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=43619cbe3b2a10ded01b5354ac6928b30cc91bda45176f89a191796b7a7c0e26)共享中获取:yum:! + +## 🍄 其他工具 +- [htmlhelp](https://gitee.com/dotnetchina/DBCHM/attach_files),生成chm文件时,需提前安装。 +- [PDMToCHM](https://gitee.com/dotnetchina/DBCHM/attach_files),将PDM表结构文件导出到CHM文件。 + +## 🌱开发计划 + +- [x] 收集问题,修复完善基础功能 +- [x] MJTop.Data 类库完善 +- [ ] 测试数据生成器 +- [ ] C#实体代码生成器 +- [ ] ... + +## 📖常见问题 +- **连接不上,怎么办?** + + 1. `连接数据库`界面填写的`连接信息`真的正确无误? + 2. `数据库服务器`有`防火墙/安全组`限制? + 3. 用 [Navicat Premium](https://gitee.com/dotnetchina/DBCHM/attach_files) 连接数据库服务器试试! + +- **连接数据库时,点了 `连接/测试` ,半天没响应?** + + 可能是连接远程数据库网络不好的原因,可以把`连接超时`设置的小一些。 + +- **dbchm可以连接上,但显示不了数据怎么办?** + - 导出文档前,数据库使用账号要给予`root级别`的权限,非root级别账号连接,可能会出现`表数据显示不全`或数据查询因权限不足,会`查不出来数据`! + - dbchm有Bug, [提Issue](https://gitee.com/dotnetchina/DBCHM/issues/new) 或 [进群里](http://shang.qq.com/wpa/qunwpa?idkey=43619cbe3b2a10ded01b5354ac6928b30cc91bda45176f89a191796b7a7c0e26) 反馈。 + +- **表列的批注数据我想迁移,怎么办?** + 1. 使用 dbchm 的 `XML导出`,对当前数据库的批注数据 就会导出一个xml文件。 + 2. 点`数据连接`, 切换至 目标数据库连 + 3. 再用`批注上载` 就可以选择刚刚的xml文件,如果数据库表结构相同,批注就会更新到目标数据库服上。 + +- **数据库比较老,如 `Sql Server 2000 `,怎么使用dbchm?** + 1. 下载安装 [Navicat Premium](https://gitee.com/dotnetchina/DBCHM/attach_files) + 2. 连接上老旧的数据库服务器,将数据库表结构脚本导出。 + 3. 找一台高版本的数据库服务器,新建一个临时数据库,将导出的脚本导入。 + 4. 然后用dbchm连接高版本的数据库服务器。 + +- **chm文件可以正常导出,但是文件名中文乱码,打开显示 无法访问此页** + + + 这种情况,有一种可能是win系统的**区域设置**,勾选了 + + `Beta 版:使用Unicode UTF-8提供全球语言支持` 。取消勾选后,可能不存在该问题。 + +- **Oracle数据库连上之后,一直未响应,像卡死了一样,怎么办?** + + 因为Oracle的 `列是否自增` 的sql语句,查询效率比较低,查的比较慢,没有卡死!!请耐心等待!! + + +🔹**注:因Oracle查询自增相当耗时,Oracle在v1.8.0.3-beta版本及以后暂不会查询自增数据。** + +PS:如果你有更好方法,欢迎提供改善建议,助力✊该工具越来越好使! + +- **Oracle 11g、Oracle 12c测试连接显示“[28040]ORA-28040:没有匹配的验证协议”?** + + 目前群里及isuues反馈的问题,可能11g以后的版本均会出现此项问题。 + + 该问题描述:navicat等工具可以直接连接,但是本程序连接不上有上述问题。 + + 目前想到的解决问题办法是,需在sqlnet.ora添加设置 + + ```shell + SQLNET.ALLOWED_LOGON_VERSION=8 + SQLNET.ALLOWED_LOGON_VERSION_SERVER=8 + SQLNET.ALLOWED_LOGON_VERSION_CLIENT=8 + ``` + + 参数值可设置8、10等,使用者可根据需要自行设置。 + + ![ORA-28040修改兼容](https://gitee.com/dotnetchina/DBCHM/raw/master/DBChm/Images/ORA-28040.png) + + 注意:改完后其他相关用户的密码必须重置,或直接更新为原来的密码也是可以的(修改密码sql示例:alter user System identified by oldpassword;),此项操作慎重。 + + 要么在建库的初期添加此参数,然后重置相关密码;要么新建测试环境,进行此项操作。 + + - **其他问题** + + 如遇其他问题,可以通过Issues或群里反馈,记录问题,请写清楚遇到问题的原因、软件版本、系统环境、数据库版本、甚至数据库结构、复显步骤以及期望达到的效果;建议配上多张全屏大图,请勿使用局部截屏小图!方便我们这边可以迅速定位,就事论事,解决问题。 + + 如果你有更好的解决方法,欢迎提供改善建议或直接提pr,我们一起完善该工具! + +## 🔗交流 +- QQ交流群:[![加入QQ群](https://img.shields.io/badge/QQ群-132941648-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=43619cbe3b2a10ded01b5354ac6928b30cc91bda45176f89a191796b7a7c0e26) ,推荐点击按钮入群,当然如果无法成功操作,请自行搜索群号132941648进行添加 ),其它疑问或idea欢迎入群交流! \ No newline at end of file diff --git a/ReleaseNote.md b/ReleaseNote.md new file mode 100644 index 0000000..3e8c35e --- /dev/null +++ b/ReleaseNote.md @@ -0,0 +1,25 @@ +# DBCHM - Release Notes + +====================================== +## v1.8.0.3-beta +* 优化oracle连接时超时出现 ORA-01013错误的情况(Oracle将不再查找自增列) +* 所有类型的文档,对Oracle表结构导出时,将不再显示自增列。 +* 连接/测试时,数据库下拉框,将不再加载 默认数据库,如Sql Server 的默认数据库 master、tempdb...等数据库不会再加载 + +## v1.8.0.2-beta +* 修复hhk索引文件过大,生成chm时弹出 An internal error has occurred...的情况 +* 连接/测试 按钮只进行测试连接,不进行表结构信息查询,减少测试连接时间。 +* 列批注 在编辑前的选中状态下,可以从选定行开始粘贴多行文本内容 对多个列注释批量赋值。 +* 连接数据库 连接超时的默认值由原60秒改为30秒 +* 导出 部分勾选,对视图、存储过程不起作用的情况 + +## v1.8.0.1-beta +* PostgreSql 多模式 情况下 导出会有问题的情况 +* 错误时,记录详细日志 + +## v1.8.0.0-beta +* 各类文档生成 的代码重构 +* chm/html可以使用模板生成文档 +* 支持数据库的视图、存储过程导出,默认格式化显示,并且关键字高亮 +* 软件其他细节完善 + diff --git a/htmlhelp.exe b/htmlhelp.exe new file mode 100644 index 0000000..0fd56d9 Binary files /dev/null and b/htmlhelp.exe differ diff --git a/数据库生成字典文档.txt b/数据库生成字典文档.txt new file mode 100644 index 0000000..e69de29