From 5444360515bc72a4d3070544f8e36558edfb83d3 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Tue, 25 Oct 2005 18:49:11 +0000 Subject: [PATCH] Can now assign an XSLT stylesheet to an XML file and run transforms. New XML menu to make it easier to locate xml editor menu items. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@627 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/AssignStylesheetCommand.cs | 68 +++++ .../Project/Src/OpenStylesheetCommand.cs | 35 +++ .../Project/Src/RunXslTransformCommand.cs | 96 +++++++ .../Src/StylesheetAssignedCondition.cs | 33 +++ .../XmlEditor/Project/Src/XmlView.cs | 247 +++++++++++++++--- .../XmlEditor/Project/Src/XslOutputView.cs | 52 ++++ .../XmlEditor/Project/XmlEditor.addin | 73 ++++-- .../XmlEditor/Project/XmlEditor.csproj | 9 +- .../DisplayBindings/XmlEditor/XmlEditor.sln | 10 +- 9 files changed, 565 insertions(+), 58 deletions(-) create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/AssignStylesheetCommand.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/OpenStylesheetCommand.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/RunXslTransformCommand.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/StylesheetAssignedCondition.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/XslOutputView.cs diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/AssignStylesheetCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/AssignStylesheetCommand.cs new file mode 100644 index 0000000000..78af85cdfb --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/AssignStylesheetCommand.cs @@ -0,0 +1,68 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +using System; +using System.Windows.Forms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Allows the user to browse for an XSLT stylesheet. The selected + /// stylesheet will be assigned to the currently open xml file. + /// + public class AssignStylesheetCommand : AbstractMenuCommand + { + public override void Run() + { + // Get active xml document. + XmlView xmlView = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow.ActiveViewContent as XmlView; + if (xmlView != null) { + + // Prompt user for filename. + string stylesheetFileName = BrowseForStylesheetFile(); + + // Assign stylesheet. + if (stylesheetFileName != null) { + xmlView.StylesheetFileName = stylesheetFileName; + } + } + } + + public static string BrowseForStylesheetFile() + { + using (OpenFileDialog dialog = new OpenFileDialog()) + { + dialog.AddExtension = true; + dialog.Multiselect = false; + dialog.CheckFileExists = true; + dialog.Title = "Assign XSLT Stylesheet"; + + AddInTreeNode node = AddInTree.GetTreeNode("/SharpDevelop/Workbench/FileFilter"); + if (node != null) { + + string xmlFileFilter = (string)node.BuildChildItem("Xml", null, null); + string allFilesFilter = (string)node.BuildChildItem("AllFiles", null, null); + string xslFileFilter = (string)node.BuildChildItem("Xsl", null, null); + + dialog.Filter = String.Concat(xslFileFilter, "|", xmlFileFilter, "|", allFilesFilter); + dialog.FilterIndex = 1; + } + + if (dialog.ShowDialog(ICSharpCode.SharpDevelop.Gui.WorkbenchSingleton.MainForm) == DialogResult.OK) { + return dialog.FileName; + } + } + + return null; + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/OpenStylesheetCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/OpenStylesheetCommand.cs new file mode 100644 index 0000000000..729ba81701 --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/OpenStylesheetCommand.cs @@ -0,0 +1,35 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using System; +using System.Windows.Forms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Opens the stylesheet associated with the active XML document. + /// + public class OpenStylesheetCommand : AbstractMenuCommand + { + public override void Run() + { + XmlView xmlView = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow.ActiveViewContent as XmlView; + if (xmlView != null) { + if (xmlView.StylesheetFileName != null) { + try { + FileService.OpenFile(xmlView.StylesheetFileName); + } catch (Exception ex) { + MessageService.ShowError(ex); + } + } + } + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RunXslTransformCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RunXslTransformCommand.cs new file mode 100644 index 0000000000..7be05da878 --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RunXslTransformCommand.cs @@ -0,0 +1,96 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using System; +using System.IO; +using System.Windows.Forms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Runs an XSL transform on an xml document. + /// + public class RunXslTransformCommand : AbstractCommand + { + /// + /// Runs the transform on the xml file using the assigned stylesheet. + /// If no stylesheet is assigned the user is prompted to choose one. + /// If the view represents a stylesheet that is currently assigned to an + /// opened document then run the transform on that document. + /// + public override void Run() + { + XmlView xmlView = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow.ActiveViewContent as XmlView; + if (xmlView != null) { + + if (xmlView is XslOutputView) { + return; + } + + // Check to see if this view is actually a referenced stylesheet. + if (xmlView.FileName != null && xmlView.FileName.Length > 0) { + XmlView associatedXmlView = GetAssociatedXmlView(xmlView.FileName); + if (associatedXmlView != null) { + LoggingService.Debug("Using associated xml view."); + xmlView = associatedXmlView; + } + } + + // Assign a stylesheet. + if (xmlView.StylesheetFileName == null) { + xmlView.StylesheetFileName = AssignStylesheetCommand.BrowseForStylesheetFile(); + } + + if (xmlView.StylesheetFileName != null) { + try { + xmlView.RunXslTransform(GetStylesheetContent(xmlView.StylesheetFileName)); + } catch (Exception ex) { + MessageService.ShowError(ex); + } + } + } + } + + /// + /// Gets the xml view that is currently referencing the + /// specified stylesheet view. + /// + XmlView GetAssociatedXmlView(string stylesheetFileName) + { + foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { + XmlView view = content as XmlView; + if (view != null && view.StylesheetFileName != null) { + if (FileUtility.IsEqualFileName(view.StylesheetFileName, stylesheetFileName)) { + return view; + } + } + } + return null; + } + + string GetStylesheetContent(string fileName) + { + // File already open? + IWorkbenchWindow window = FileService.GetOpenFile(fileName); + if (window != null) { + XmlView view = window.ActiveViewContent as XmlView; + if (view != null) { + return view.Text; + } else { + LoggingService.Warn("Stylesheet file not opened in xml editor."); + } + } + + // Read in file contents. + StreamReader reader = new StreamReader(fileName, true); + return reader.ReadToEnd(); + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/StylesheetAssignedCondition.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/StylesheetAssignedCondition.cs new file mode 100644 index 0000000000..983b864634 --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/StylesheetAssignedCondition.cs @@ -0,0 +1,33 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Determines whether the active XML document has been assigned + /// an XSLT stylesheet. + /// + public class StylesheetAssignedCondition : IConditionEvaluator + { + public bool IsValid(object caller, Condition condition) + { + IWorkbenchWindow window = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow; + if (window != null) { + XmlView view = window.ActiveViewContent as XmlView; + if (view != null) { + return view.StylesheetFileName != null; + } + } + return false; + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs index a206a5ca60..95677c14a7 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs @@ -15,9 +15,12 @@ using System; using System.Drawing; using System.Drawing.Printing; using System.IO; +using System.Text; using System.Windows.Forms; using System.Xml; using System.Xml.Schema; +using System.Xml.XPath; +using System.Xml.Xsl; namespace ICSharpCode.XmlEditor { @@ -37,6 +40,7 @@ namespace ICSharpCode.XmlEditor FileSystemWatcher watcher; bool wasChangedExternally; MessageViewCategory category; + string stylesheetFileName; public XmlView() { @@ -99,7 +103,7 @@ namespace ICSharpCode.XmlEditor /// public void ValidateXml() { - ClearTasks(); + TaskService.ClearExceptCommentTasks(); Category.ClearText(); OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationStarted}")); @@ -107,13 +111,17 @@ namespace ICSharpCode.XmlEditor StringReader stringReader = new StringReader(xmlEditor.Document.TextContent); XmlTextReader xmlReader = new XmlTextReader(stringReader); xmlReader.XmlResolver = null; - XmlValidatingReader reader = new XmlValidatingReader(xmlReader); - reader.XmlResolver = null; + XmlReaderSettings settings = new XmlReaderSettings(); + settings.ValidationType = ValidationType.Schema; + settings.ValidationFlags = XmlSchemaValidationFlags.None; + settings.XmlResolver = null; foreach (XmlSchemaCompletionData schemaData in XmlSchemaManager.SchemaCompletionDataItems) { - reader.Schemas.Add(schemaData.Schema); + settings.Schemas.Add(schemaData.Schema); } + XmlReader reader = XmlReader.Create(xmlReader, settings); + XmlDocument doc = new XmlDocument(); doc.Load(reader); @@ -126,9 +134,9 @@ namespace ICSharpCode.XmlEditor DisplayValidationError(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1); } - // Show tasks. - if (HasTasks && ShowTaskListAfterBuild) { - ShowTasks(); + // Show errors. + if (ShowErrorListAfterBuild) { + ShowErrorList(); } } @@ -147,7 +155,7 @@ namespace ICSharpCode.XmlEditor } public override void Dispose() - { + { base.Dispose(); ((Form)ICSharpCode.SharpDevelop.Gui.WorkbenchSingleton.Workbench).Activated -= new EventHandler(GotFocusEvent); @@ -172,7 +180,44 @@ namespace ICSharpCode.XmlEditor base.FileName = value; base.TitleName = Path.GetFileName(value); - SetDefaultSchema(Path.GetExtension(xmlEditor.FileName)); + SetDefaultSchema(Path.GetExtension(value)); + } + } + + /// + /// Gets or sets the stylesheet associated with this xml file. + /// + public string StylesheetFileName { + get { + return stylesheetFileName; + } + + set { + stylesheetFileName = value; + } + } + + /// + /// Applys the stylesheet to the xml and displays the resulting output. + /// + public void RunXslTransform(string xsl) + { + try { + TaskService.ClearExceptCommentTasks(); + + if (IsWellFormed) { + if (IsValidXsl(xsl)) { + string transformedXml = Transform(Text, xsl); + ShowTransformOutput(transformedXml); + } + } + + if (ShowErrorListAfterBuild) { + ShowErrorList(); + } + + } catch (Exception ex) { + MessageService.ShowError(ex); } } @@ -569,34 +614,16 @@ namespace ICSharpCode.XmlEditor Category.AppendText(String.Concat(message, Environment.NewLine)); } - void ClearTasks() - { - if (HasTasks) { - TaskService.ClearExceptCommentTasks(); - } - } - - bool ShowTaskListAfterBuild { - get { - return PropertyService.Get("SharpDevelop.ShowTaskListAfterBuild", true); - } - } - - bool HasTasks { + bool ShowErrorListAfterBuild { get { - bool hasTasks = false; - if (TaskService.TaskCount > 0) { - hasTasks = true; - } - return hasTasks; + return PropertyService.Get("SharpDevelop.ShowErrorListAfterBuild", true); } } - void ShowTasks() + void ShowErrorList() { - PadDescriptor pad = WorkbenchSingleton.Workbench.GetPad(typeof(OpenTaskView)); - if (pad != null) { - WorkbenchSingleton.Workbench.ShowPad(pad); + if (TaskService.TaskCount > 0) { + WorkbenchSingleton.Workbench.GetPad(typeof(ErrorList)).BringPadToFront(); } } @@ -656,5 +683,161 @@ namespace ICSharpCode.XmlEditor { SetDefaultSchema(Path.GetExtension(xmlEditor.FileName).ToLower()); } + + /// + /// Displays the transformed output. + /// + void ShowTransformOutput(string xml) + { + // Pretty print the xml. + xml = SimpleFormat(IndentedFormat(xml)); + + // Display the output xml. + XslOutputView view = XslOutputView.Instance; + if (view == null) { + view = new XslOutputView(); + view.LoadContent(xml); + WorkbenchSingleton.Workbench.ShowView(view); + } else { + // Transform output window already opened. + view.LoadContent(xml); + view.WorkbenchWindow.SelectWindow(); + } + + } + + /// + /// Returns a formatted xml string using a simple formatting algorithm. + /// + static string SimpleFormat(string xml) + { + return xml.Replace("><", ">\r\n<"); + } + + /// + /// Runs an XSL transform on the input xml. + /// + /// The input xml to transform. + /// The transform xml. + /// The output of the transform. + static string Transform(string input, string transform) + { + StringReader inputString = new StringReader(input); + XPathDocument sourceDocument = new XPathDocument(inputString); + + StringReader transformString = new StringReader(transform); + XPathDocument transformDocument = new XPathDocument(transformString); + + XslCompiledTransform xslTransform = new XslCompiledTransform(); + xslTransform.Load(transformDocument, XsltSettings.Default, new XmlUrlResolver()); + + MemoryStream outputStream = new MemoryStream(); + XmlTextWriter writer = new XmlTextWriter(outputStream, Encoding.UTF8); + + xslTransform.Transform(sourceDocument, null, writer); + + int preambleLength = Encoding.UTF8.GetPreamble().Length; + byte[] outputBytes = outputStream.ToArray(); + return UTF8Encoding.UTF8.GetString(outputBytes, preambleLength, outputBytes.Length - preambleLength); + } + + /// + /// Returns a pretty print version of the given xml. + /// + /// Xml string to pretty print. + /// A pretty print version of the specified xml. If the + /// string is not well formed xml the original string is returned. + /// + static string IndentedFormat(string xml) + { + string indentedText = String.Empty; + + try + { + XmlTextReader reader = new XmlTextReader(new StringReader(xml)); + reader.WhitespaceHandling = WhitespaceHandling.None; + + StringWriter indentedXmlWriter = new StringWriter(); + XmlTextWriter writer = new XmlTextWriter(indentedXmlWriter); + writer.Indentation = 1; + writer.IndentChar = '\t'; + writer.Formatting = Formatting.Indented; + writer.WriteNode(reader, false); + writer.Flush(); + + indentedText = indentedXmlWriter.ToString(); + } + catch(Exception) + { + indentedText = xml; + } + + return indentedText; + } + + /// + /// Checks that the xml in this view is well-formed. + /// + bool IsWellFormed { + get { + try + { + XmlDocument Document = new XmlDocument( ); + Document.LoadXml(Text); + return true; + } + catch(XmlException ex) + { + string fileName = FileName; + if (fileName == null || fileName.Length == 0) { + fileName = TitleName; + } + AddTask(fileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error); + } + return false; + } + } + + /// + /// Validates the given xsl string,. + /// + bool IsValidXsl(string xml) + { + try + { + StringReader reader = new StringReader(xml); + XPathDocument doc = new XPathDocument(reader); + + XslCompiledTransform xslTransform = new XslCompiledTransform(); + xslTransform.Load(doc, XsltSettings.Default, new XmlUrlResolver()); + + return true; + } + catch(XsltCompileException ex) + { + string message = String.Empty; + + if(ex.InnerException != null) + { + message = ex.InnerException.Message; + } + else + { + message = ex.ToString(); + } + + AddTask(StylesheetFileName, message, ex.LineNumber - 1, ex.LinePosition - 1, TaskType.Error); + } + catch(XsltException ex) + { + AddTask(StylesheetFileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error); + } + catch(XmlException ex) + { + AddTask(StylesheetFileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error); + } + + return false; + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XslOutputView.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XslOutputView.cs new file mode 100644 index 0000000000..8236ffeedb --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XslOutputView.cs @@ -0,0 +1,52 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Displays the resulting output from an XSL transform. + /// + public class XslOutputView : XmlView + { + public XslOutputView() + { + } + + public static XslOutputView Instance { + get { + foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { + if (content is XslOutputView) { + LoggingService.Debug("XslOutputView instance exists."); + LoggingService.Debug("XslOutputView.IsDisposed=" + content.Control.IsDisposed.ToString()); + return (XslOutputView)content; + } + } + return null; + } + } + + public override bool IsDirty { + get { + return false; + } + set { + } + } + + public override string TitleName { + get { + return "XSLT Output"; + } + set { + } + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin index e82662bd65..0fe1a4d163 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin @@ -6,7 +6,9 @@ version = "1.0.0"> - + + + @@ -25,6 +27,51 @@ class = "ICSharpCode.XmlEditor.Parser"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj index 95fb4c504f..6fe6096dee 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj @@ -3,7 +3,7 @@ Debug AnyCPU 2.0 - {6B717BD1-CD5E-498C-A42E-9E6A4584DC48} + {63B6CA43-58D0-4BF0-9D4F-1ABE4009E488} ICSharpCode.XmlEditor XmlEditor Library @@ -12,6 +12,8 @@ False OnSuccessfulBuild 8.0.50215 + Program + ..\..\..\..\..\bin\SharpDevelop.exe True @@ -82,6 +84,11 @@ Always + + + + + diff --git a/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln b/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln index 642d9caabf..d4433efca4 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln +++ b/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln @@ -1,8 +1,14 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# SharpDevelop 2.0.0.422 +Microsoft Visual Studio Solution File, Format Version 9.00 +# SharpDevelop 2.0.0.612 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor", "Project\XmlEditor.csproj", "{63B6CA43-58D0-4BF0-9D4F-1ABE4009E488}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor.Tests", "Test\XmlEditor.Tests.csproj", "{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core", "..\..\..\Main\Core\Project\ICSharpCode.Core.csproj", "{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor", "..\..\..\Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj", "{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}" +EndProject Global EndGlobal