Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins/Xml/Parser/XmlParser.cs')
-rw-r--r--main/src/addins/Xml/Parser/XmlParser.cs424
1 files changed, 424 insertions, 0 deletions
diff --git a/main/src/addins/Xml/Parser/XmlParser.cs b/main/src/addins/Xml/Parser/XmlParser.cs
new file mode 100644
index 0000000000..de40145786
--- /dev/null
+++ b/main/src/addins/Xml/Parser/XmlParser.cs
@@ -0,0 +1,424 @@
+//
+// Parser.cs
+//
+// Author:
+// Michael Hutchinson <mhutchinson@novell.com>
+//
+// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
+//
+// 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.Text;
+
+using ICSharpCode.NRefactory;
+using ICSharpCode.NRefactory.TypeSystem;
+using MonoDevelop.Ide.Gui.Content;
+using MonoDevelop.Xml.Dom;
+
+namespace MonoDevelop.Xml.Parser
+{
+ public class XmlParser : IDocumentStateEngine, IXmlParserContext
+ {
+ XmlRootState rootState;
+ XmlParserState currentState;
+ XmlParserState previousState;
+ bool buildTree;
+
+ int position;
+ TextLocation location;
+ TextLocation previousLineEnd;
+ int stateTag;
+ StringBuilder keywordBuilder;
+ int currentStateLength;
+ NodeStack nodes;
+ List<Error> errors;
+
+ public XmlParser (XmlRootState rootState, bool buildTree)
+ {
+ this.rootState = rootState;
+ this.currentState = rootState;
+ this.previousState = rootState;
+ this.buildTree = buildTree;
+ Reset ();
+ }
+
+ XmlParser (XmlParser copyFrom)
+ {
+ buildTree = false;
+
+ rootState = copyFrom.rootState;
+ currentState = copyFrom.currentState;
+ previousState = copyFrom.previousState;
+
+ position = copyFrom.position;
+ previousLineEnd = copyFrom.location;
+ location = copyFrom.location;
+ stateTag = copyFrom.stateTag;
+ keywordBuilder = new StringBuilder (copyFrom.keywordBuilder.ToString ());
+ currentStateLength = copyFrom.currentStateLength;
+
+ //clone the node stack
+ var l = new List<XObject> (CopyXObjects (copyFrom.nodes));
+ l.Reverse ();
+ nodes = new NodeStack (l);
+ }
+
+ IEnumerable<XObject> CopyXObjects (IEnumerable<XObject> src)
+ {
+ foreach (XObject o in src)
+ yield return o.ShallowCopy ();
+ }
+
+ public XmlRootState RootState { get { return rootState; } }
+
+ #region IDocumentStateEngine
+
+ public int Position { get { return position; } }
+ public TextLocation Location { get { return location; } }
+
+ public void Reset ()
+ {
+ currentState = rootState;
+ previousState = rootState;
+ position = 0;
+ stateTag = 0;
+ location = new TextLocation (1, 1);
+ previousLineEnd = TextLocation.Empty;
+ keywordBuilder = new StringBuilder ();
+ currentStateLength = 0;
+ nodes = new NodeStack ();
+ nodes.Push (rootState.CreateDocument ());
+
+ if (buildTree)
+ errors = new List<Error> ();
+ else
+ errors = null;
+ }
+
+ public void Parse (System.IO.TextReader reader)
+ {
+ int i = reader.Read ();
+ while (i >= 0) {
+ char c = (char) i;
+ Push (c);
+ i = reader.Read ();
+ }
+ }
+
+ public void Push (char c)
+ {
+ try {
+ //FIXME: position/location should be at current char, not after it
+ position++;
+ if (c == '\n') {
+ previousLineEnd = new TextLocation (location.Line, location.Column + 1);
+ location = new TextLocation (location.Line + 1, 1);
+ } else {
+ location = new TextLocation (location.Line, location.Column + 1);
+ }
+
+ for (int loopLimit = 0; loopLimit < 10; loopLimit++) {
+ currentStateLength++;
+ string rollback = null;
+ if (currentState == null)
+ return;
+ XmlParserState nextState = currentState.PushChar (c, this, ref rollback);
+
+ // no state change
+ if (nextState == currentState || nextState == null)
+ return;
+
+ // state changed; reset stuff
+ previousState = currentState;
+ currentState = nextState;
+ stateTag = 0;
+ currentStateLength = 0;
+ if (keywordBuilder.Length < 50)
+ keywordBuilder.Length = 0;
+ else
+ keywordBuilder = new StringBuilder ();
+
+
+ // only loop if the same char should be run through the new state
+ if (rollback == null)
+ return;
+
+ //simple rollback, just run same char through again
+ if (rollback.Length == 0)
+ continue;
+
+ //"complex" rollbacks require actually skipping backwards.
+ //Note the previous state is invalid for this operation.
+
+ //rollback position and location so they're valid
+ position -= (rollback.Length + 1);
+ location = new TextLocation (location.Line, location.Column - (rollback.Length + 1));
+ if (location.Column < 0)
+ throw new InvalidOperationException ("Can't roll back across line boundary");
+
+ foreach (char rollChar in rollback)
+ Push (rollChar);
+
+ //restore position and location
+ position++;
+ location = new TextLocation (location.Line, location.Column + 1);
+ }
+ throw new InvalidOperationException ("Too many state changes for char '" + c + "'. Current state is " + currentState.ToString () + ".");
+ } catch (Exception ex) {
+ //attach parser state to exceptions
+ throw new Exception (ToString (), ex);
+ }
+ }
+
+ object ICloneable.Clone ()
+ {
+ if (buildTree)
+ throw new InvalidOperationException ("Parser can only be cloned when in stack mode");
+ return new XmlParser (this);
+ }
+
+ #endregion
+
+ public XmlParser GetTreeParser ()
+ {
+ if (buildTree)
+ throw new InvalidOperationException ("Parser can only be cloned when in stack mode");
+ XmlParser p = new XmlParser (this);
+
+ p.buildTree = true;
+
+ //reconnect the node tree
+ ((IXmlParserContext)p).ConnectAll ();
+
+ p.errors = new List<Error> ();
+
+ return p;
+ }
+
+ public override string ToString ()
+ {
+ StringBuilder builder = new StringBuilder ();
+ builder.AppendFormat ("[Parser Location={0} CurrentStateLength={1}", position, currentStateLength);
+ builder.AppendLine ();
+
+ builder.Append (' ', 2);
+ builder.AppendLine ("Stack=");
+
+ XObject rootOb = null;
+ foreach (XObject ob in nodes) {
+ rootOb = ob;
+ builder.Append (' ', 4);
+ builder.Append (ob.ToString ());
+ builder.AppendLine ();
+ }
+
+ builder.Append (' ', 2);
+ builder.AppendLine ("States=");
+ XmlParserState s = currentState;
+ while (s != null) {
+ builder.Append (' ', 4);
+ builder.Append (s.ToString ());
+ builder.AppendLine ();
+ s = s.Parent;
+ }
+
+ if (buildTree && rootOb != null) {
+ builder.Append (' ', 2);
+ builder.AppendLine ("Tree=");
+ rootOb.BuildTreeString (builder, 3);
+ }
+
+ if (buildTree && errors.Count > 0) {
+ builder.Append (' ', 2);
+ builder.AppendLine ("Errors=");
+ foreach (Error err in errors) {
+ builder.Append (' ', 4);
+ builder.AppendFormat ("[{0}@{1}:{2}, {3}]\n", err.ErrorType, err.Region.BeginLine,
+ err.Region.BeginColumn, err.Message);
+ }
+ }
+
+ builder.AppendLine ("]");
+ return builder.ToString ();
+ }
+
+ #region IParseContext
+
+ int IXmlParserContext.StateTag {
+ get { return stateTag; }
+ set { stateTag = value; }
+ }
+
+ TextLocation IXmlParserContext.LocationMinus (int colOffset)
+ {
+ int col = Location.Column - colOffset;
+ int line = Location.Line;
+ if (col <= 0) {
+ col = previousLineEnd.Column + col;
+ line -= 1;
+ System.Diagnostics.Debug.Assert (col > 0);
+ }
+ System.Diagnostics.Debug.Assert (line > 0);
+ return new TextLocation (line, col);
+ }
+
+ StringBuilder IXmlParserContext.KeywordBuilder {
+ get { return keywordBuilder; }
+ }
+
+ XmlParserState IXmlParserContext.PreviousState {
+ get { return previousState; }
+ }
+
+ public int CurrentStateLength { get { return currentStateLength; } }
+ public NodeStack Nodes { get { return nodes; } }
+
+ public bool BuildTree { get { return buildTree; } }
+
+ void IXmlParserContext.LogError (string message)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Error, message, Location));
+ }
+
+ void IXmlParserContext.LogWarning (string message)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Warning, message, ((IXmlParserContext)this).LocationMinus (1)));
+ }
+
+ void IXmlParserContext.LogError (string message, TextLocation location)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Error, message, ((IXmlParserContext)this).LocationMinus (1)));
+ }
+
+ void IXmlParserContext.LogWarning (string message, TextLocation location)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Warning, message, location));
+ }
+
+ void IXmlParserContext.LogError (string message, DomRegion region)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Error, message, region));
+ }
+
+ void IXmlParserContext.LogWarning (string message, DomRegion region)
+ {
+ if (errors != null || ErrorLogged != null)
+ InternalLogError (new Error (ErrorType.Warning, message, region));
+ }
+
+ void InternalLogError (Error err)
+ {
+ if (errors != null)
+ errors.Add (err);
+ if (ErrorLogged != null)
+ ErrorLogged (err);
+ }
+
+ void IXmlParserContext.ConnectAll ()
+ {
+ XNode prev = null;
+ foreach (XObject o in Nodes) {
+ XContainer container = o as XContainer;
+ if (prev != null && container != null && prev.IsComplete)
+ container.AddChildNode (prev);
+ if (o.Parent != null)
+ break;
+ prev = o as XNode;
+ }
+ }
+
+ void IXmlParserContext.EndAll (bool pop)
+ {
+ int popCount = 0;
+ foreach (XObject ob in Nodes) {
+ if (!ob.IsEnded && !(ob is XDocument)) {
+ ob.End (Location);
+ popCount++;
+ } else {
+ break;
+ }
+ }
+ if (pop)
+ for (; popCount > 0; popCount--)
+ Nodes.Pop ();
+ }
+
+ #endregion
+
+ public XmlParserState CurrentState { get { return currentState; } }
+
+ public IList<Error> Errors { get { return errors; } }
+
+ public event Action<Error> ErrorLogged;
+ }
+
+ public interface IXmlParserContext
+ {
+ int StateTag { get; set; }
+ StringBuilder KeywordBuilder { get; }
+ int CurrentStateLength { get; }
+ TextLocation Location { get; }
+ TextLocation LocationMinus (int colOffset);
+ XmlParserState PreviousState { get; }
+ NodeStack Nodes { get; }
+ bool BuildTree { get; }
+ void LogError (string message);
+ void LogWarning (string message);
+ void LogError (string message, TextLocation location);
+ void LogWarning (string message, TextLocation location);
+ void LogError (string message, DomRegion region);
+ void LogWarning (string message, DomRegion region);
+ void EndAll (bool pop);
+ void ConnectAll ();
+ }
+
+ public class NodeStack : Stack<XObject>
+ {
+ public NodeStack (IEnumerable<XObject> collection) : base (collection) {}
+ public NodeStack () {}
+
+ public XObject Peek (int down)
+ {
+ int i = 0;
+ foreach (XObject o in this) {
+ if (i == down)
+ return o;
+ i++;
+ }
+ return null;
+ }
+
+ public XDocument GetRoot ()
+ {
+ XObject last = null;
+ foreach (XObject o in this)
+ last = o;
+ return last as XDocument;
+ }
+ }
+}