XmlCache.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:16k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Xml;
  4. using System.Xml.Schema;
  5. using System.Xml.XPath;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Windows.Forms;
  9. using System.ComponentModel;
  10. using System.Text;
  11. using System.Net;
  12. using System.Net.Cache;
  13. namespace XmlNotepad
  14. {   
  15.     /// <summary>
  16.     /// XmlCache wraps an XmlDocument and provides the stuff necessary for an "editor" in terms
  17.     /// of watching for changes on disk, notification when the file has been reloaded, and keeping
  18.     /// track of the current file name and dirty state.
  19.     /// </summary>
  20.     public class XmlCache : IDisposable
  21.     {
  22.         string filename;
  23.         string xsltFilename;
  24.         bool dirty;
  25.         DomLoader loader;
  26.         XmlDocument doc;
  27.         FileSystemWatcher watcher;
  28.         int retries;
  29.         Timer timer = new Timer();
  30.         ISynchronizeInvoke sync;
  31.         //string namespaceUri = string.Empty;
  32.         SchemaCache schemaCache;
  33.         Dictionary<XmlNode, XmlSchemaInfo> typeInfo;
  34.         int batch;
  35.         DateTime lastModified;
  36.         Checker checker;
  37.         IServiceProvider site;
  38.         public event EventHandler FileChanged;
  39.         public event EventHandler<ModelChangedEventArgs> ModelChanged;
  40.         public XmlCache(IServiceProvider site, ISynchronizeInvoke sync)
  41.         {
  42.             this.loader = new DomLoader(site);
  43.             this.schemaCache = new SchemaCache(site);
  44.             this.site = site;
  45.             this.sync = sync;
  46.             this.Document = new XmlDocument();
  47.             this.timer.Tick += new EventHandler(Reload);
  48.             this.timer.Interval = 1000;
  49.             this.timer.Enabled = false;
  50.         }
  51.         ~XmlCache() {
  52.             Dispose(false);
  53.         }
  54.         public Uri Location {
  55.             get { return new Uri(this.filename); }
  56.         }
  57.         public string FileName {
  58.             get { return this.filename; }
  59.         }
  60.         public bool IsFile {
  61.             get {
  62.                 if (!string.IsNullOrEmpty(this.filename)) {
  63.                     return this.Location.IsFile;
  64.                 }
  65.                 return false;
  66.             }
  67.         }
  68.         /// <summary>
  69.         /// File path to (optionally user-specified) xslt file.
  70.         /// </summary>
  71.         public string XsltFileName
  72.         {
  73.             get {
  74.                 return this.xsltFilename;
  75.             }
  76.             set { this.xsltFilename = value; }
  77.         }
  78.         public bool Dirty
  79.         {
  80.             get { return this.dirty; }
  81.         }
  82.         public XmlResolver SchemaResolver {
  83.             get {
  84.                 return this.schemaCache.Resolver;
  85.             }
  86.         }
  87.         public XPathNavigator Navigator
  88.         {
  89.             get
  90.             {
  91.                 XPathDocument xdoc = new XPathDocument(this.filename);
  92.                 XPathNavigator nav = xdoc.CreateNavigator();
  93.                 return nav;
  94.             }
  95.         }
  96.         public void ValidateModel(TaskHandler handler) {
  97.             this.checker = new Checker(handler);
  98.             checker.Validate(this);
  99.         }
  100.       
  101.         public XmlDocument Document
  102.         {
  103.             get { return this.doc; }
  104.             set
  105.             {
  106.                 if (this.doc != null)
  107.                 {
  108.                     this.doc.NodeChanged -= new XmlNodeChangedEventHandler(OnDocumentChanged);
  109.                     this.doc.NodeInserted -= new XmlNodeChangedEventHandler(OnDocumentChanged);
  110.                     this.doc.NodeRemoved -= new XmlNodeChangedEventHandler(OnDocumentChanged);
  111.                 }
  112.                 this.doc = value;
  113.                 if (this.doc != null)
  114.                 {
  115.                     this.doc.NodeChanged += new XmlNodeChangedEventHandler(OnDocumentChanged);
  116.                     this.doc.NodeInserted += new XmlNodeChangedEventHandler(OnDocumentChanged);
  117.                     this.doc.NodeRemoved += new XmlNodeChangedEventHandler(OnDocumentChanged);
  118.                 }
  119.             }
  120.         }
  121.         public Dictionary<XmlNode, XmlSchemaInfo> TypeInfoMap {
  122.             get { return this.typeInfo; }
  123.             set { this.typeInfo = value; }
  124.         }
  125.         public XmlSchemaInfo GetTypeInfo(XmlNode node) {
  126.             if (this.typeInfo == null) return null;
  127.             if (this.typeInfo.ContainsKey(node)) {
  128.                 return this.typeInfo[node];
  129.             }
  130.             return null;
  131.         }
  132.         /// <summary>
  133.         /// Provides schemas used for validation.
  134.         /// </summary>
  135.         public SchemaCache SchemaCache
  136.         {
  137.             get { return this.schemaCache; }
  138.             set { this.schemaCache = value; }
  139.         }
  140.         
  141.         /// <summary>
  142.         /// Loads an instance of xml.
  143.         /// Load updated to handle validation when instance doc refers to schema.
  144.         /// </summary>
  145.         /// <param name="file">Xml instance document</param>
  146.         /// <returns></returns>
  147.         public void Load(string file)
  148.         {
  149.             this.Clear();
  150.             loader = new DomLoader(this.site);
  151.             StopFileWatch();
  152.             Uri uri = new Uri(file, UriKind.RelativeOrAbsolute);
  153.             if (!uri.IsAbsoluteUri) {
  154.                 Uri resolved = new Uri(new Uri(Directory.GetCurrentDirectory() + "\"), uri);
  155.                 file = resolved.LocalPath;
  156.             }
  157.             this.filename = file;
  158.             this.lastModified = this.LastModTime;
  159.             this.dirty = false;
  160.             StartFileWatch();
  161.             XmlReaderSettings settings = GetReaderSettings();
  162.             settings.ValidationEventHandler += new ValidationEventHandler(OnValidationEvent);
  163.             using (XmlReader reader = XmlReader.Create(file, settings)) {
  164.                 this.Document = loader.Load(reader);
  165.             }
  166.             this.xsltFilename = this.loader.XsltFileName;
  167.             // calling this event will cause the XmlTreeView to populate
  168.             FireModelChanged(ModelChangeType.Reloaded, this.doc);
  169.         }
  170.         internal XmlReaderSettings GetReaderSettings() {
  171.             XmlReaderSettings settings = new XmlReaderSettings();
  172.             settings.ProhibitDtd = false;
  173.             settings.CheckCharacters = false;
  174.             settings.XmlResolver = new XmlProxyResolver(this.site);
  175.             return settings;
  176.         }
  177.         public void ExpandIncludes() {
  178.             if (this.Document != null) {
  179.                 this.dirty = true;
  180.                 XmlReaderSettings s = new XmlReaderSettings();
  181.                 s.ProhibitDtd = false;
  182.                 s.XmlResolver = new XmlProxyResolver(this.site);
  183.                 using (XmlReader r = XmlIncludeReader.CreateIncludeReader(this.Document, s, this.FileName)) {
  184.                     this.Document = loader.Load(r);
  185.                 }
  186.                 // calling this event will cause the XmlTreeView to populate
  187.                 FireModelChanged(ModelChangeType.Reloaded, this.doc);
  188.             }
  189.         }
  190.         public void BeginUpdate() {
  191.             if (batch == 0)
  192.                 FireModelChanged(ModelChangeType.BeginBatchUpdate, this.doc);
  193.             batch++;
  194.         }
  195.         public void EndUpdate() {
  196.             batch--;
  197.             if (batch == 0)
  198.                 FireModelChanged(ModelChangeType.EndBatchUpdate, this.doc);
  199.         }
  200.         public LineInfo GetLineInfo(XmlNode node) {
  201.             return loader.GetLineInfo(node);
  202.         }
  203.         void OnValidationEvent(object sender, ValidationEventArgs e)
  204.         {
  205.             // todo: log errors in error list window.
  206.         }                
  207.         public void Reload()
  208.         {
  209.             string filename = this.filename;
  210.             Clear();
  211.             Load(filename);
  212.         }
  213.         public void Clear()
  214.         {
  215.             this.Document = new XmlDocument();
  216.             StopFileWatch();
  217.             this.filename = null;
  218.             FireModelChanged(ModelChangeType.Reloaded, this.doc);
  219.         }
  220.         public void Save()
  221.         {
  222.             Save(this.filename);
  223.         }
  224.         public Encoding GetEncoding() {
  225.             XmlDeclaration xmldecl = doc.FirstChild as XmlDeclaration;
  226.             if (xmldecl == null) return Encoding.UTF8;
  227.             try {
  228.                 string e = xmldecl.Encoding;
  229.                 if (!string.IsNullOrEmpty(e))
  230.                     return Encoding.GetEncoding(e);
  231.             } catch (Exception) {
  232.             }
  233.             return Encoding.UTF8;
  234.         }
  235.         public void Save(string name)
  236.         {
  237.             try
  238.             {
  239.                 StopFileWatch();
  240.                 XmlWriterSettings s = new XmlWriterSettings();
  241.                 Utilities.InitializeWriterSettings(s, this.site);                
  242.                 s.Encoding = GetEncoding();
  243.                 using (XmlWriter w = XmlWriter.Create(name, s)) {
  244.                     doc.Save(w);
  245.                 }
  246.                 this.dirty = false;
  247.                 this.filename = name;
  248.                 this.lastModified = this.LastModTime;
  249.                 FireModelChanged(ModelChangeType.Saved, this.doc);
  250.             }
  251.             finally
  252.             {
  253.                 StartFileWatch();
  254.             }
  255.         }
  256.         public bool IsReadOnly(string filename) {
  257.             return File.Exists(filename) &&
  258.                 (File.GetAttributes(filename) & FileAttributes.ReadOnly) != 0;
  259.         }
  260.         public void MakeReadWrite(string filename) {
  261.             if (!File.Exists(filename))
  262.                 return;
  263.             StopFileWatch();
  264.             try {
  265.                 FileAttributes attrsMinusReadOnly = File.GetAttributes(this.filename) & ~FileAttributes.ReadOnly;
  266.                 File.SetAttributes(filename, attrsMinusReadOnly);
  267.             } finally {
  268.                 StartFileWatch();
  269.             }           
  270.         }
  271.         void StopFileWatch()
  272.         {
  273.             if (this.watcher != null)
  274.             {
  275.                 this.watcher.Dispose();
  276.                 this.watcher = null;
  277.             }
  278.         }
  279.         private void StartFileWatch()
  280.         {
  281.             if (this.filename != null && Location.IsFile && File.Exists(this.filename))
  282.             {
  283.                 string dir = Path.GetDirectoryName(this.filename) + "\";
  284.                 this.watcher = new FileSystemWatcher(dir, "*.*");
  285.                 this.watcher.Changed += new FileSystemEventHandler(watcher_Changed);
  286.                 this.watcher.Renamed += new RenamedEventHandler(watcher_Renamed);
  287.                 this.watcher.EnableRaisingEvents = true;
  288.             }
  289.             else
  290.             {
  291.                 StopFileWatch();
  292.             }
  293.         }
  294.         void StartReload(object sender, EventArgs e)
  295.         {
  296.             // Apart from retrying, the timer has the nice side effect of also 
  297.             // collapsing multiple file system events into one timer event.
  298.             retries = 3;
  299.             timer.Enabled = true;
  300.             timer.Start();
  301.         }
  302.         DateTime LastModTime {
  303.             get {
  304.                 if (Location.IsFile) return File.GetLastWriteTime(this.filename);
  305.                 return DateTime.Now;
  306.             }
  307.         }
  308.         void Reload(object sender, EventArgs e)
  309.         {
  310.             try
  311.             {
  312.                 // Only do the reload if the file on disk really is different from
  313.                 // what we last loaded.
  314.                 if (this.lastModified < LastModTime) {
  315.                     
  316.                     // Test if we can open the file (it might still be locked).
  317.                     FileStream fs = new FileStream(this.filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  318.                     fs.Close();
  319.                     timer.Enabled = false;
  320.                     FireFileChanged();
  321.                 }
  322.             }
  323.             finally
  324.             {
  325.                 retries--;
  326.                 if (retries == 0)
  327.                 {
  328.                     timer.Enabled = false;
  329.                 }
  330.             }
  331.         }
  332.         private void watcher_Changed(object sender, FileSystemEventArgs e)
  333.         {
  334.             if (e.ChangeType == WatcherChangeTypes.Changed && 
  335.                 IsSamePath(this.filename, e.FullPath))
  336.             {
  337.                 sync.BeginInvoke(new EventHandler(StartReload), new object[] { this, EventArgs.Empty });
  338.             }
  339.         }
  340.         private void watcher_Renamed(object sender, RenamedEventArgs e)
  341.         {
  342.             if (IsSamePath(this.filename, e.OldFullPath))
  343.             {
  344.                 StopFileWatch();
  345.                 this.filename = e.FullPath;
  346.                 StartFileWatch();
  347.                 sync.BeginInvoke(new EventHandler(StartReload), new object[] { this, EventArgs.Empty });
  348.             }
  349.         }
  350.         static bool IsSamePath(string a, string b)
  351.         {
  352.             return string.Compare(a, b, true) == 0;
  353.         }
  354.         void FireFileChanged()
  355.         {
  356.             if (this.FileChanged != null)
  357.             {
  358.                 FileChanged(this, EventArgs.Empty);
  359.             }
  360.         }
  361.         void FireModelChanged(ModelChangeType t, XmlNode node)
  362.         {
  363.             if (this.ModelChanged != null)
  364.                 this.ModelChanged(this, new ModelChangedEventArgs(t, node));
  365.         }
  366.         void OnPIChange(XmlNodeChangedEventArgs e) {
  367.             XmlProcessingInstruction pi = (XmlProcessingInstruction)e.Node;
  368.             if (pi.Name == "xml-stylesheet") {
  369.                 if (e.Action == XmlNodeChangedAction.Remove) {
  370.                     // see if there's another!
  371.                     pi = (XmlProcessingInstruction)this.doc.SelectSingleNode("processing-instruction('xml-stylesheet')");
  372.                 }
  373.                 if (pi != null) {
  374.                     this.xsltFilename = DomLoader.ParseXsltArgs(pi.Data);
  375.                 }
  376.             }
  377.         }
  378.         private void OnDocumentChanged(object sender, XmlNodeChangedEventArgs e)
  379.         {
  380.             // initialize t
  381.             ModelChangeType t = ModelChangeType.NodeChanged;
  382.             if (e.Node is XmlProcessingInstruction) {
  383.                 OnPIChange(e);
  384.             }
  385.             if (XmlHelpers.IsXmlnsNode(e.NewParent) || XmlHelpers.IsXmlnsNode(e.Node)) {
  386.                 // we flag a namespace change whenever an xmlns attribute changes.
  387.                 t = ModelChangeType.NamespaceChanged;
  388.                 XmlNode node = e.Node;
  389.                 if (e.Action == XmlNodeChangedAction.Remove) {
  390.                     node = e.OldParent; // since node.OwnerElement link has been severed!
  391.                 }
  392.                 this.dirty = true;
  393.                 FireModelChanged(t, node);
  394.             } else {
  395.                 switch (e.Action) {
  396.                     case XmlNodeChangedAction.Change:
  397.                         t = ModelChangeType.NodeChanged;
  398.                         break;
  399.                     case XmlNodeChangedAction.Insert:
  400.                         t = ModelChangeType.NodeInserted;
  401.                         break;
  402.                     case XmlNodeChangedAction.Remove:
  403.                         t = ModelChangeType.NodeRemoved;
  404.                         break;
  405.                 }
  406.                 this.dirty = true;
  407.                 FireModelChanged(t, e.Node);
  408.             }
  409.         }
  410.         public void Dispose() {
  411.             Dispose(true);
  412.         }
  413.         protected virtual void Dispose(bool disposing) {
  414.             if (timer != null) {
  415.                 timer.Dispose();
  416.                 timer = null;
  417.             }
  418.             StopFileWatch();
  419.             GC.SuppressFinalize(this);
  420.         }
  421.     }
  422.     public enum ModelChangeType
  423.     {
  424.         Reloaded,
  425.         Saved,
  426.         NodeChanged,
  427.         NodeInserted,
  428.         NodeRemoved,
  429.         NamespaceChanged,
  430.         BeginBatchUpdate,
  431.         EndBatchUpdate,
  432.     }
  433.     public class ModelChangedEventArgs : EventArgs
  434.     {
  435.         ModelChangeType type;
  436.         XmlNode node;
  437.         public ModelChangedEventArgs(ModelChangeType t, XmlNode node)
  438.         {
  439.             this.type = t;
  440.             this.node = node;
  441.         }
  442.         public XmlNode Node {
  443.             get { return node; }
  444.             set { node = value; }
  445.         }
  446.         public ModelChangeType ModelChangeType
  447.         {
  448.             get { return this.type; }
  449.             set { this.type = value; }
  450.         }
  451.     }
  452.     public enum IndentChar {
  453.         Space,
  454.         Tab
  455.     }
  456. }