5
// Michael Hutchinson <mhutchinson@novell.com>
7
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
using System.Collections.Generic;
33
using MonoDevelop.Projects.Dom;
34
using MonoDevelop.Ide.Gui.Content;
36
namespace MonoDevelop.Xml.StateEngine
38
public class Parser : IDocumentStateEngine, IParseContext
48
StringBuilder keywordBuilder;
49
int currentStateLength;
53
public Parser (RootState rootState, bool buildTree)
55
this.rootState = rootState;
56
this.currentState = rootState;
57
this.previousState = rootState;
58
this.buildTree = buildTree;
62
Parser (Parser copyFrom)
66
rootState = copyFrom.rootState;
67
currentState = copyFrom.currentState;
68
previousState = copyFrom.previousState;
70
position = copyFrom.position;
71
location = copyFrom.location;
72
stateTag = copyFrom.stateTag;
73
keywordBuilder = new StringBuilder (copyFrom.keywordBuilder.ToString ());
74
currentStateLength = copyFrom.currentStateLength;
76
//clone the node stack
77
List<XObject> l = new List<XObject> (CopyXObjects (copyFrom.nodes));
79
nodes = new NodeStack (l);
82
IEnumerable<XObject> CopyXObjects (IEnumerable<XObject> src)
84
foreach (XObject o in src)
85
yield return o.ShallowCopy ();
88
public RootState RootState { get { return rootState; } }
90
#region IDocumentStateEngine
92
public int Position { get { return position; } }
93
public DomLocation Location { get { return location; } }
97
currentState = rootState;
98
previousState = rootState;
103
keywordBuilder = new StringBuilder ();
104
currentStateLength = 0;
105
nodes = new NodeStack ();
106
nodes.Push (rootState.CreateDocument ());
109
errors = new List<Error> ();
114
public void Parse (System.IO.TextReader reader)
116
int i = reader.Read ();
124
public void Push (char c)
136
for (int loopLimit = 0; loopLimit < 10; loopLimit++) {
137
currentStateLength++;
138
string rollback = null;
139
State nextState = currentState.PushChar (c, this, ref rollback);
142
if (nextState == currentState || nextState == null)
145
// state changed; reset stuff
146
previousState = currentState;
147
currentState = nextState;
149
currentStateLength = 0;
150
if (keywordBuilder.Length < 50)
151
keywordBuilder.Length = 0;
153
keywordBuilder = new StringBuilder ();
156
// only loop if the same char should be run through the new state
157
if (rollback == null)
160
//"complex" rollbacks require actually skipping backwards.
161
//Note the previous state is invalid for this operation.
162
else if (rollback.Length > 0) {
163
position -= (rollback.Length + 1);
164
foreach (char rollChar in rollback)
169
throw new InvalidOperationException ("Too many state changes for char '" + c + "'. Current state is " + currentState.ToString () + ".");
170
} catch (Exception ex) {
171
//attach parser state to exceptions
172
throw new Exception (ToString (), ex);
176
object ICloneable.Clone ()
179
throw new InvalidOperationException ("Parser can only be cloned when in stack mode");
180
return new Parser (this);
185
public Parser GetTreeParser ()
188
throw new InvalidOperationException ("Parser can only be cloned when in stack mode");
189
Parser p = new Parser (this);
193
//reconnect the node tree
194
((IParseContext)p).ConnectAll ();
196
p.errors = new List<Error> ();
201
public override string ToString ()
203
StringBuilder builder = new StringBuilder ();
204
builder.AppendFormat ("[Parser Location={0} CurrentStateLength={1}", position, currentStateLength);
205
builder.AppendLine ();
207
builder.Append (' ', 2);
208
builder.AppendLine ("Stack=");
210
XObject rootOb = null;
211
foreach (XObject ob in nodes) {
213
builder.Append (' ', 4);
214
builder.Append (ob.ToString ());
215
builder.AppendLine ();
218
builder.Append (' ', 2);
219
builder.AppendLine ("States=");
220
State s = currentState;
222
builder.Append (' ', 4);
223
builder.Append (s.ToString ());
224
builder.AppendLine ();
228
if (buildTree && rootOb != null) {
229
builder.Append (' ', 2);
230
builder.AppendLine ("Tree=");
231
rootOb.BuildTreeString (builder, 3);
234
if (buildTree && errors.Count > 0) {
235
builder.Append (' ', 2);
236
builder.AppendLine ("Errors=");
237
foreach (Error err in errors) {
238
builder.Append (' ', 4);
239
builder.AppendFormat ("[{0}@{1}:{2}, {3}]\n", err.ErrorType, err.Region.Start.Line,
240
err.Region.Start.Column, err.Message);
244
builder.AppendLine ("]");
245
return builder.ToString ();
248
#region IParseContext
250
int IParseContext.StateTag {
251
get { return stateTag; }
252
set { stateTag = value; }
255
DomLocation IParseContext.LocationMinus (int colOffset)
257
int col = Location.Column - colOffset;
258
System.Diagnostics.Debug.Assert (col > 0);
259
return new DomLocation (Location.Line, col);
262
StringBuilder IParseContext.KeywordBuilder {
263
get { return keywordBuilder; }
266
State IParseContext.PreviousState {
267
get { return previousState; }
270
public int CurrentStateLength { get { return currentStateLength; } }
271
public NodeStack Nodes { get { return nodes; } }
273
public bool BuildTree { get { return buildTree; } }
275
void IParseContext.LogError (string message)
277
if (errors != null || ErrorLogged != null)
278
InternalLogError (new Error (ErrorType.Error, Location, message));
281
void IParseContext.LogWarning (string message)
283
if (errors != null || ErrorLogged != null)
284
InternalLogError (new Error (ErrorType.Warning, Location, message));
287
void IParseContext.LogError (string message, DomLocation location)
289
if (errors != null || ErrorLogged != null)
290
InternalLogError (new Error (ErrorType.Error, location, message));
293
void IParseContext.LogWarning (string message, DomLocation location)
295
if (errors != null || ErrorLogged != null)
296
InternalLogError (new Error (ErrorType.Warning, location, message));
299
void IParseContext.LogError (string message, DomRegion region)
301
if (errors != null || ErrorLogged != null)
302
InternalLogError (new Error (ErrorType.Error, region, message));
305
void IParseContext.LogWarning (string message, DomRegion region)
307
if (errors != null || ErrorLogged != null)
308
InternalLogError (new Error (ErrorType.Warning, region, message));
311
void InternalLogError (Error err)
315
if (ErrorLogged != null)
319
void IParseContext.ConnectAll ()
322
foreach (XObject o in Nodes) {
323
XContainer container = o as XContainer;
324
if (prev != null && container != null && prev.IsComplete)
325
container.AddChildNode (prev);
326
if (o.Parent != null)
332
void IParseContext.EndAll (bool pop)
335
foreach (XObject ob in Nodes) {
336
if (!ob.IsEnded && !(ob is XDocument)) {
344
for (; popCount > 0; popCount--)
350
public State CurrentState { get { return currentState; } }
352
public IList<Error> Errors { get { return errors; } }
354
public event Action<Error> ErrorLogged;
357
public interface IParseContext
359
int StateTag { get; set; }
360
StringBuilder KeywordBuilder { get; }
361
int CurrentStateLength { get; }
362
DomLocation Location { get; }
363
DomLocation LocationMinus (int colOffset);
364
State PreviousState { get; }
365
NodeStack Nodes { get; }
366
bool BuildTree { get; }
367
void LogError (string message);
368
void LogWarning (string message);
369
void LogError (string message, DomLocation location);
370
void LogWarning (string message, DomLocation location);
371
void LogError (string message, DomRegion region);
372
void LogWarning (string message, DomRegion region);
373
void EndAll (bool pop);
377
public class NodeStack : Stack<XObject>
379
public NodeStack (IEnumerable<XObject> collection) : base (collection) {}
380
public NodeStack () {}
382
public XObject Peek (int down)
385
foreach (XObject o in this) {
393
public XDocument GetRoot ()
396
foreach (XObject o in this)
398
return last as XDocument;