/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#region Using directives
#define USE_TRACING
using System;
using System.Xml;
using System.IO;
#endregion
//////////////////////////////////////////////////////////////////////
// <summary>Contains BaseFeedParser.</summary>
//////////////////////////////////////////////////////////////////////
namespace Google.GData.Client{
//////////////////////////////////////////////////////////////////////
/// <summary>Parsing event class...
/// </summary>
//////////////////////////////////////////////////////////////////////
public class FeedParserEventArgs : EventArgs
{
private bool discard;
private bool creatingEntry;
private AtomEntry feedEntry;
private AtomFeed feed;
private bool done;
//////////////////////////////////////////////////////////////////////
/// <summary>constructor for the feedParser events - this one is only used
/// to ask for a new entry</summary>
//////////////////////////////////////////////////////////////////////
public FeedParserEventArgs()
{
this.creatingEntry = true;
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>constructor for the feedParser events</summary>
/// <param name="feed">the feed to use </param>
/// <param name="entry">the feedentry to use </param>
//////////////////////////////////////////////////////////////////////
public FeedParserEventArgs(AtomFeed feed, AtomEntry entry)
{
this.feedEntry = entry;
this.feed = feed;
if (feed == null && entry == null)
{
this.done = true;
}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>the eventhandler can set this to discard the entry</summary>
//////////////////////////////////////////////////////////////////////
public bool DiscardEntry
{
get {return this.discard;}
set {this.discard = value;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>Read only accessor for done</summary>
//////////////////////////////////////////////////////////////////////
public bool DoneParsing
{
get {return this.done;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>Read only accessor for creating an entry</summary>
//////////////////////////////////////////////////////////////////////
public bool CreatingEntry
{
get {return this.creatingEntry;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>the newly created entry obect</summary>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
public AtomEntry Entry
{
get {return this.feedEntry;}
set {this.feedEntry = value;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>accessor method public Feed Feed</summary>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
public AtomFeed Feed
{
get {return this.feed;}
}
/////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>extension element event class
/// </summary>
//////////////////////////////////////////////////////////////////////
public class ExtensionElementEventArgs : EventArgs
{
private bool discard;
private AtomBase baseObject;
private XmlNode node;
//////////////////////////////////////////////////////////////////////
/// <summary>the eventhandler can set this to discard the entry</summary>
//////////////////////////////////////////////////////////////////////
public bool DiscardEntry
{
get {return this.discard;}
set {this.discard = value;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>accessor method public XmlNode ExtensionElement</summary>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
public XmlNode ExtensionElement
{
get {return this.node;}
set {this.node = value;}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>accessor method public AtomBase Base</summary>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
public AtomBase Base
{
get {return this.baseObject;}
set {this.baseObject = value;}
}
/////////////////////////////////////////////////////////////////////////////
}
/// <summary>Delegate declaration for the parsing eventhandler</summary>
public delegate void FeedParserEventHandler(object sender, FeedParserEventArgs e);
/// <summary>Delegate declaration for the extension eventhandler</summary>
public delegate void ExtensionElementEventHandler(object sender, ExtensionElementEventArgs e);
//////////////////////////////////////////////////////////////////////
/// <summary>AtomEntry object, representing an item in the RSS feed
/// </summary>
//////////////////////////////////////////////////////////////////////
public abstract class BaseFeedParser
{
/// <summary>eventhandler, when the parser created a new feed entry</summary>
public event FeedParserEventHandler NewAtomEntry;
/// <summary>eventhandler, when the parser finds an extension element</summary>
public event ExtensionElementEventHandler NewExtensionElement;
/// <summary>the XmlDoc that we use to hand nodes to, in case of extensions</summary>
private XmlDocument doc;
//////////////////////////////////////////////////////////////////////
/// <summary>Read only accessor for doc</summary>
//////////////////////////////////////////////////////////////////////
protected XmlDocument Document
{
get
{
if (this.doc == null)
{
this.doc = new XmlDocument();
}
return this.doc;
}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>virtual, starts the parsing process</summary>
/// <param name="streamInput">input stream to parse </param>
/// <param name="feed">the basefeed object that should be set</param>
//////////////////////////////////////////////////////////////////////
public abstract void Parse(Stream streamInput, AtomFeed feed);
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>overloaded to make it easier to fire the event</summary>
//////////////////////////////////////////////////////////////////////
protected AtomEntry OnCreateNewEntry()
{
FeedParserEventArgs args = new FeedParserEventArgs();
this.OnNewAtomEntry(args);
if (args.Entry == null)
{
return new AtomEntry();
}
return args.Entry;
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>overloaded to make it easier to fire the event</summary>
/// <param name="newEntry">the new AtomEntry to fire </param>
//////////////////////////////////////////////////////////////////////
protected void OnNewAtomEntry(AtomEntry newEntry)
{
FeedParserEventArgs args = new FeedParserEventArgs(null, newEntry);
this.OnNewAtomEntry(args);
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>protected void OnParsingDone()</summary>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
protected void OnParsingDone()
{
FeedParserEventArgs args = new FeedParserEventArgs(null, null);
this.OnNewAtomEntry(args);
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>overloaded to make eventfiring easier</summary>
/// <param name="feed"> the new feed to fire</param>
//////////////////////////////////////////////////////////////////////
protected void OnNewAtomEntry(AtomFeed feed)
{
FeedParserEventArgs args = new FeedParserEventArgs(feed, null);
this.OnNewAtomEntry(args);
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>eventfiring helper for new extensions</summary>
/// <param name="node"> the new node that was found</param>
/// <param name="baseObject"> the object this node should be added to</param>
//////////////////////////////////////////////////////////////////////
protected void OnNewExtensionElement(XmlNode node, AtomBase baseObject)
{
ExtensionElementEventArgs args = new ExtensionElementEventArgs();
args.ExtensionElement = node;
args.Base = baseObject;
if (this.NewExtensionElement != null)
{
this.NewExtensionElement(this, args);
}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>eventfiring helper for new extensions</summary>
/// <param name="reader"> the reader positioned on the extension</param>
/// <param name="baseObject"> the object this node should be added to</param>
//////////////////////////////////////////////////////////////////////
protected void OnNewExtensionElement(XmlReader reader, AtomBase baseObject)
{
Tracing.Assert(reader != null, "reader should not be null");
if (reader == null)
{
throw new ArgumentNullException("reader");
}
Tracing.Assert(baseObject != null, "baseObject should not be null");
if (baseObject == null)
{
throw new ArgumentNullException("baseObject");
}
if (reader.NodeType == XmlNodeType.Element || reader.NodeType == XmlNodeType.Attribute)
{
XmlNode node = this.Document.ReadNode(reader);
if (node != null)
{
OnNewExtensionElement(node, baseObject);
}
}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>protected virtual OnNewAtomEntry( FeedParserEventArgs args)</summary>
/// <param name="args"> FeedParserEventArgs, includes the new entry</param>
/// <returns> </returns>
//////////////////////////////////////////////////////////////////////
protected virtual void OnNewAtomEntry(FeedParserEventArgs args)
{
if (this.NewAtomEntry != null)
{
this.NewAtomEntry(this,args);
}
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>moves to the next element</summary>
/// <param name="reader">the xmlreader to skip </param>
//////////////////////////////////////////////////////////////////////
static protected bool NextElement(XmlReader reader)
{
Tracing.Assert(reader != null, "reader should not be null");
if (reader == null)
{
throw new ArgumentNullException("reader");
}
while (reader.Read() && reader.NodeType != XmlNodeType.Element);
return !reader.EOF;
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>starts with the parent element, and stops when it reaches the same DEPTH again</summary>
/// <param name="reader"> the xml reader positioned at the parent element</param>
/// <param name="depth"> indicates the depth level of the parent where to stop</param>
/// <returns> true while still inside this level</returns>
//////////////////////////////////////////////////////////////////////
static protected bool NextChildElement(XmlReader reader, ref int depth)
{
Tracing.Assert(reader != null, "reader should not be null");
if (reader == null)
{
throw new ArgumentNullException("reader");
}
if (reader.Depth == depth)
{
// assume we gone around circle, a child read and moved to the next element of the same KIND
return false;
}
if (reader.NodeType == XmlNodeType.Element && depth >= 0 && reader.Depth > depth)
{
// assume we gone around circle, a child read and moved to the next element of the same KIND
// but now we are in the parent/containing element, hence we return TRUE without reading further
return true;
}
if (depth == -1)
{
depth = reader.Depth;
}
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == depth)
{
return false;
}
else if (reader.NodeType == XmlNodeType.Element && reader.Depth > depth)
{
return true;
}
else if (reader.NodeType == XmlNodeType.Element && reader.Depth == depth)
{
// assume that we had no children. We read once and we are at the
// next element, same level as the previous one.
return false;
}
}
return !reader.EOF;
}
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/// <summary>moves to the next element start</summary>
/// <param name="reader"> the xml reader positioned somewhere</param>
/// <returns> true if found, otherwise false (indicating most likely EOF</returns>
//////////////////////////////////////////////////////////////////////
static protected bool MoveToStartElement(XmlReader reader)
{
Tracing.Assert(reader != null, "reader should not be null");
if (reader == null)
{
throw new ArgumentNullException("reader");
}
if (reader.NodeType == XmlNodeType.Attribute)
{
reader.MoveToElement();
}
else
{
while (reader.NodeType != XmlNodeType.Element && reader.Read() && reader.EOF != true);
}
return !reader.EOF;
}
/////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
|