mike chambers | about

Atom Feed ActionScript 2 Class Alpha

Thursday, February 5, 2004

I am putting together an ActionScript 2 class to represent and parse Atom feeds. I have put together an early alpha version of the class, but wanted to get some feedback on it before I continue work on it.

Alpha .22: (Feb.12 2004)

Alpha .20:

Alpha .10:

Before I finish up adding support for all of Atom, I wanted to get some input on the class, and how I make the Atom dom avaliable. Please post any suggestions in the comments.

One quick comment (and my personal opinion), thus far, Atom has been a pure joy to work with. It is so nice have a structured format to work with. I love what RSS enables, but with all of the different versions, and permutations, it is a huge hassle to work with.

Anyways, here is the class (v .20):

/*
	Atom.as
	
	Mike Chambers
	mesh@macromedia.com
	
	Class that represents an Atom XML document.
	
	You can find more information on Atom at:

http://www.atomenabled.org

	Version Alpha .22	
		-user cannot yet add nodes
		-check case sensitivity in switch statements
		-check if namespace is added tags : atom:element
		-optimize parse functions	
*/

import mx.events.EventDispatcher;
import com.macromedia.date.W3CDateTime;

class com.macromedia.data.Atom extends XML
{
	private var dispatchEvent:Function;
	public var addEventListener:Function;
	public var removeEventListener:Function;
	
	private var feed:Object;
	private var entriesArray:Array;
	
	private var ignoreWhite:Boolean = true;
	
	public function Atom(xmlSource:String)
	{	
		super(xmlSource);
		
		mx.events.EventDispatcher.initialize(this);
	}
	
	private function onLoad(success:Boolean):Void
	{
		if(!success)
		{
			//throw error
			trace("xml did not load");
			return;
		}
		parseFeedTags();
		
		dispatchEvent({type:"onAtomLoad", target:this});
	}	
	
	//need a way to reparse if user manually adds nodes
	private function parseFeedTags(Void):Void
	{	
		feed = parseFeedElement();
		
		//should this be in info, or in feed		
		//feed.info = parseFeedElement();
			
		var nodes:Array = firstChild.childNodes;
		
		var len:Number = nodes.length;
		
		var node:XMLNode;
		for(var i:Number = 0; i< len; i++)
		{
			node = nodes[i];

			//do we need to worry about case sensitivity?
			switch(node.nodeName)
			{
				case "entry":
				{
					break;
				}
				case "title":
				{
					feed.title = parseFeedTitle(node);
					break;
				}
				case "author":
				{
					feed.author = parseFeedAuthor(node);
					break;
				}	
				case "contributor":
				{
					feed.contributor = parseFeedContributor(node);
					break;
				}					
				case "tagline":
				{
					feed.tagline = parseFeedTagline(node);
					break;
				}		
				case "copyright":
				{
					feed.copyright = parseFeedCopyright(node);
					break;
				}	
				case "generator":
				{
					
					feed.generator = parseFeedGenerator(node);

					break;
				}	
				case "info":
				{
					feed.info = parseFeedInfo(node);
					break;
				}				
				case "id":
				{
					feed.id = parseFeedId(node);
					break;
				}	
				case "link":
				{					
					feed.link = parseFeedLink(node);
					 
					break;
				}					
				case "modified":
				{
					feed.modified = parseFeedModified(node);
					break;
				}	
				default:
				{
					parseFeedDefault(node);
					break;
				}
			}	
		}
	}
	
	private function parseFeedTitle(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
			
		return obj;
	}

	private function parseFeedAuthor(node:XMLNode):Object
	{
		var obj:Object = parsePersonConstruct(node);
		
		return obj;
	}
	
	private function parseFeedContributor(node:XMLNode):Object
	{
		var obj:Object = parsePersonConstruct(node);
		
		return obj;
	}
	
	private function parseFeedTagline(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
		
		return obj;
	}

	private function parseFeedCopyright(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
			
		return;		
	}

	private function parseFeedGenerator(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
					
			var genAtts:Object = node.attributes;
					
			obj.url = genAtts.url;
			obj.version = genAtts.version;
			
		return obj;
	}

	private function parseFeedInfo(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
			
			return obj;
	}

	private function parseFeedId(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
			
		return;
	}

	private function parseFeedLink(node:XMLNode):Object
	{
		var obj:Object = parseLinkConstruct(node);
		
		return obj;
	}

	private function parseFeedModified(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = W3CDateTime.parseString(node.firstChild.nodeValue);
			
		return obj;
	}

	private function parseFeedDefault(node:XMLNode):Object
	{
		return undefined;
	}	
	
	private function parseLinkConstruct(node:XMLNode):Object
	{
		var feedAtts:Object = node.attributes;
		var link:Object = new Object();
			link.attributes = new Object();
					
		link.attributes.rel = feedAtts.rel;
		link.attributes.type = feedAtts.type;
		link.attributes.href = feedAtts.href;
		link.attributes.title = feedAtts.title;

		return link;		
	}
	
	private function parseFeedElement(Void):Object
	{
		var feedInfo:Object = new Object();
			feedInfo.attributes = new Object();
			
		var feedAttributes:Object = firstChild.attributes;
					
		feedInfo.attributes.version = feedAttributes.version;
		feedInfo.attributes["xml:lang"] = feedAttributes["xml:lang"];
		
		return feedInfo;
	}	
	
	//need a way to reparse if user manually adds nodes
	private function parseEntries(Void):Void
	{				
		entriesArray = new Array();
				
		var nodes:Array = firstChild.childNodes;
		
		var len:Number = nodes.length;
		
		var node:XMLNode;
		var tempEntry:Object;
		
		for(var i:Number = 0; i< len; i++)
		{
			node = nodes[i];
			
			if(node.nodeName == "entry")
			{
				tempEntry = parseEntry(node);
				entriesArray.push(tempEntry);
			}
		}
	}	
	
	private function parseEntry(node:XMLNode):Object
	{
		if(node == undefined)
		{
			return;
		}
	
		var entryElements:Array = node.childNodes;
		var elementLen:Number = entryElements.length;
	
		var entry:Object = new Object();
		var entryElement:XMLNode;
		
		for(var k:Number = 0; k < elementLen; k++)
		{
			entryElement = entryElements[k];		

			switch(entryElement.nodeName)
			{
				case "title":
				{		
					entry.title = parseEntryTitle(entryElement);
					break;
				}
				case "link":
				{
					entry.link = parseEntryLink(entryElement);
					break;
				}
				case "modified":
				{
					entry.modified = parseEntryModified(entryElement);						
					break;
				}
				case "issued":
				{
					entry.issued = parseEntryIssued(entryElement);							
					break;
				}
				case "id":
				{
					entry.id = parseEntryId(entryElement);						
					break;
				}	
				case "created":
				{
					entry.created = parseEntryCreated(entryElement);						
					break;
				}
				case "summary":
				{
					entry.summary = parseEntrySummary(entryElement);				
					break;
				}
				case "author":
				{
					entry.author = parseEntryAuthor(entryElement);
					break;
				}
				case "contributor":
				{
					entry.contributor = parseEntryContributor(entryElement);
					break;
				}						
				case "content":
				{
		
					entry.content = parseEntryContent(entryElement);
					break;
				}	
				default:
				{
					//pass this to a parse default function
					break;
				}				
			}
		}	
		return entry;
	}

	
	private function parseEntryTitle(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;

		return obj;
	}
	
	private function parseEntryLink(node:XMLNode):Object
	{
		var obj:Object = parseLinkConstruct(node);
		return obj;
	}

	private function parseEntryModified(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = W3CDateTime.parseString(node.firstChild.nodeValue);
		
		return obj;
	}

	private function parseEntryIssued(node:XMLNode):Object
	{	
		var obj:Object = new Object();
			obj.value = W3CDateTime.parseString(node.firstChild.nodeValue);
		
		return obj;		
	}
	
	private function parseEntryId(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = node.firstChild.nodeValue;
		
		return obj;			
	}

	private function parseEntryCreated(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.value = W3CDateTime.parseString(node.firstChild.nodeValue);
		
		return obj;			
	}

	private function parseEntrySummary(node:XMLNode):Object
	{
		var obj:Object = new Object();		
		
		obj.value = node.firstChild.nodeValue;
		obj.type = node.attributes.type;	
		
		var childNodes:Array = node.childNodes;
		var childNodesLen:Number = childNodes.length;
		
		var childNode:XMLNode;
		//TODO: test this with a summary tag											
		//find out if there is a created node
		for(var z:Number = 0; z < childNodesLen; z++)
		{
			childNode = childNodes[z];

			if(childNode.nodeName == "created")
			{
				obj.created = new Object();
				obj.created.value = 
						W3CDateTime.parseString(childNode.firstChild.nodeValue);	
				
				break;
			}								
		}	
		
		return obj;
	}
	

	private function parseEntryAuthor(node:XMLNode):Object
	{
		var obj:Object = parsePersonConstruct(node);
		return obj;
	}
	

	private function parseEntryContributor(node:XMLNode):Object
	{
		var obj:Object = parsePersonConstruct(node);
		return obj;
	}

	private function parseEntryContent(node:XMLNode):Object
	{
		var obj:Object = new Object();
			obj.attributes = new Object();
		
		var atts:Object = node.attributes;
					
		obj.attributes.mode = atts.mode;
		obj.attributes.type = atts.type;
		obj.value = node.firstChild.nodeValue;
		
		return obj;
	}		
	
	private function parsePersonConstruct(node:XMLNode):Object
	{	
		var author:Object = new Object();
		
		var children:Array = node.childNodes;
		var len:Number = children.length;

		var tempNode:Object;
		var tempName:String;
		
		for(var i:Number = 0; i < len; i++)
		{
			tempNode = children[i];
			tempName = tempNode.nodeName;
			
			switch(tempName)
			{
				case "name":
				{
					//fall through
				}
				case "url":
				{
					//fall through
				}
				case "email":
				{
					author[tempName] = new Object();
					author[tempName].value = tempNode.firstChild.nodeValue;
					break;
				}
			}
			
		}
		
		return author;
	}
	
	/**************** Public API / Getters *******************/
	
//TODO: make sure we have getters for all of the top level feed elements
	
	public function getFeedTitle(Void):String
	{
		return feed.title.value;
	}
	
	public function getFeedModified(Void):Date
	{
		return feed.modified.value;
	}	
	
	public function getFeedLink(Void):Object
	{
		return feed.link;
	}
	
	public function getFeedId(Void):String
	{
		return feed.id.value;
	}
	
	public function getFeedGenerator(Void):String
	{
		return feed.generator.value;
	}
	
	public function getFeedCopyright(Void):String
	{
		return feed.copyright.value;
	}
	
	public function getFeedTagline(Void):String
	{	
		return feed.tagline.value;
	}	
	
	//TODO : should this be a property?
	public function getFeed(Void):Object
	{
		return feed;
	}
	
	public function get entries ():Array
	{
		if(entriesArray == undefined)
		{
			parseEntries();
		}
		
		return entriesArray;
	}
	
}
twitter github flickr behance