// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team // // 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. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Xml; using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Highlighting { /// /// Manages a list of syntax highlighting definitions. /// /// /// All memers on this class (including instance members) are thread-safe. /// public class HighlightingManager : IHighlightingDefinitionReferenceResolver { sealed class DelayLoadedHighlightingDefinition : IHighlightingDefinition { readonly object lockObj = new object(); readonly string name; Func lazyLoadingFunction; IHighlightingDefinition definition; Exception storedException; public DelayLoadedHighlightingDefinition(string name, Func lazyLoadingFunction) { this.name = name; this.lazyLoadingFunction = lazyLoadingFunction; } public string Name { get { if (name != null) return name; else return GetDefinition().Name; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception will be rethrown")] IHighlightingDefinition GetDefinition() { Func func; lock (lockObj) { if (this.definition != null) return this.definition; func = this.lazyLoadingFunction; } Exception exception = null; IHighlightingDefinition def = null; try { using (var busyLock = BusyManager.Enter(this)) { if (!busyLock.Success) throw new InvalidOperationException("Tried to create delay-loaded highlighting definition recursively. Make sure the are no cyclic references between the highlighting definitions."); def = func(); } if (def == null) throw new InvalidOperationException("Function for delay-loading highlighting definition returned null"); } catch (Exception ex) { exception = ex; } lock (lockObj) { this.lazyLoadingFunction = null; if (this.definition == null && this.storedException == null) { this.definition = def; this.storedException = exception; } if (this.storedException != null) throw new HighlightingDefinitionInvalidException("Error delay-loading highlighting definition", this.storedException); return this.definition; } } public HighlightingRuleSet MainRuleSet { get { return GetDefinition().MainRuleSet; } } public HighlightingRuleSet GetNamedRuleSet(string name) { return GetDefinition().GetNamedRuleSet(name); } public HighlightingColor GetNamedColor(string name) { return GetDefinition().GetNamedColor(name); } public IEnumerable NamedHighlightingColors { get { return GetDefinition().NamedHighlightingColors; } } public override string ToString() { return this.Name; } public IDictionary Properties { get { return GetDefinition().Properties; } } } readonly object lockObj = new object(); Dictionary highlightingsByName = new Dictionary(); Dictionary highlightingsByExtension = new Dictionary(StringComparer.OrdinalIgnoreCase); List allHighlightings = new List(); /// /// Gets a highlighting definition by name. /// Returns null if the definition is not found. /// public IHighlightingDefinition GetDefinition(string name) { lock (lockObj) { IHighlightingDefinition rh; if (highlightingsByName.TryGetValue(name, out rh)) return rh; else return null; } } /// /// Gets a copy of all highlightings. /// public ReadOnlyCollection HighlightingDefinitions { get { lock (lockObj) { return Array.AsReadOnly(allHighlightings.ToArray()); } } } /// /// Gets a highlighting definition by extension. /// Returns null if the definition is not found. /// public IHighlightingDefinition GetDefinitionByExtension(string extension) { lock (lockObj) { IHighlightingDefinition rh; if (highlightingsByExtension.TryGetValue(extension, out rh)) return rh; else return null; } } /// /// Registers a highlighting definition. /// /// The name to register the definition with. /// The file extensions to register the definition for. /// The highlighting definition. public void RegisterHighlighting(string name, string[] extensions, IHighlightingDefinition highlighting) { if (highlighting == null) throw new ArgumentNullException("highlighting"); lock (lockObj) { allHighlightings.Add(highlighting); if (name != null) { highlightingsByName[name] = highlighting; } if (extensions != null) { foreach (string ext in extensions) { highlightingsByExtension[ext] = highlighting; } } } } /// /// Registers a highlighting definition. /// /// The name to register the definition with. /// The file extensions to register the definition for. /// A function that loads the highlighting definition. public void RegisterHighlighting(string name, string[] extensions, Func lazyLoadedHighlighting) { if (lazyLoadedHighlighting == null) throw new ArgumentNullException("lazyLoadedHighlighting"); RegisterHighlighting(name, extensions, new DelayLoadedHighlightingDefinition(name, lazyLoadedHighlighting)); } /// /// Gets the default HighlightingManager instance. /// The default HighlightingManager comes with built-in highlightings. /// public static HighlightingManager Instance { get { return DefaultHighlightingManager.Instance; } } internal sealed class DefaultHighlightingManager : HighlightingManager { public new static readonly DefaultHighlightingManager Instance = new DefaultHighlightingManager(); public DefaultHighlightingManager() { Resources.RegisterBuiltInHighlightings(this); } // Registering a built-in highlighting internal void RegisterHighlighting(string name, string[] extensions, string resourceName) { try { #if DEBUG // don't use lazy-loading in debug builds, show errors immediately Xshd.XshdSyntaxDefinition xshd; using (Stream s = Resources.OpenStream(resourceName)) { using (XmlTextReader reader = new XmlTextReader(s)) { xshd = Xshd.HighlightingLoader.LoadXshd(reader, false); } } Debug.Assert(name == xshd.Name); if (extensions != null) Debug.Assert(System.Linq.Enumerable.SequenceEqual(extensions, xshd.Extensions)); else Debug.Assert(xshd.Extensions.Count == 0); // round-trip xshd: // string resourceFileName = Path.Combine(Path.GetTempPath(), resourceName); // using (XmlTextWriter writer = new XmlTextWriter(resourceFileName, System.Text.Encoding.UTF8)) { // writer.Formatting = Formatting.Indented; // new Xshd.SaveXshdVisitor(writer).WriteDefinition(xshd); // } // using (FileStream fs = File.Create(resourceFileName + ".bin")) { // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, xshd); // } // using (FileStream fs = File.Create(resourceFileName + ".compiled")) { // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, Xshd.HighlightingLoader.Load(xshd, this)); // } RegisterHighlighting(name, extensions, Xshd.HighlightingLoader.Load(xshd, this)); #else RegisterHighlighting(name, extensions, LoadHighlighting(resourceName)); #endif } catch (HighlightingDefinitionInvalidException ex) { throw new InvalidOperationException("The built-in highlighting '" + name + "' is invalid.", ex); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "LoadHighlighting is used only in release builds")] Func LoadHighlighting(string resourceName) { Func func = delegate { Xshd.XshdSyntaxDefinition xshd; using (Stream s = Resources.OpenStream(resourceName)) { using (XmlTextReader reader = new XmlTextReader(s)) { // in release builds, skip validating the built-in highlightings xshd = Xshd.HighlightingLoader.LoadXshd(reader, true); } } return Xshd.HighlightingLoader.Load(xshd, this); }; return func; } } } }