//
//
//
//
// $Revision$
//
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
namespace ICSharpCode.XmlEditor
{
///
/// Wrapper class for the XmlEditor used when displaying the xml file.
///
public class XmlView : AbstractViewContent, IEditable, IClipboardHandler, IParseInformationListener, IMementoCapable, IPrintable, ITextEditorControlProvider, IPositionable, IUndoHandler
{
///
/// The language handled by this view.
///
public static readonly string Language = "XML";
///
/// Output window category name.
///
public static readonly string CategoryName = "XML";
///
/// Edit actions addin tree path for the xml editor control.
///
static readonly string editActionsPath = "/AddIns/XmlEditor/EditActions";
///
/// Right click menu addin tree path for the xml editor control.
///
static readonly string contextMenuPath = "/SharpDevelop/ViewContent/XmlEditor/ContextMenu";
XmlEditorControl xmlEditor;
TextEditorDisplayBindingWrapper.FileChangeWatcher watcher;
static MessageViewCategory category;
string stylesheetFileName;
XmlTreeView xmlTreeView;
///
/// Creates an XmlView that is used by SharpDevelop to provide an
/// XML editor.
///
public XmlView()
: this(new SharpDevelopTextEditorProperties(), XmlSchemaManager.SchemaCompletionDataItems)
{
watcher = new TextEditorDisplayBindingWrapper.FileChangeWatcher(this);
xmlEditor.AddEditActions(GetEditActions());
xmlEditor.TextAreaContextMenuStrip = MenuService.CreateContextMenu(xmlEditor, contextMenuPath);
// Add event handlers so we can update the status bar when
// the cursor position changes.
xmlEditor.ActiveTextAreaControl.Caret.CaretModeChanged += CaretModeChanged;
xmlEditor.ActiveTextAreaControl.Caret.PositionChanged += CaretChanged;
xmlEditor.ActiveTextAreaControl.Enter += CaretUpdate;
// Listen for changes to the xml editor properties.
XmlEditorAddInOptions.PropertyChanged += PropertyChanged;
XmlSchemaManager.UserSchemaAdded += UserSchemaAdded;
XmlSchemaManager.UserSchemaRemoved += UserSchemaRemoved;
xmlTreeView = new XmlTreeView(this);
SecondaryViewContents.Add(xmlTreeView);
}
///
/// Creates an XmlView that is independent of SharpDevelop. This
/// constructor does rely on SharpDevelop being available and is
/// only used for testing the XmlView.
///
public XmlView(ITextEditorProperties textEditorProperties, XmlSchemaCompletionDataCollection schemas)
{
xmlEditor = new XmlEditorControl();
xmlEditor.Dock = DockStyle.Fill;
xmlEditor.TextEditorProperties = textEditorProperties;
xmlEditor.SchemaCompletionDataItems = schemas;
xmlEditor.Document.DocumentChanged += DocumentChanged;
}
///
/// Gets the active XmlView.
///
/// if the active view is not an XmlView.
public static XmlView ActiveXmlView {
get {
IWorkbench workbench = WorkbenchSingleton.Workbench;
if (workbench != null) {
IWorkbenchWindow window = workbench.ActiveWorkbenchWindow;
if (window != null) {
return window.ActiveViewContent as XmlView;
}
}
return null;
}
}
///
/// Gets whether the active view is an XmlView.
///
public static bool IsXmlViewActive {
get {
return ActiveXmlView != null;
}
}
public override string TabPageText {
get {
return "XML";
}
}
public XmlEditorControl XmlEditor {
get {
return xmlEditor;
}
}
///
/// Loads the string content into the view.
///
public void LoadContent(string content)
{
xmlEditor.Document.TextContent = StringParser.Parse(content);
xmlEditor.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(XmlView.Language);
UpdateFolding();
}
///
/// Can create content for the 'XML' language.
///
public static bool IsLanguageHandled(string language)
{
return language == XmlView.Language;
}
///
/// Returns whether the view can handle the specified file.
///
public static bool IsFileNameHandled(string fileName)
{
return IsXmlFileExtension(Path.GetExtension(fileName));
}
///
/// Gets the known xml file extensions.
///
public static string[] GetXmlFileExtensions()
{
foreach (ParserDescriptor parser in (ParserDescriptor[])AddInTree.BuildItems("/Workspace/Parser", null, false).ToArray(typeof(ParserDescriptor))) {
if (parser.Codon.Id == "XmlFoldingParser") {
return parser.Supportedextensions;
}
}
// Did not find the XmlFoldingParser so default to those files defined by the
// HighlightingManager.
IHighlightingStrategy strategy = HighlightingManager.Manager.FindHighlighter(XmlView.Language);
if (strategy != null) {
return strategy.Extensions;
}
return new string[0];
}
///
/// Finds the xml nodes that match the specified xpath.
///
/// An array of XPathNodeMatch items. These include line number
/// and line position information aswell as the node found.
public static XPathNodeMatch[] SelectNodes(string xml, string xpath, ReadOnlyCollection namespaces)
{
XmlTextReader xmlReader = new XmlTextReader(new StringReader(xml));
xmlReader.XmlResolver = null;
XPathDocument doc = new XPathDocument(xmlReader);
XPathNavigator navigator = doc.CreateNavigator();
// Add namespaces.
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(navigator.NameTable);
foreach (XmlNamespace xmlNamespace in namespaces) {
namespaceManager.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri);
}
// Run the xpath query.
XPathNodeIterator iterator = navigator.Select(xpath, namespaceManager);
List nodes = new List();
while (iterator.MoveNext()) {
nodes.Add(new XPathNodeMatch(iterator.Current));
}
return nodes.ToArray();
}
///
/// Finds the xml nodes that match the specified xpath.
///
/// An array of XPathNodeMatch items. These include line number
/// and line position information aswell as the node found.
public static XPathNodeMatch[] SelectNodes(string xml, string xpath)
{
List list = new List();
return SelectNodes(xml, xpath, new ReadOnlyCollection(list));
}
///
/// Finds the xml nodes in the current document that match the specified xpath.
///
/// An array of XPathNodeMatch items. These include line number
/// and line position information aswell as the node found.
public XPathNodeMatch[] SelectNodes(string xpath, ReadOnlyCollection namespaces)
{
return SelectNodes(Text, xpath, namespaces);
}
///
/// Gets the XmlSchemaObject that defines the currently selected xml element or
/// attribute.
///
/// The complete xml text.
/// The current cursor index.
/// The completion data provider
public static XmlSchemaObject GetSchemaObjectSelected(string xml, int index, XmlCompletionDataProvider provider)
{
return GetSchemaObjectSelected(xml, index, provider, null);
}
///
/// Gets the XmlSchemaObject that defines the currently selected xml element or
/// attribute.
///
/// The complete xml text.
/// The current cursor index.
/// The completion data provider
/// This is the schema completion data for the
/// schema currently being displayed. This can be null if the document is
/// not a schema.
public static XmlSchemaObject GetSchemaObjectSelected(string xml, int index, XmlCompletionDataProvider provider, XmlSchemaCompletionData currentSchemaCompletionData)
{
// Find element under cursor.
XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(xml, index);
string attributeName = XmlParser.GetAttributeNameAtIndex(xml, index);
// Find schema definition object.
XmlSchemaCompletionData schemaCompletionData = provider.FindSchema(path);
XmlSchemaObject schemaObject = null;
if (schemaCompletionData != null) {
XmlSchemaElement element = schemaCompletionData.FindElement(path);
schemaObject = element;
if (element != null) {
if (attributeName.Length > 0) {
XmlSchemaAttribute attribute = schemaCompletionData.FindAttribute(element, attributeName);
if (attribute != null) {
if (currentSchemaCompletionData != null) {
schemaObject = GetSchemaObjectReferenced(xml, index, provider, currentSchemaCompletionData, element, attribute);
} else {
schemaObject = attribute;
}
}
}
return schemaObject;
}
}
return null;
}
///
/// Validates the xml against known schemas.
///
public void ValidateXml()
{
TaskService.ClearExceptCommentTasks();
Category.ClearText();
ShowOutputWindow();
OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationStarted}"));
if (IsSchema) {
if (!ValidateSchema()) {
return;
}
} else {
if (!ValidateAgainstSchema()) {
return;
}
}
OutputWindowWriteLine(String.Empty);
OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationSuccess}"));
}
///
/// Gets the name of a new view.
///
public override string UntitledName {
get {
return base.UntitledName;
}
set {
base.UntitledName = value;
xmlEditor.FileName = value;
SetDefaultSchema(Path.GetExtension(xmlEditor.FileName));
}
}
public override void Dispose()
{
base.Dispose();
if (watcher != null) {
watcher.Dispose();
XmlEditorAddInOptions.PropertyChanged -= PropertyChanged;
XmlSchemaManager.UserSchemaAdded -= new EventHandler(UserSchemaAdded);
XmlSchemaManager.UserSchemaRemoved -= new EventHandler(UserSchemaRemoved);
}
xmlEditor.Dispose();
}
///
/// Sets the filename associated with the view.
///
public override string FileName {
set {
string extension = Path.GetExtension(value);
if (Path.GetExtension(FileName) != extension) {
if (xmlEditor.Document.HighlightingStrategy != null) {
if (XmlView.IsXmlFileExtension(extension)) {
xmlEditor.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(XmlView.Language);
} else {
xmlEditor.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(value);
}
xmlEditor.Refresh();
}
}
base.FileName = value;
base.TitleName = Path.GetFileName(value);
SetDefaultSchema(extension);
}
}
///
/// 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 {
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
TaskService.ClearExceptCommentTasks();
if (IsWellFormed) {
if (IsValidXsl(xsl)) {
string transformedXml = Transform(Text, xsl);
ShowTransformOutput(transformedXml);
}
}
ShowErrorList();
} catch (Exception ex) {
MessageService.ShowError(ex);
}
}
///
/// Pretty prints the xml.
///
public void FormatXml()
{
TaskService.ClearExceptCommentTasks();
if (IsWellFormed) {
ReplaceAll(Text);
} else {
ShowErrorList();
}
}
///
/// Creates a schema based on the xml content.
///
/// A set of generated schemas or null if the xml content is not
/// well formed.
public string[] InferSchema()
{
TaskService.ClearExceptCommentTasks();
if (IsWellFormed) {
try {
using (XmlTextReader reader = new XmlTextReader(new StringReader(Text))) {
XmlSchemaInference schemaInference = new XmlSchemaInference();
XmlSchemaSet schemaSet = schemaInference.InferSchema(reader);
return GetSchemas(schemaSet);
}
} catch (XmlSchemaInferenceException ex) {
AddTask(xmlEditor.FileName, ex.Message, ex.LinePosition, ex.LineNumber, TaskType.Error);
}
}
ShowErrorList();
return null;
}
///
/// Finds the definition of the xml element or attribute under the cursor
/// in the corresponding schema and then displays that schema and the definition
/// found.
///
public void GoToSchemaDefinition()
{
// Find schema object for selected xml element or attribute.
XmlCompletionDataProvider provider = new XmlCompletionDataProvider(xmlEditor.SchemaCompletionDataItems, xmlEditor.DefaultSchemaCompletionData, xmlEditor.DefaultNamespacePrefix);
XmlSchemaCompletionData currentSchemaCompletionData = provider.FindSchemaFromFileName(FileName);
XmlSchemaObject schemaObject = GetSchemaObjectSelected(Text, xmlEditor.ActiveTextAreaControl.Caret.Offset, provider, currentSchemaCompletionData);
// Open schema.
if (schemaObject != null && schemaObject.SourceUri != null && schemaObject.SourceUri.Length > 0) {
string fileName = schemaObject.SourceUri.Replace("file:///", String.Empty);
FileService.JumpToFilePosition(fileName, schemaObject.LineNumber - 1, schemaObject.LinePosition - 1);
}
}
///
/// Checks that the xml is well formed. Any errors are displayed in the
/// errors list.
///
public void CheckIsWellFormed()
{
TaskService.ClearExceptCommentTasks();
if (!IsWellFormed) {
ShowErrorList();
}
}
///
/// Replaces the entire text of the xml view with the xml in the
/// specified. The xml will be formatted.
///
public void ReplaceAll(string xml)
{
string formattedXml = SimpleFormat(IndentedFormat(xml));
xmlEditor.Document.Replace(0, xmlEditor.Document.TextLength, formattedXml);
UpdateFolding();
}
#region IEditable interface
public IClipboardHandler ClipboardHandler {
get {
return this;
}
}
public bool EnableUndo {
get {
return xmlEditor.EnableUndo;
}
}
public bool EnableRedo {
get {
return xmlEditor.EnableRedo;
}
}
// ParserUpdateThread uses the text property via IEditable, I had an exception
// because multiple threads were accessing the GapBufferStrategy at the same time.
internal string GetText()
{
return xmlEditor.Document.TextContent;
}
internal void SetText(string value)
{
xmlEditor.Document.TextContent = value;
}
public string Text {
get {
if (WorkbenchSingleton.InvokeRequired) {
return WorkbenchSingleton.SafeThreadFunction(GetText);
} else {
return GetText();
}
}
set {
if (WorkbenchSingleton.InvokeRequired) {
WorkbenchSingleton.SafeThreadCall(SetText, value);
} else {
SetText(value);
}
}
}
public void Redo()
{
xmlEditor.Redo();
}
public void Undo()
{
xmlEditor.Undo();
}
#endregion
#region AbstractViewContent implementation
public override Control Control {
get {
return xmlEditor;
}
}
public override void Load(string fileName)
{
xmlEditor.IsReadOnly = IsFileReadOnly(fileName);
xmlEditor.LoadFile(fileName, false, true);
FileName = fileName;
TitleName = Path.GetFileName(fileName);
IsDirty = false;
// Add bookmarks.
foreach (ICSharpCode.SharpDevelop.Bookmarks.SDBookmark bookmark in ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.GetBookmarks(fileName)) {
bookmark.Document = xmlEditor.Document;
xmlEditor.Document.BookmarkManager.Marks.Add(bookmark);
}
UpdateFolding();
watcher.SetWatcher(fileName);
}
public override void Save(string fileName)
{
OnSaving(EventArgs.Empty);
watcher.Disable();
xmlEditor.SaveFile(fileName);
FileName = fileName;
TitleName = Path.GetFileName(fileName);
IsDirty = false;
watcher.SetWatcher(fileName);
OnSaved(new SaveEventArgs(true));
}
public override INavigationPoint BuildNavPoint()
{
int line = Line;
LineSegment lineSegment = xmlEditor.Document.GetLineSegment(line);
string text = xmlEditor.Document.GetText(lineSegment);
return new TextNavigationPoint(FileName, line, Column, text);
}
#endregion
#region IClipboardHandler interface
public bool EnableCut {
get {
return xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableCut;
}
}
public bool EnableCopy {
get {
return xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableCopy;
}
}
public bool EnablePaste {
get {
return xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.EnablePaste;
}
}
public bool EnableDelete {
get {
return xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableDelete;
}
}
public bool EnableSelectAll {
get {
return xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableSelectAll;
}
}
public void SelectAll()
{
xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(null, EventArgs.Empty);
}
public void Delete()
{
xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.Delete(null, EventArgs.Empty);
}
public void Paste()
{
xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(null, EventArgs.Empty);
}
public void Copy()
{
xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(null, EventArgs.Empty);
}
public void Cut()
{
xmlEditor.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(null, EventArgs.Empty);
}
#endregion
#region IParseInformationListener interface
public void ParseInformationUpdated(ParseInformation parseInfo)
{
WorkbenchSingleton.SafeThreadAsyncCall(UpdateFolding);
}
#endregion
#region IMementoCapable interface
public void SetMemento(Properties properties)
{
xmlEditor.ActiveTextAreaControl.Caret.Position = xmlEditor.Document.OffsetToPosition(Math.Min(xmlEditor.Document.TextLength, Math.Max(0, properties.Get("CaretOffset", xmlEditor.ActiveTextAreaControl.Caret.Offset))));
if (xmlEditor.Document.HighlightingStrategy.Name != properties.Get("HighlightingLanguage", xmlEditor.Document.HighlightingStrategy.Name)) {
IHighlightingStrategy highlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(properties.Get("HighlightingLanguage", xmlEditor.Document.HighlightingStrategy.Name));
if (highlightingStrategy != null) {
xmlEditor.Document.HighlightingStrategy = highlightingStrategy;
}
}
xmlEditor.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine = properties.Get("VisibleLine", 0);
xmlEditor.Document.FoldingManager.DeserializeFromString(properties.Get("Foldings", String.Empty));
}
public Properties CreateMemento()
{
Properties properties = new Properties();
properties.Set("CaretOffset", xmlEditor.ActiveTextAreaControl.Caret.Offset);
properties.Set("VisibleLine", xmlEditor.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine);
properties.Set("HighlightingLanguage", xmlEditor.Document.HighlightingStrategy.Name);
properties.Set("Foldings", xmlEditor.Document.FoldingManager.SerializeToString());
return properties;
}
#endregion
#region IPrintable interface
public PrintDocument PrintDocument{
get {
return xmlEditor.PrintDocument;
}
}
#endregion
#region ITextEditorControlProvider interface
public TextEditorControl TextEditorControl {
get {
return xmlEditor;
}
}
#endregion
#region IPositionable interface
///
/// Moves the cursor to the specified line and column.
///
public void JumpTo(int line, int column)
{
xmlEditor.ActiveTextAreaControl.JumpTo(line, column);
}
public int Line {
get {
return xmlEditor.ActiveTextAreaControl.Caret.Line;
}
}
public int Column {
get {
return xmlEditor.ActiveTextAreaControl.Caret.Column;
}
}
#endregion
protected override void OnFileNameChanged(EventArgs e)
{
base.OnFileNameChanged(e);
xmlEditor.FileName = base.FileName;
ICSharpCode.SharpDevelop.Bookmarks.SDBookmarkFactory factory = (ICSharpCode.SharpDevelop.Bookmarks.SDBookmarkFactory)xmlEditor.Document.BookmarkManager.Factory;
factory.ChangeFilename(base.FileName);
}
static bool IsFileReadOnly(string fileName)
{
return (File.GetAttributes(fileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
}
///
/// Checks that the file extension refers to an xml file as
/// specified in the SyntaxModes.xml file.
///
static bool IsXmlFileExtension(string extension)
{
foreach (string currentExtension in GetXmlFileExtensions()) {
if (String.Compare(extension, currentExtension, true) == 0) {
return true;
}
}
return false;
}
///
/// Forces the editor to update its folds.
///
void UpdateFolding()
{
xmlEditor.Document.FoldingManager.UpdateFoldings(String.Empty, null);
RefreshMargin();
}
///
/// Repaints the folds in the margin.
///
void RefreshMargin()
{
WorkbenchSingleton.SafeThreadAsyncCall(xmlEditor.ActiveTextAreaControl.TextArea.Refresh,
xmlEditor.ActiveTextAreaControl.TextArea.FoldMargin);
}
///
/// Sets the dirty flag since the document has changed.
///
void DocumentChanged(object sender, DocumentEventArgs e)
{
IsDirty = true;
}
///
/// Updates the line, col, overwrite/insert mode in the status bar.
///
void CaretUpdate(object sender, EventArgs e)
{
CaretChanged(sender, e);
CaretModeChanged(sender, e);
}
///
/// Updates the line, col information in the status bar.
///
void CaretChanged(object sender, EventArgs e)
{
TextAreaControl activeTextAreaControl = xmlEditor.ActiveTextAreaControl;
int line = activeTextAreaControl.Caret.Line;
int col = activeTextAreaControl.Caret.Column;
StatusBarService.SetCaretPosition(activeTextAreaControl.TextArea.TextView.GetVisualColumn(line, col), line, col);
}
///
/// Updates the insert/overwrite mode text in the status bar.
///
void CaretModeChanged(object sender, EventArgs e)
{
StatusBarService.SetInsertMode(xmlEditor.ActiveTextAreaControl.Caret.CaretMode == CaretMode.InsertMode);
}
///
/// Gets the xml validation output window.
///
MessageViewCategory Category {
get {
if (category == null) {
category = new MessageViewCategory(CategoryName);
CompilerMessageView cmv = (CompilerMessageView)WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).PadContent;
cmv.AddCategory(category);
}
return category;
}
}
///
/// Brings output window pad to the front.
///
void ShowOutputWindow()
{
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
}
///
/// Writes a line of text to the output window.
///
/// The message to send to the output
/// window.
void OutputWindowWriteLine(string message)
{
Category.AppendText(String.Concat(message, Environment.NewLine));
}
void ShowErrorList()
{
if (ErrorListPad.ShowAfterBuild && TaskService.SomethingWentWrong) {
WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
}
}
void AddTask(string fileName, string message, int column, int line, TaskType taskType)
{
TaskService.Add(new Task(fileName, message, column, line, taskType));
}
///
/// Displays the validation error.
///
void DisplayValidationError(string fileName, string message, int column, int line)
{
OutputWindowWriteLine(message);
AddTask(fileName, message, column, line, TaskType.Error);
}
void ShowValidationFailedMessage()
{
OutputWindowWriteLine(String.Empty);
OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationFailed}"));
}
///
/// Updates the default schema associated with the xml editor.
///
void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string extension = Path.GetExtension(xmlEditor.FileName).ToLowerInvariant();
if (e.Key == String.Concat("ext", extension)) {
SetDefaultSchema(extension);
} else if (e.Key == XmlEditorAddInOptions.ShowAttributesWhenFoldedPropertyName) {
UpdateFolding();
xmlEditor.Refresh();
}
}
///
/// Sets the default schema and namespace prefix that the xml editor will use.
///
void SetDefaultSchema(string extension)
{
xmlEditor.DefaultSchemaCompletionData = XmlSchemaManager.GetSchemaCompletionData(extension);
xmlEditor.DefaultNamespacePrefix = XmlSchemaManager.GetNamespacePrefix(extension);
}
///
/// Updates the default schema association since the schema
/// may have been added.
///
void UserSchemaAdded(object source, EventArgs e)
{
SetDefaultSchema(Path.GetExtension(xmlEditor.FileName).ToLowerInvariant());
}
///
/// Updates the default schema association since the schema
/// may have been removed.
///
void UserSchemaRemoved(object source, EventArgs e)
{
SetDefaultSchema(Path.GetExtension(xmlEditor.FileName).ToLowerInvariant());
}
///
/// 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);
XmlTextReader sourceDocument = new XmlTextReader(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.
///
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 = CreateXmlTextWriter(indentedXmlWriter);
writer.WriteNode(reader, false);
writer.Flush();
indentedText = indentedXmlWriter.ToString();
} catch(Exception) {
indentedText = xml;
}
return indentedText;
}
XmlTextWriter CreateXmlTextWriter(TextWriter textWriter)
{
XmlTextWriter writer = new XmlTextWriter(textWriter);
if (xmlEditor.TextEditorProperties.ConvertTabsToSpaces) {
writer.Indentation = xmlEditor.TextEditorProperties.TabIndent;
writer.IndentChar = ' ';
} else {
writer.Indentation = 1;
writer.IndentChar = '\t';
}
writer.Formatting = Formatting.Indented;
return writer;
}
///
/// 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) {
AddTask(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error);
}
return false;
}
}
///
/// Validates the given xsl string,.
///
bool IsValidXsl(string xml)
{
try {
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
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;
}
///
/// Validates the XML in the editor against all the schemas in the
/// schema manager.
///
bool ValidateAgainstSchema()
{
try {
StringReader stringReader = new StringReader(xmlEditor.Document.TextContent);
XmlTextReader xmlReader = new XmlTextReader(stringReader);
xmlReader.XmlResolver = null;
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = XmlSchemaValidationFlags.None;
settings.XmlResolver = null;
XmlSchemaCompletionData schemaData = null;
try {
for (int i = 0; i < XmlSchemaManager.SchemaCompletionDataItems.Count; ++i) {
schemaData = XmlSchemaManager.SchemaCompletionDataItems[i];
settings.Schemas.Add(schemaData.Schema);
}
} catch (XmlSchemaException ex) {
DisplayValidationError(schemaData.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1);
ShowValidationFailedMessage();
ShowErrorList();
return false;
}
XmlReader reader = XmlReader.Create(xmlReader, settings);
XmlDocument doc = new XmlDocument();
doc.Load(reader);
return true;
} catch (XmlSchemaException ex) {
DisplayValidationError(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1);
} catch (XmlException ex) {
DisplayValidationError(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1);
}
ShowValidationFailedMessage();
ShowErrorList();
return false;
}
///
/// Assumes the content in the editor is a schema and validates it using
/// the XmlSchema class. This is used instead of validating against the
/// XMLSchema.xsd file since it gives us better error information.
///
bool ValidateSchema()
{
StringReader stringReader = new StringReader(xmlEditor.Document.TextContent);
XmlTextReader xmlReader = new XmlTextReader(stringReader);
xmlReader.XmlResolver = null;
try {
XmlSchema schema = XmlSchema.Read(xmlReader, new ValidationEventHandler(SchemaValidation));
schema.Compile(new ValidationEventHandler(SchemaValidation));
} catch (XmlSchemaException ex) {
DisplayValidationError(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1);
} catch (XmlException ex) {
DisplayValidationError(xmlEditor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1);
} finally {
xmlReader.Close();
}
if (TaskService.SomethingWentWrong) {
ShowValidationFailedMessage();
ShowErrorList();
return false;
}
return true;
}
void SchemaValidation(object source, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Error) {
DisplayValidationError(xmlEditor.FileName, e.Message, e.Exception.LinePosition - 1, e.Exception.LineNumber - 1);
} else {
DisplayValidationWarning(xmlEditor.FileName, e.Message, e.Exception.LinePosition - 1, e.Exception.LineNumber - 1);
}
}
///
/// Displays the validation warning.
///
void DisplayValidationWarning(string fileName, string message, int column, int line)
{
OutputWindowWriteLine(message);
AddTask(fileName, message, column, line, TaskType.Warning);
}
bool IsSchema {
get {
string extension = Path.GetExtension(xmlEditor.FileName);
if (extension != null) {
return String.Compare(".xsd", extension, true) == 0;
}
return false;
}
}
///
/// Checks whether the element belongs to the XSD namespace.
///
static bool IsXmlSchemaNamespace(XmlSchemaElement element)
{
XmlQualifiedName qualifiedName = element.QualifiedName;
if (qualifiedName != null) {
return XmlSchemaManager.IsXmlSchemaNamespace(qualifiedName.Namespace);
}
return false;
}
///
/// If the attribute value found references another item in the schema
/// return this instead of the attribute schema object. For example, if the
/// user can select the attribute value and the code will work out the schema object pointed to by the ref
/// or type attribute:
///
/// xs:element ref="ref-name"
/// xs:attribute type="type-name"
///
///
/// The if no schema object was referenced.
///
static XmlSchemaObject GetSchemaObjectReferenced(string xml, int index, XmlCompletionDataProvider provider, XmlSchemaCompletionData currentSchemaCompletionData, XmlSchemaElement element, XmlSchemaAttribute attribute)
{
XmlSchemaObject schemaObject = null;
if (IsXmlSchemaNamespace(element)) {
// Find attribute value.
string attributeValue = XmlParser.GetAttributeValueAtIndex(xml, index);
if (attributeValue.Length == 0) {
return attribute;
}
if (attribute.Name == "ref") {
schemaObject = FindSchemaObjectReference(attributeValue, provider, currentSchemaCompletionData, element.Name);
} else if (attribute.Name == "type") {
schemaObject = FindSchemaObjectType(attributeValue, provider, currentSchemaCompletionData, element.Name);
}
}
if (schemaObject != null) {
return schemaObject;
}
return attribute;
}
///
/// Attempts to locate the reference name in the specified schema.
///
/// The reference to look up.
/// The schema completion data to use to
/// find the reference.
/// The element to determine what sort of reference it is
/// (e.g. group, attribute, element).
/// if no match can be found.
static XmlSchemaObject FindSchemaObjectReference(string name, XmlCompletionDataProvider provider, XmlSchemaCompletionData schemaCompletionData, string elementName)
{
QualifiedName qualifiedName = schemaCompletionData.CreateQualifiedName(name);
XmlSchemaCompletionData qualifiedNameSchema = provider.FindSchema(qualifiedName.Namespace);
if (qualifiedNameSchema != null) {
schemaCompletionData = qualifiedNameSchema;
}
switch (elementName) {
case "element":
return schemaCompletionData.FindElement(qualifiedName);
case "attribute":
return schemaCompletionData.FindAttribute(qualifiedName.Name);
case "group":
return schemaCompletionData.FindGroup(qualifiedName.Name);
case "attributeGroup":
return schemaCompletionData.FindAttributeGroup(qualifiedName.Name);
}
return null;
}
///
/// Attempts to locate the type name in the specified schema.
///
/// The type to look up.
/// The schema completion data to use to
/// find the type.
/// The element to determine what sort of type it is
/// (e.g. group, attribute, element).
/// if no match can be found.
static XmlSchemaObject FindSchemaObjectType(string name, XmlCompletionDataProvider provider, XmlSchemaCompletionData schemaCompletionData, string elementName)
{
QualifiedName qualifiedName = schemaCompletionData.CreateQualifiedName(name);
XmlSchemaCompletionData qualifiedNameSchema = provider.FindSchema(qualifiedName.Namespace);
if (qualifiedNameSchema != null) {
schemaCompletionData = qualifiedNameSchema;
}
switch (elementName) {
case "element":
return schemaCompletionData.FindComplexType(qualifiedName);
case "attribute":
return schemaCompletionData.FindSimpleType(qualifiedName.Name);
}
return null;
}
///
/// Converts a set of schemas to a string array, each array item
/// contains the schema converted to a string.
///
string[] GetSchemas(XmlSchemaSet schemaSet)
{
List schemas = new List();
foreach (XmlSchema schema in schemaSet.Schemas()) {
using (EncodedStringWriter writer = new EncodedStringWriter(xmlEditor.TextEditorProperties.Encoding)) {
using (XmlTextWriter xmlWriter = CreateXmlTextWriter(writer)) {
schema.Write(xmlWriter);
schemas.Add(writer.ToString());
}
}
}
return schemas.ToArray();
}
///
/// Gets the edit actions for the xml editor from the addin tree.
///
IEditAction[] GetEditActions()
{
return (IEditAction[])(AddInTree.BuildItems(editActionsPath, this, false).ToArray(typeof(IEditAction)));
}
}
}