<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://microformats.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=RobertSayre</id>
	<title>Microformats Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://microformats.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=RobertSayre"/>
	<link rel="alternate" type="text/html" href="http://microformats.org/wiki/Special:Contributions/RobertSayre"/>
	<updated>2026-04-15T08:32:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.38.4</generator>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=4471</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=4471"/>
		<updated>2005-10-30T20:00:36Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOParser.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
This needs some small additions to handle the XHTML DTD and named character entities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2506</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2506"/>
		<updated>2005-10-30T19:33:39Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOParser.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
This needs some small additions to handle the XHTML DTD and entities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2505</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2505"/>
		<updated>2005-10-30T19:32:54Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOTest.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2504</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2504"/>
		<updated>2005-10-30T19:32:31Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOParser.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2503</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2503"/>
		<updated>2005-10-30T19:32:19Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOParser.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2502</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2502"/>
		<updated>2005-10-30T19:32:00Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: /* XOXOWriter.java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright 2005 Robert Sayre&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;br /&gt;
 * you may not use this file except in compliance with the License.&lt;br /&gt;
 * You may obtain a copy of the License at&lt;br /&gt;
 * &lt;br /&gt;
 *      http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;
 * &lt;br /&gt;
 * Unless required by applicable law or agreed to in writing, software&lt;br /&gt;
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;br /&gt;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;
 * See the License for the specific language governing permissions and&lt;br /&gt;
 * limitations under the License.&lt;br /&gt;
 *&lt;br /&gt;
 * Portions of this code are derived from the Apache-licensed Python XOXO&lt;br /&gt;
 * module by Kevin Marks. &amp;lt;http://microformats.org/wiki/xoxo-sample-code&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2501</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2501"/>
		<updated>2005-10-30T19:28:56Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python and Java (with Perl, PHP, ... to follow).&lt;br /&gt;
= Python =&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2500</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2500"/>
		<updated>2005-10-30T19:28:27Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python (with Perl, PHP, ... to follow).&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Java =&lt;br /&gt;
== XOXOWriter.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOParser.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import org.xml.sax.InputSource;&lt;br /&gt;
import org.xml.sax.SAXException;&lt;br /&gt;
import org.xml.sax.XMLReader;&lt;br /&gt;
import org.xml.sax.Attributes;&lt;br /&gt;
import org.xml.sax.helpers.XMLReaderFactory;&lt;br /&gt;
import org.xml.sax.helpers.DefaultHandler;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
import java.io.InputStream;&lt;br /&gt;
import java.io.StringReader;&lt;br /&gt;
import java.io.IOException;&lt;br /&gt;
&lt;br /&gt;
public class XOXOParser extends DefaultHandler {&lt;br /&gt;
&lt;br /&gt;
  protected String XHTML_NS = &amp;quot;http://www.w3.org/1999/xhtml&amp;quot;;&lt;br /&gt;
  protected List elStack;&lt;br /&gt;
  protected Map listEls;&lt;br /&gt;
  public List structs;&lt;br /&gt;
  public List xoStack;&lt;br /&gt;
  public List textStack;&lt;br /&gt;
&lt;br /&gt;
  public XOXOParser() {&lt;br /&gt;
    reset();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void pushStruct(Object struct){&lt;br /&gt;
    if((struct instanceof Map) &amp;amp;&amp;amp; (((Map) struct).size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (structs.get(structs.size()-1) instanceof Map)&lt;br /&gt;
        &amp;amp;&amp;amp; (((Map) struct).containsKey(&amp;quot;url&amp;quot;))){&lt;br /&gt;
      // put back the &amp;lt;a&amp;gt;-made one for extra defs&lt;br /&gt;
      xoStack.add(structs.get(structs.size()-1));&lt;br /&gt;
    }else{&lt;br /&gt;
      structs.add(struct);&lt;br /&gt;
      xoStack.add(struct);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void startElement(String nsUri, String localName,&lt;br /&gt;
                           String qName, Attributes atts){&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri.equals(XHTML_NS)){&lt;br /&gt;
      elStack.add(localName);&lt;br /&gt;
    }else{&lt;br /&gt;
      elStack.add(&amp;quot;foo&amp;quot;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      Map attmap = new LinkedHashMap();&lt;br /&gt;
      int len = atts.getLength();&lt;br /&gt;
      for(int i=0; i&amp;lt;len; i++){&lt;br /&gt;
        attmap.put(atts.getQName(i),atts.getValue(i));&lt;br /&gt;
      }&lt;br /&gt;
      if(attmap.containsKey(&amp;quot;href&amp;quot;)){&lt;br /&gt;
        attmap.put(&amp;quot;url&amp;quot;,attmap.get(&amp;quot;href&amp;quot;));&lt;br /&gt;
        attmap.remove(&amp;quot;href&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      pushStruct(attmap);&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      pushStruct(new LinkedHashMap());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      pushStruct(new ArrayList());&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dt&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      textStack.add(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void endElement(String nsUri, String localName,&lt;br /&gt;
                         String qName){&lt;br /&gt;
    elStack.remove(elStack.size()-1);&lt;br /&gt;
    // bounce non-XHTML elements&lt;br /&gt;
    if(nsUri != XHTML_NS){&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if(localName.equals(&amp;quot;a&amp;quot;)){&lt;br /&gt;
      String val = (String) textStack.remove(textStack.size()-1);&lt;br /&gt;
      if (val.length() &amp;gt; 0){&lt;br /&gt;
        Map defs = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
        String defVal = (String) defs.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        defVal = (String) defs.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        if((defVal != null) &amp;amp;&amp;amp; (val.equals(defVal))){&lt;br /&gt;
          val = &amp;quot;&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        if(val.length() &amp;gt; 0){&lt;br /&gt;
          defs.put(&amp;quot;text&amp;quot;,val);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dl&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ol&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;ul&amp;quot;)){&lt;br /&gt;
      xoStack.remove(xoStack.size()-1);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;li&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      List last = (List) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.add(val);&lt;br /&gt;
    }else if(localName.equals(&amp;quot;dd&amp;quot;)){&lt;br /&gt;
      Object val = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Object key = textStack.remove(textStack.size()-1);&lt;br /&gt;
      Map last = (Map) xoStack.get(xoStack.size()-1);&lt;br /&gt;
      if(structs.get(structs.size()-1) != last){&lt;br /&gt;
        val = structs.remove(structs.size()-1);&lt;br /&gt;
      }&lt;br /&gt;
      last.put(key,val);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void characters(char[] ch, int start, int length){&lt;br /&gt;
    if((xoStack.size() &amp;gt; 0)&lt;br /&gt;
        &amp;amp;&amp;amp; (!listEls.containsKey(elStack.get(elStack.size()-1)))){&lt;br /&gt;
      String text = (String) textStack.get(textStack.size()-1);&lt;br /&gt;
      String test = new String(ch,start,length);&lt;br /&gt;
      textStack.set(textStack.size()-1,text+test);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(String s) throws SAXException, IOException{&lt;br /&gt;
    return parse(new InputSource(new StringReader(s)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputStream is) throws SAXException, IOException {&lt;br /&gt;
    return parse(new InputSource(is));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public Object parse(InputSource in) throws SAXException, IOException {&lt;br /&gt;
    XMLReader parser = XMLReaderFactory.createXMLReader();&lt;br /&gt;
    parser.setContentHandler(this);&lt;br /&gt;
    parser.parse(in);&lt;br /&gt;
    List returnList = new ArrayList();&lt;br /&gt;
    for(Iterator i = this.structs.iterator(); i.hasNext();){&lt;br /&gt;
      Object thing = i.next();&lt;br /&gt;
      if(thing != null){&lt;br /&gt;
        returnList.add(thing);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    while((returnList.size()==1)){&lt;br /&gt;
      if(returnList.get(0) instanceof List){&lt;br /&gt;
        returnList = (List) returnList.get(0);&lt;br /&gt;
      }else{&lt;br /&gt;
        reset();&lt;br /&gt;
        return returnList.get(0);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    reset();&lt;br /&gt;
    return returnList;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  protected void reset(){&lt;br /&gt;
    elStack = new ArrayList();&lt;br /&gt;
    listEls = new HashMap();&lt;br /&gt;
    structs = new ArrayList();&lt;br /&gt;
    xoStack = new ArrayList();&lt;br /&gt;
    textStack = new ArrayList();&lt;br /&gt;
    listEls.put(&amp;quot;ol&amp;quot;,&amp;quot;ol&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;ul&amp;quot;,&amp;quot;ul&amp;quot;);&lt;br /&gt;
    listEls.put(&amp;quot;dl&amp;quot;,&amp;quot;dl&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== XOXOTest.java ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2499</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2499"/>
		<updated>2005-10-30T19:26:06Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python (with Perl, PHP, ... to follow).&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOWriter {&lt;br /&gt;
&lt;br /&gt;
  public String[] attrs = {&amp;quot;title&amp;quot;,&amp;quot;rel&amp;quot;,&amp;quot;type&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className){&lt;br /&gt;
    return makeXOXO(struct, className, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct, String className,&lt;br /&gt;
                         boolean doNSDeclaration){&lt;br /&gt;
    return makeXOXO(struct, className, 0, doNSDeclaration);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(List struct){&lt;br /&gt;
    return makeXOXO(struct, &amp;quot;xoxo&amp;quot;, 0, true);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, int depth){&lt;br /&gt;
    return makeXOXO(struct, null, 0, false);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String makeXOXO(Object struct, String className,&lt;br /&gt;
                         int depth, boolean doNSDeclaration){&lt;br /&gt;
    if(struct == null) return &amp;quot;&amp;quot;;&lt;br /&gt;
    StringBuffer sb = new StringBuffer();&lt;br /&gt;
    if(struct instanceof Object[]){&lt;br /&gt;
      struct = Arrays.asList((Object[]) struct);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof List){&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;ol&amp;quot;);&lt;br /&gt;
      if(doNSDeclaration)&lt;br /&gt;
        sb.append(&amp;quot; xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;quot;);&lt;br /&gt;
      if(className != null){&lt;br /&gt;
        sb.append(&amp;quot; class=\&amp;quot;&amp;quot;);&lt;br /&gt;
        sb.append(className);&lt;br /&gt;
        sb.append(&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if(struct instanceof Map){&lt;br /&gt;
      Map d = new LinkedHashMap((Map) struct);&lt;br /&gt;
      if(d.containsKey(&amp;quot;url&amp;quot;)){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;a href=\&amp;quot;&amp;quot; + d.get(&amp;quot;url&amp;quot;) + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
        Object text;&lt;br /&gt;
        if(d.containsKey(&amp;quot;text&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;text&amp;quot;);&lt;br /&gt;
        }else if(d.containsKey(&amp;quot;title&amp;quot;)){&lt;br /&gt;
          text = d.get(&amp;quot;title&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
          text = d.get(&amp;quot;url&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        for(int i=0; i&amp;lt;attrs.length; i++){&lt;br /&gt;
          String xVal = makeXOXO(d.get(attrs[i]),depth+1);&lt;br /&gt;
          if(xVal != null &amp;amp;&amp;amp; !xVal.equals(&amp;quot;&amp;quot;)){&lt;br /&gt;
            sb.append(attrs[i] + &amp;quot;=\&amp;quot;&amp;quot; + xVal + &amp;quot;\&amp;quot; &amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
          d.remove(attrs[i]);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;gt;&amp;quot; + makeXOXO(text, depth+1) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;text&amp;quot;);&lt;br /&gt;
        d.remove(&amp;quot;url&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      if(d.size() &amp;gt; 0){&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;);&lt;br /&gt;
        for(Iterator i = d.keySet().iterator(); i.hasNext();){&lt;br /&gt;
          Object k = i.next();&lt;br /&gt;
          String ddVal = makeXOXO(d.get(k),depth+1);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dt&amp;gt;&amp;quot; + k + &amp;quot;&amp;lt;/dt&amp;gt;&amp;quot;);&lt;br /&gt;
          sb.append(&amp;quot;&amp;lt;dd&amp;gt;&amp;quot; + ddVal + &amp;quot;&amp;lt;/dd&amp;gt;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
    }else if(struct instanceof List){&lt;br /&gt;
      List l = (List) struct;&lt;br /&gt;
      for(Iterator i = l.iterator(); i.hasNext();){&lt;br /&gt;
        Object item = i.next();&lt;br /&gt;
        sb.append(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,depth+1) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
      sb.append(&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
      sb.append(struct);&lt;br /&gt;
    }&lt;br /&gt;
    return sb.toString();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct){&lt;br /&gt;
    return toXOXO(struct, false, null);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(Object struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    List alist = new ArrayList();&lt;br /&gt;
    alist.add(struct);&lt;br /&gt;
    return toXOXO(alist, addHTMLWrapper, cssUrl);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String toXOXO(List struct,&lt;br /&gt;
                       boolean addHTMLWrapper,&lt;br /&gt;
                       String cssUrl){&lt;br /&gt;
    String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;quot;;&lt;br /&gt;
    if(addHTMLWrapper){&lt;br /&gt;
      String s = startHTML;&lt;br /&gt;
      if(cssUrl != null){&lt;br /&gt;
        s += &amp;quot;&amp;lt;style type=\&amp;quot;text/css\&amp;quot;&amp;gt;@import \&amp;quot;&amp;quot;&lt;br /&gt;
            + cssUrl + &amp;quot;\&amp;quot;;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      s += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot; + makeXOXO(struct, &amp;quot;xoxo&amp;quot;, false)&lt;br /&gt;
          + &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
      return s;&lt;br /&gt;
    }else{&lt;br /&gt;
      return makeXOXO(struct, &amp;quot;xoxo&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2498</id>
		<title>xoxo-sample-code</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=xoxo-sample-code&amp;diff=2498"/>
		<updated>2005-10-30T19:25:07Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= XOXO Sample Code =&lt;br /&gt;
&lt;br /&gt;
A whole bunch of open source ([http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0]) sample code to read and write [[xoxo]] files in Python (with Perl, PHP, ... to follow).&lt;br /&gt;
== xoxo.py ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;xoxo.py - a utility module for transforming to and from the XHTMLOutlines format XOXO&lt;br /&gt;
toXOXO takes a Python datastructure (tuples, lists or dictionaries, arbitrarily nested) and returns a XOXO representation of it.&lt;br /&gt;
fromXOXO parses an XHTML file for a xoxo list and returns the structure&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__version__ = &amp;quot;0.8&amp;quot;&lt;br /&gt;
__date__ = &amp;quot;2004-10-05&amp;quot;&lt;br /&gt;
__author__ = &amp;quot;Kevin Marks &amp;lt;kmarks@technorati.com&amp;gt;&amp;quot;&lt;br /&gt;
__copyright__ = &amp;quot;Copyright 2004, Kevin marks &amp;amp; Technorati&amp;quot;&lt;br /&gt;
__license__ = &amp;quot;http://creativecommons.org/licenses/by/2.0/ CC-by-2.0], [http://www.apache.org/licenses/LICENSE-2.0 Apache 2.0&amp;quot;&lt;br /&gt;
__credits__ = &amp;quot;&amp;quot;&amp;quot;Tantek Çelik and Mark Pilgrim for data structure&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
__history__ = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
TODO: add &amp;lt;title&amp;gt; tag&lt;br /&gt;
TODO: add a proper profile link&lt;br /&gt;
0.8 work in unicode then render to utf-8&lt;br /&gt;
0.7 initial encoding support - just utf-8 for now&lt;br /&gt;
0.6 support the special behaviour for url properties  to/from &amp;lt;a&amp;gt;&lt;br /&gt;
0.5 fix some awkward side effects of whitespace and text outside our expected tags; simplify writing code&lt;br /&gt;
0.4 add correct XHTML headers so it validates&lt;br /&gt;
0.3 read/write version; fixed invlaid nested list generation;&lt;br /&gt;
0.1 first write-only version&lt;br /&gt;
 &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    True, False&lt;br /&gt;
except NameError:&lt;br /&gt;
    True, False = not not 1, not 1&lt;br /&gt;
containerTags={'ol':False,'ul':False,'dl':False}&lt;br /&gt;
import sgmllib, urllib, urlparse, re&lt;br /&gt;
def makeXOXO(struct,className=None,depth=0):&lt;br /&gt;
    s=u''&lt;br /&gt;
    if isinstance(struct,list) or isinstance(struct,tuple):&lt;br /&gt;
        if className:&lt;br /&gt;
            s += u'&amp;lt;ol class=&amp;quot;%s&amp;quot;&amp;gt;' % className&lt;br /&gt;
        else:&lt;br /&gt;
            s+= u&amp;quot;&amp;lt;ol&amp;gt;&amp;quot;&lt;br /&gt;
    if isinstance(struct,dict):&lt;br /&gt;
        d=struct.copy()&lt;br /&gt;
        if d.has_key('url'):&lt;br /&gt;
            s+=u'&amp;lt;a href=&amp;quot;%s&amp;quot; ' % d['url']&lt;br /&gt;
            text =  d.get('text',d.get('title',d['url']))&lt;br /&gt;
            for attr in ('title','rel','type'):&lt;br /&gt;
                if d.has_key(attr):&lt;br /&gt;
                    xVal = makeXOXO(d[attr],None,depth+1)&lt;br /&gt;
                    s +=u'%s=&amp;quot;%s&amp;quot; ' % (attr,xVal)&lt;br /&gt;
                    del d[attr]&lt;br /&gt;
            s +=u'&amp;gt;%s&amp;lt;/a&amp;gt;' % makeXOXO(text,None,depth+1)&lt;br /&gt;
            if d.has_key('text'):&lt;br /&gt;
                del d['text']&lt;br /&gt;
            del d['url']&lt;br /&gt;
        if len(d):&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;dl&amp;gt;&amp;quot;&lt;br /&gt;
            for key,value in d.items():&lt;br /&gt;
                xVal = makeXOXO(value,None,depth+1)&lt;br /&gt;
                s+= u'&amp;lt;dt&amp;gt;%s&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;%s&amp;lt;/dd&amp;gt;' % (key, xVal)&lt;br /&gt;
            s +=u&amp;quot;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        for item in struct:&lt;br /&gt;
            s+=u&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + makeXOXO(item,None,depth+1)+&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;br /&gt;
        s +=u&amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;&lt;br /&gt;
    elif type(struct) == type(u'unicode'):&lt;br /&gt;
        s+=struct&lt;br /&gt;
    else:&lt;br /&gt;
        if not type(struct)==type(' '):&lt;br /&gt;
            struct=str(struct)&lt;br /&gt;
        try:&lt;br /&gt;
            s+=unicode(struct,'utf-8')&lt;br /&gt;
        except:&lt;br /&gt;
            s+=unicode(struct,'windows_1252')&lt;br /&gt;
    return s&lt;br /&gt;
class xoxoParser(sgmllib.SGMLParser):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        sgmllib.SGMLParser.__init__(self)&lt;br /&gt;
        self.structs=[]&lt;br /&gt;
        self.xostack=[]&lt;br /&gt;
        self.textstack=['']&lt;br /&gt;
    def normalize_attrs(self, attrs):&lt;br /&gt;
        attrs = [(k.lower(), sgmllib.charref.sub(lambda m: chr(int(m.groups()[0])), v).strip()) for k, v in attrs]&lt;br /&gt;
        attrs = [(k, k in ('rel','type') and v.lower() or v) for k, v in attrs]&lt;br /&gt;
        return attrs&lt;br /&gt;
    def pushStruct(self,struct):&lt;br /&gt;
        if type(struct) == type({}) and len(struct)==0 and len(self.structs) and type(self.structs[-1]) == type({}) and self.structs[-1].has_key('url'):&lt;br /&gt;
            self.xostack.append(self.structs[-1]) # put back the &amp;lt;a&amp;gt;-made one for extra def's&lt;br /&gt;
        else:&lt;br /&gt;
            self.structs.append(struct)&lt;br /&gt;
            self.xostack.append(self.structs[-1])&lt;br /&gt;
    def start_a(self,attrs):&lt;br /&gt;
        attrsD = dict(self.normalize_attrs(attrs))&lt;br /&gt;
        attrsD['url']= attrsD.get('href','')&lt;br /&gt;
        del attrsD['href']&lt;br /&gt;
        self.pushStruct(attrsD)&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_a(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if val: &lt;br /&gt;
            if self.xostack[-1].get('title','') == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if self.xostack[-1]['url'] == val:&lt;br /&gt;
                val=''&lt;br /&gt;
            if val:&lt;br /&gt;
                self.xostack[-1]['text']=val&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_dl(self,attrs):&lt;br /&gt;
        self.pushStruct({})&lt;br /&gt;
    def end_dl(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ol(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ol(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_ul(self,attrs):&lt;br /&gt;
        self.pushStruct([])&lt;br /&gt;
    def end_ul(self):&lt;br /&gt;
        self.xostack.pop()&lt;br /&gt;
    def start_li(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_li(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1].append(val)&lt;br /&gt;
    def start_dt(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dt(self):&lt;br /&gt;
        pass&lt;br /&gt;
    def start_dd(self,attrs):&lt;br /&gt;
        self.textstack.append('')&lt;br /&gt;
    def end_dd(self):&lt;br /&gt;
        val = self.textstack.pop()&lt;br /&gt;
        key = self.textstack.pop()&lt;br /&gt;
        if self.structs[-1] != self.xostack[-1]:&lt;br /&gt;
            val = self.structs.pop()&lt;br /&gt;
        self.xostack[-1][key]=val&lt;br /&gt;
    def handle_data(self, text):&lt;br /&gt;
        if len(self.stack) and containerTags.get(self.stack[-1],True): #skip text not within an element&lt;br /&gt;
            self.textstack[-1] += text&lt;br /&gt;
def toXOXO(struct,addHTMLWrapper=False,cssUrl=''):&lt;br /&gt;
    if type(struct) ==type((1,))or type(struct) ==type([1,]):&lt;br /&gt;
        inStruct = struct&lt;br /&gt;
    else:&lt;br /&gt;
        inStruct = [struct]&lt;br /&gt;
    if addHTMLWrapper:&lt;br /&gt;
        s= '''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot; /&amp;gt;'''&lt;br /&gt;
        if cssUrl:&lt;br /&gt;
            s+='&amp;lt;style type=&amp;quot;text/css&amp;quot; &amp;gt;@import &amp;quot;%s&amp;quot;;&amp;lt;/style&amp;gt;' % cssUrl&lt;br /&gt;
        s+=&amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;%s&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot; % makeXOXO(inStruct,'xoxo')&lt;br /&gt;
        return s.encode('utf-8')&lt;br /&gt;
    else:&lt;br /&gt;
        return makeXOXO(inStruct,'xoxo').encode('utf-8')&lt;br /&gt;
    &lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
def fromXOXO(html):&lt;br /&gt;
    parser = xoxoParser()&lt;br /&gt;
    parser.feed(unicode(html,'utf-8'))&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, parser.structs&lt;br /&gt;
    structs=[struct for struct in parser.structs if struct]&lt;br /&gt;
    #print &amp;gt;&amp;gt;sys.stderr, structs&lt;br /&gt;
    while (len(structs) ==1 and type(structs)==type([1,])):&lt;br /&gt;
        structs=structs[0]&lt;br /&gt;
    return structs&lt;br /&gt;
&lt;br /&gt;
# Allow direct invocation&lt;br /&gt;
# Read HTML from URL, parse into data structures, then re-output&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
  if len(sys.argv) &amp;lt; 2: raise SystemExit(&amp;quot;Usage: &amp;quot;+sys.argv[0]+&amp;quot; url\n&amp;quot;+__doc__)&lt;br /&gt;
  url=sys.argv[1]&lt;br /&gt;
  file = urllib.urlopen(url)&lt;br /&gt;
  html=file.read(-1)&lt;br /&gt;
  file.close&lt;br /&gt;
  s=fromXOXO(html)&lt;br /&gt;
  p=toXOXO(s,True)&lt;br /&gt;
  print p&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== testxoxo.py  ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;testxoxo.py &lt;br /&gt;
Unit tests for xoxo.py&lt;br /&gt;
This file tests the functions in xoxo.py &lt;br /&gt;
The underlying model here is http://diveintopython.org/unit_testing/index.html &lt;br /&gt;
&lt;br /&gt;
run from command line with&lt;br /&gt;
python testxoxo.py -v&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
import xoxo&lt;br /&gt;
reload(xoxo)&lt;br /&gt;
import unittest&lt;br /&gt;
&lt;br /&gt;
class xoxoTestCases(unittest.TestCase):&lt;br /&gt;
    &lt;br /&gt;
    def testSimpleList(self):&lt;br /&gt;
        '''make a xoxo file from a list'''&lt;br /&gt;
        l = ['1','2','3']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
    def testNestedList(self):&lt;br /&gt;
        '''make a xoxo file from a list with a list in'''&lt;br /&gt;
        l = ['1',['2','3']]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testDictionary(self):&lt;br /&gt;
        '''make a xoxo file from a dictionary'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testSingleItem(self):&lt;br /&gt;
        '''make a xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        self.assertEqual(html,'&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
&lt;br /&gt;
    def testWrapDiffers(self):&lt;br /&gt;
        '''make a xoxo file from a string with and without html wrapper and check they are different'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        htmlwrap =  xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.failIfEqual(html,htmlwrap)&lt;br /&gt;
&lt;br /&gt;
    def testWrapSingleItem(self):&lt;br /&gt;
        '''make a wrapped xoxo file from a string'''&lt;br /&gt;
        l = &amp;quot;test&amp;quot;&lt;br /&gt;
        html = xoxo.toXOXO(l,addHTMLWrapper=True)&lt;br /&gt;
        self.assertEqual(html,'''&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;br /&gt;
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&amp;lt;head profile=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;''')&lt;br /&gt;
&lt;br /&gt;
    def testDictionaryRoundTrip(self):&lt;br /&gt;
        ''' make a dictionary into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':'1','name':'Kevin'}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
        &lt;br /&gt;
    def testListRoundTrip(self):&lt;br /&gt;
        ''' make a list into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3','2','1']&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofDictsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Dicts into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',{'a':'2'},{'b':'1','c':'4'}]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testListofListsRoundTrip(self):&lt;br /&gt;
        ''' make a list of Lists into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        l = ['3',['a','2'],['b',['1',['c','4']]]]&lt;br /&gt;
        html = xoxo.toXOXO(l)&lt;br /&gt;
        newdl= xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(l,newdl)&lt;br /&gt;
    def testDictofListsRoundTrip(self):&lt;br /&gt;
        ''' make a dict with lists in into a xoxo file and back again; check it is the same'''&lt;br /&gt;
        d = {'test':['1','2'],&lt;br /&gt;
        'name':'Kevin',&lt;br /&gt;
        'nestlist':['a',['b','c']],&lt;br /&gt;
        'nestdict':{'e':'6','f':'7'}}&lt;br /&gt;
        html = xoxo.toXOXO(d)&lt;br /&gt;
        newd = xoxo.fromXOXO(html)&lt;br /&gt;
        self.assertEqual(d,newd)&lt;br /&gt;
&lt;br /&gt;
    def testXOXOjunkInContainers(self):&lt;br /&gt;
        '''make sure text outside &amp;lt;li&amp;gt; etc is ignored'''&lt;br /&gt;
        d=xoxo.fromXOXO('&amp;lt;ol&amp;gt;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(d,{'good': 'buy'})&lt;br /&gt;
    def testXOXOjunkInElements(self):&lt;br /&gt;
        '''make sure text within &amp;lt;li&amp;gt; but outside a subcontainer is ignored'''&lt;br /&gt;
        l=xoxo.fromXOXO('&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;')&lt;br /&gt;
        self.assertEqual(l,[{'good': 'buy'},['OK']])&lt;br /&gt;
    def testXOXOWithSpacesAndNewlines(self):&lt;br /&gt;
        '''unmung some xoxo with spaces in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        d2={'text':'item 1',&lt;br /&gt;
            'description':&amp;quot; This item represents the main point we're trying to make.&amp;quot;,&lt;br /&gt;
            'url':'http://example.com/more.xoxo',&lt;br /&gt;
            'title':'title of item 1',&lt;br /&gt;
            'type':'text/xml',&lt;br /&gt;
            'rel':'help'&lt;br /&gt;
            }&lt;br /&gt;
        xoxoAgain = xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
        #this needs a smarter whitespace-sensitive comparison&lt;br /&gt;
        #self.assertEqual(xoxoSample,xoxoAgain)&lt;br /&gt;
&lt;br /&gt;
    def testSpecialAttributeDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class='xoxo'&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeAndDLDecoding(self):&lt;br /&gt;
        '''unmung some xoxo with &amp;lt;a href=' rel= etc in plus a &amp;lt;dl&amp;gt; in the same item and check result is right'''&lt;br /&gt;
        xoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;&lt;br /&gt;
        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;&lt;br /&gt;
    &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d = xoxo.fromXOXO(xoxoSample)&lt;br /&gt;
        smartxoxoSample= '''&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt; &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot;&lt;br /&gt;
         title=&amp;quot;title of item 1&amp;quot;&lt;br /&gt;
         type=&amp;quot;text/xml&amp;quot;&lt;br /&gt;
         rel=&amp;quot;help&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; &lt;br /&gt;
&amp;lt;!-- note how the &amp;quot;text&amp;quot; property is simply the contents of the &amp;lt;a&amp;gt; element --&amp;gt;&lt;br /&gt;
      &amp;lt;dl&amp;gt;&lt;br /&gt;
        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;&lt;br /&gt;
          &amp;lt;dd&amp;gt; This item represents the main point we're trying to make.&amp;lt;/dd&amp;gt;&lt;br /&gt;
      &amp;lt;/dl&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;'''&lt;br /&gt;
        d2 = xoxo.fromXOXO(smartxoxoSample)&lt;br /&gt;
        self.assertEqual(d,d2)&lt;br /&gt;
    def testSpecialAttributeEncode(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        expectedHTML= '&amp;lt;ol class=&amp;quot;xoxo&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://example.com/more.xoxo&amp;quot; title=&amp;quot;sample url&amp;quot; rel=&amp;quot;help&amp;quot; type=&amp;quot;text/xml&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;' &lt;br /&gt;
        self.assertEqual(html,expectedHTML)&lt;br /&gt;
        &lt;br /&gt;
    def testSpecialAttributeRoundTripFull(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help','text':'an example'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoText(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo','title':'sample url','type':&amp;quot;text/xml&amp;quot;,'rel':'help'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testSpecialAttributeRoundTripNoTextOrTitle(self):&lt;br /&gt;
        '''check it makes an &amp;lt;a href with a url parameter and no text or title attribute'''&lt;br /&gt;
        d={'url':'http://example.com/more.xoxo'}&lt;br /&gt;
        html=xoxo.toXOXO(d)&lt;br /&gt;
        self.assertEqual(d,xoxo.fromXOXO(html))&lt;br /&gt;
    def testUnicodeRoundtrip(self):&lt;br /&gt;
        '''check unicode characters can go to xoxo and back'''&lt;br /&gt;
        src=unicode('Tantek Çelik and a snowman ?','utf-8')&lt;br /&gt;
        html = html=xoxo.toXOXO(src)&lt;br /&gt;
        self.assertEqual(src,xoxo.fromXOXO(html))&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    unittest.main()&lt;br /&gt;
else:&lt;br /&gt;
    runner = unittest.TextTestRunner()&lt;br /&gt;
    suite = unittest.makeSuite(xoxoTestCases,'test')&lt;br /&gt;
    runner.run(suite)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
package org.atompub.draft.xoxo.tests;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOWriter;&lt;br /&gt;
import org.atompub.draft.xoxo.XOXOParser;&lt;br /&gt;
&lt;br /&gt;
import java.util.*;&lt;br /&gt;
&lt;br /&gt;
public class XOXOTest extends TestCase {&lt;br /&gt;
&lt;br /&gt;
  public static void main(String[] args) {&lt;br /&gt;
    new TestRunner().doRun(new TestSuite(XOXOTest.class));&lt;br /&gt;
  }&lt;br /&gt;
  String XHTML_DEC = &amp;quot;xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot; &amp;quot;;&lt;br /&gt;
  public String simpleListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSimpleList(){&lt;br /&gt;
    String [] numbers = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testStringIntegerList(){&lt;br /&gt;
    Object[] numbers = {new Integer(1),&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(simpleListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(numbers)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String nestedListHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testNestedList(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,Arrays.asList(arr)};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testNestedArray(){&lt;br /&gt;
    Object[] arr = {&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] nested = {&amp;quot;1&amp;quot;,arr};&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(nestedListHTML,&lt;br /&gt;
                 xoxo.toXOXO(Arrays.asList(nested)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String dictHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;test&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;1&amp;lt;/dd&amp;gt;&amp;lt;dt&amp;gt;name&amp;lt;/dt&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;dd&amp;gt;Kevin&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testDictionary(){&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, new Integer(1));&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(dictHTML,&lt;br /&gt;
                 xoxo.toXOXO(dict));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String singleHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(singleHTML,&lt;br /&gt;
                 xoxo.toXOXO(item));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testWrapDiffers(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String nowrap = xoxo.toXOXO(item);&lt;br /&gt;
    Object[] itemArr = {item};&lt;br /&gt;
    String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);&lt;br /&gt;
    assertFalse(wrap.equals(nowrap));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String startHTML = &amp;quot;&amp;lt;!DOCTYPE html PUBLIC \&amp;quot;-//W3C//DTD&amp;quot;&lt;br /&gt;
        + &amp;quot;XHTML 1.0 Transitional//EN\n&amp;quot;&lt;br /&gt;
        + &amp;quot;http://www.w3.org/TR/xhtml1/DTD/&amp;quot;&lt;br /&gt;
        + &amp;quot;xhtml1-transitional.dtd\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;html xmlns=\&amp;quot;http://www.w3.org/1999/xhtml\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
        + &amp;quot;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
  public String singleWrapHTML = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;&amp;lt;li&amp;gt;test&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
  public String endHTML = &amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testWrapSingleItem(){&lt;br /&gt;
    String item = &amp;quot;test&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    assertEquals(startHTML + singleWrapHTML + endHTML,&lt;br /&gt;
                 xoxo.toXOXO(item,true,null));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOParser(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      parser.parse(dictHTML);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
     try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListRoundTrip(){&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfDictsRoundTrip(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;test&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;name&amp;quot;, &amp;quot;Kevin&amp;quot;);&lt;br /&gt;
    Map dict2 = new LinkedHashMap();&lt;br /&gt;
    dict2.put(&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;three&amp;quot;, &amp;quot;four&amp;quot;);&lt;br /&gt;
    dict2.put(&amp;quot;five&amp;quot;, &amp;quot;six&amp;quot;);&lt;br /&gt;
    Object[] obj = {&amp;quot;1&amp;quot;,dict,dict2};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testListOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;, Arrays.asList(list1)};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;, Arrays.asList(list2)};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, Arrays.asList(list3)};&lt;br /&gt;
    List testList = Arrays.asList(list4);&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(testList);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(html);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testDictOfListsRoundTrip(){&lt;br /&gt;
    Object[] list1 = {&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;};&lt;br /&gt;
    Object[] list2 = {&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;};&lt;br /&gt;
    Object[] list3 = {&amp;quot;7&amp;quot;};&lt;br /&gt;
    Object[] list4 = {&amp;quot;8&amp;quot;, &amp;quot;9&amp;quot;};&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;foo&amp;quot;, Arrays.asList(list1));&lt;br /&gt;
    dict.put(&amp;quot;bar&amp;quot;, Arrays.asList(list2));&lt;br /&gt;
    dict.put(&amp;quot;baz&amp;quot;, Arrays.asList(list3));&lt;br /&gt;
    dict.put(&amp;quot;qux&amp;quot;, Arrays.asList(list4));&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;bad&amp;lt;li&amp;gt;&amp;lt;dl&amp;gt;worse&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt; now&amp;lt;/dl&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOJunkInContainers(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(junkXOXO);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String junkElementXOXO = &amp;quot;&amp;lt;ol &amp;quot;&lt;br /&gt;
  + XHTML_DEC&lt;br /&gt;
  + &amp;quot;&amp;gt;&amp;lt;li&amp;gt;bad&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;good&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;buy&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&amp;quot;&lt;br /&gt;
  + &amp;quot;worse&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;bag&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;OK&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;fish&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOjunkInElements(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;good&amp;quot;,&amp;quot;buy&amp;quot;);&lt;br /&gt;
    Object[] ok = {&amp;quot;OK&amp;quot;};&lt;br /&gt;
    Object[] obj ={dict, Arrays.asList(ok)};&lt;br /&gt;
    List testList = Arrays.asList(obj);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newList = parser.parse(junkElementXOXO);&lt;br /&gt;
      assertEquals(testList,newList);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSpacesNewlines = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;description&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt; This item represents the main&amp;quot; +&lt;br /&gt;
      &amp;quot; point we're trying to make.&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testXOXOWithSpacesAndNewlines(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;description&amp;quot;,&amp;quot; This item represents the main&amp;quot; +&lt;br /&gt;
        &amp;quot; point we're trying to make.&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;title of item 1&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(xoxoSpacesNewlines);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String xoxoSample = &amp;quot;&amp;lt;ol &amp;quot; +  XHTML_DEC +&lt;br /&gt;
      &amp;quot; class='xoxo'&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;text&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;url&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;http://example.com/more.xoxo&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;title&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;title of item 1&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;type&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;text/xml&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dt&amp;gt;rel&amp;lt;/dt&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;        &amp;lt;dd&amp;gt;help&amp;lt;/dd&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;    &amp;lt;/dl&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public String smartXOXOSample = &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         title=\&amp;quot;title of item 1\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         type=\&amp;quot;text/xml\&amp;quot;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;         rel=\&amp;quot;help\&amp;quot;&amp;gt;item 1&amp;lt;/a&amp;gt; \n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;!-- note how the \&amp;quot;text\&amp;quot; property is simply&amp;quot; +&lt;br /&gt;
      &amp;quot; the contents of the &amp;lt;a&amp;gt; element --&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;  &amp;lt;/li&amp;gt;\n&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeDecoding(){&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    try{&lt;br /&gt;
      Object xoxoDict = parser.parse(xoxoSample);&lt;br /&gt;
      Object xoxoDict2 = parser.parse(smartXOXOSample);&lt;br /&gt;
      assertEquals(xoxoDict,xoxoDict2);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public String specialAttrHTML =  &amp;quot;&amp;lt;ol &amp;quot; + XHTML_DEC +&lt;br /&gt;
      &amp;quot;class=\&amp;quot;xoxo\&amp;quot;&amp;gt;&amp;quot; +&lt;br /&gt;
      &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;http://example.com/more.xoxo\&amp;quot; title=\&amp;quot;sample url\&amp;quot; &amp;quot; +&lt;br /&gt;
      &amp;quot;rel=\&amp;quot;help\&amp;quot; type=\&amp;quot;text/xml\&amp;quot; &amp;gt;an example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeEncode(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    assertEquals(specialAttrHTML,html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripFull(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;text&amp;quot;,&amp;quot;an example&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoText(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;title&amp;quot;,&amp;quot;sample url&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testSpecialAttributeRoundTripNoTextOrTitle(){&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    Map dict = new LinkedHashMap();&lt;br /&gt;
    dict.put(&amp;quot;url&amp;quot;,&amp;quot;http://example.com/more.xoxo&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;type&amp;quot;,&amp;quot;text/xml&amp;quot;);&lt;br /&gt;
    dict.put(&amp;quot;rel&amp;quot;,&amp;quot;help&amp;quot;);&lt;br /&gt;
    String html = xoxo.toXOXO(dict);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newDict = parser.parse(html);&lt;br /&gt;
      assertEquals(dict,newDict);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public void testUnicodeRoundTrip(){&lt;br /&gt;
    String s = &amp;quot;Tantek Çelik and a snowman ?&amp;quot;;&lt;br /&gt;
    XOXOWriter xoxo = new XOXOWriter();&lt;br /&gt;
    XOXOParser parser = new XOXOParser();&lt;br /&gt;
    String html = xoxo.toXOXO(s);&lt;br /&gt;
    try{&lt;br /&gt;
      Object newString = parser.parse(html);&lt;br /&gt;
      assertEquals(s,newString);&lt;br /&gt;
    }catch (Exception e){&lt;br /&gt;
      fail(e.getMessage());&lt;br /&gt;
      e.printStackTrace();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=2166</id>
		<title>datetime-design-pattern</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=2166"/>
		<updated>2005-09-05T17:44:02Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This page is a draft.'''&lt;br /&gt;
&lt;br /&gt;
This is a page for exploring a datetime design pattern.&lt;br /&gt;
&lt;br /&gt;
The pattern which is now used in [[hcalendar|hCalendar]] and [[hreview|hReview]] is something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt;abbr class=&amp;quot;foo&amp;quot; title=&amp;quot;YYYYMMDDTHH:MM:SS+ZZZZ&amp;quot;&amp;amp;gt;Date Time&amp;amp;lt;/abbr&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where foo is the semantic classname which is being applied to this date/time, the title of the &amp;amp;lt;abbr&amp;amp;gt; is an ISO 8601 date/time and &amp;quot;Date Time&amp;quot; is a human-friendly representation of the same date/time.&lt;br /&gt;
&lt;br /&gt;
This pattern is likely to be highly resuable.&lt;br /&gt;
&lt;br /&gt;
--[[RyanKing]]&lt;br /&gt;
&lt;br /&gt;
Can this not be viewed as a microformat in itself?&lt;br /&gt;
&lt;br /&gt;
--[[DimitriGlazkov]]&lt;br /&gt;
&lt;br /&gt;
It could, but inventing a microformat for the sake of inventing a microformat is against the microformat principles.&lt;br /&gt;
If there is a specific real world problem (and uses cases) that such an elemental microformat would solve, then it would be worth considering.&lt;br /&gt;
&lt;br /&gt;
Until then it is best to keep the &amp;amp;lt;abbr&amp;amp;gt; datetime concept merely as a microformat design pattern, to be used in _actual_ microformats that have a demonstrated practical need.&lt;br /&gt;
&lt;br /&gt;
-- [http://tantek.com/log/ Tantek]&lt;br /&gt;
&lt;br /&gt;
Excerpt from #microformats Aug 18th. Please edit!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 15:16:14 &amp;lt;Tantek&amp;gt;	DanC, what do you think of RFC3339?&lt;br /&gt;
Aug 18 15:17:14 &amp;lt;Tantek&amp;gt;	ISO8601 subset&lt;br /&gt;
Aug 18 15:17:19 &amp;lt;DanC&amp;gt;	Date and Time on the Internet: Timestamps http://www.ietf.org/rfc/rfc3339.txt&lt;br /&gt;
Aug 18 15:17:30 &amp;lt;DanC&amp;gt;	Klyne is a good guy. I wonder if I talked with him about this.&lt;br /&gt;
Aug 18 15:17:32 &amp;lt;Tantek&amp;gt;	compat with W3C-NOTE-DATETIME&lt;br /&gt;
Aug 18 15:17:50 &amp;lt;Tantek&amp;gt;	compat with xsd:dateTime&lt;br /&gt;
Aug 18 15:17:57 &amp;lt;Tantek&amp;gt;	it's a strict intersection subset&lt;br /&gt;
Aug 18 15:17:59 &amp;lt;DanC&amp;gt;	I consider W3C-NOTE-DATETIME obsoleted by XML Schema datatype-- yeah.. xsd:dateTime&lt;br /&gt;
Aug 18 15:18:32 &amp;lt;Tantek&amp;gt;	compare/contrast normatively using xsd:dateTime vs. RFC3339&lt;br /&gt;
Aug 18 15:18:41 &amp;lt;Tantek&amp;gt;	note: Atom 1.0 chose RFC3339&lt;br /&gt;
Aug 18 15:18:50 &amp;lt;Tantek&amp;gt;	i would like input from the microformats community on this&lt;br /&gt;
Aug 18 15:19:27 &amp;lt;DanC&amp;gt;	in what context are you evaluating RFC 3339?&lt;br /&gt;
Aug 18 15:19:28 &amp;lt;jcgregorio&amp;gt;	http://bitworking.org/news/Date_Constructs_in_the_Atom_Syndication_Format&lt;br /&gt;
Aug 18 15:21:24 &amp;lt;DanC&amp;gt;	which microformat is the question coming from, Tantek ?&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	&amp;quot;   The grammar element time-second may have the value &amp;quot;60&amp;quot; at the end of&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	   months in which a leap second occurs&amp;quot; The XML Schema WG is in the 27th level of leap-second-hell for the past few months, I gather.&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	yeah... here's the scary bit: &amp;quot;   Leap seconds cannot be predicted far into the future.  The&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   International Earth Rotation Service publishes bulletins [IERS] that&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   announce leap seconds with a few weeks' warning.&amp;quot;&lt;br /&gt;
Aug 18 15:26:03 &amp;lt;Tantek&amp;gt;	DanC, which microformats? any/all that use datetime fields.&lt;br /&gt;
Aug 18 15:26:36 &amp;lt;DanC&amp;gt;	hard to give useful advice, then.&lt;br /&gt;
Aug 18 15:26:58 &amp;lt;DanC&amp;gt;	I expect they'll use datetime fields for different things that have different cost/benefit trade-offs&lt;br /&gt;
Aug 18 15:27:26 &amp;lt;DanC&amp;gt;	do you know of any particular differences that matter to anybody?&lt;br /&gt;
Aug 18 15:56:43 &amp;lt;KragenSitaker&amp;gt;	RFC3339 suggests -07:00, which seems like an improvement over -0700 anyway&lt;br /&gt;
Aug 18 15:56:49 &amp;lt;Tantek&amp;gt;	Kragen, agreed&lt;br /&gt;
Aug 18 15:57:01 &amp;lt;Tantek&amp;gt;	RFC3339 is certainly preferable to the ISO8601 subset in iCalendar&lt;br /&gt;
Aug 18 16:05:57 &amp;lt;DanC&amp;gt;	Tantek's right, Kragen; iCalendar looks like it solves the local timezone problem but doesn't.&lt;br /&gt;
Aug 18 16:06:14 &amp;lt;DanC&amp;gt;	and it's true that there's no standard solution to the local timezone problem&lt;br /&gt;
Aug 18 16:06:39 &amp;lt;Tantek&amp;gt;	so instead of appearing to solve the problem but not solving it, we chose to provide the ability to *approximate* the local timezone using e.g. &amp;quot;-07:00&amp;quot;&lt;br /&gt;
Aug 18 16:06:49 &amp;lt;DanC&amp;gt;	the simplest thing is to have people use Z time in hCalendar. But I gather that's unacceptably unusable?&lt;br /&gt;
Aug 18 16:07:35 &amp;lt;Tantek&amp;gt;	DanC, yes, the simplest thing is to have everyone use UTC Z&lt;br /&gt;
Aug 18 16:07:38 &amp;lt;Tantek&amp;gt;	However&lt;br /&gt;
Aug 18 16:07:50 &amp;lt;Tantek&amp;gt;	it is not *nearly* as usuable/verifiable&lt;br /&gt;
Aug 18 16:07:55 &amp;lt;Tantek&amp;gt;	as -07:00 etc.&lt;br /&gt;
Aug 18 16:08:02 &amp;lt;Tantek&amp;gt;	hence the decision to go with the latter&lt;br /&gt;
Aug 18 16:08:12 &amp;lt;Tantek&amp;gt;	some degree of human verifiability is important here&lt;br /&gt;
Aug 18 16:14:21 &amp;lt;Tantek&amp;gt;	DanC, my perception is that RFC3339 is a subset&lt;br /&gt;
Aug 18 16:17:00 &amp;lt;DanC&amp;gt;	   time-numoffset  = (&amp;quot;+&amp;quot; / &amp;quot;-&amp;quot;) time-hour &amp;quot;:&amp;quot; time-minute&lt;br /&gt;
Aug 18 16:17:34 &amp;lt;DanC&amp;gt;	ok, then I can't see any differences. (modulo recent leap seconds issues that may affect xsd:dateTime )&lt;br /&gt;
Aug 18 16:18:07 &amp;lt;Tantek&amp;gt;	would be interesting to know why Atom 1.0 chose RFC3339 over xsd:dateTime&lt;br /&gt;
Aug 18 16:18:21 &amp;lt;Tantek&amp;gt;	if there was a &amp;quot;real&amp;quot; reason or if it was arbitrary / coin-flip.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's an exhaustive [http://www.imc.org/atom-syntax/mail-archive/msg13103.html comparison] from ndw. I think xsd:dateTime also allows unqualified local times, while RFC3339 allows only UTC with no known timezone (-00:00). In the end, Atompub followed the advice of [http://www.imc.org/atom-syntax/mail-archive/msg13244.html Sam Ruby] and [http://www.imc.org/atom-syntax/mail-archive/msg13248.html Scott Hollenbeck], our area director. Atom dates make some additional restrictions on RFC3339, such as uppercase T and Z characters for compatibility with xsd:dateTime, RFC3339, W3C-DTF, and ISO8601. --[http://franklinmint.fm Robert Sayre]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 16:18:43 &amp;lt;KragenSitaker&amp;gt;	rfc3339 is pretty short.&lt;br /&gt;
Aug 18 16:19:36 &amp;lt;Tantek&amp;gt;	DanC, BTW, which came first? REC for xsd:dateTime or RFC3339?&lt;br /&gt;
Aug 18 16:19:50 &amp;lt;DanC&amp;gt;	RFC3339 is dated July 2002 ...&lt;br /&gt;
Aug 18 16:19:54 &amp;lt;KragenSitaker&amp;gt;	Right --- and you might be able to understand xsd:dateTime without reading all of xml schema, you wouldn't be confident of it&lt;br /&gt;
Aug 18 16:20:25 &amp;lt;DanC&amp;gt;	W3C Recommendation 28 October 2004 ... but that's 2nd ed...&lt;br /&gt;
Aug 18 16:20:47 &amp;lt;DanC&amp;gt;	W3C Recommendation 02 May 2001&lt;br /&gt;
Aug 18 16:22:10 &amp;lt;DanC&amp;gt;	I don't see a BNF in http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime ...&lt;br /&gt;
Aug 18 16:22:43 &amp;lt;KragenSitaker&amp;gt;	yeah, appendix D of the current xml schema datatypes document seems a little scanty, actually&lt;br /&gt;
Aug 18 16:23:28 &amp;lt;DanC&amp;gt;	ah... 2nd ed of http://www.w3.org/TR/xmlschema-2/#date is much more explicit about syntax.&lt;br /&gt;
Aug 18 16:23:30 &amp;lt;KragenSitaker&amp;gt;	it's 1100 words but still doesn't give any examples&lt;br /&gt;
Aug 18 16:23:35 &amp;lt;DanC&amp;gt;	still, it's given in prose and not BNF&lt;br /&gt;
Aug 18 16:24:17 &amp;lt;KragenSitaker&amp;gt;	sections 3.2.9 through 3.2.14 seem to be the relevant ones around #date&lt;br /&gt;
Aug 18 16:24:29 &amp;lt;KragenSitaker&amp;gt;	which is another 2200 words&lt;br /&gt;
Aug 18 16:24:42 &amp;lt;DanC&amp;gt;	wow... they changed the canonical form of date from always-Z to timezone-allowed between 1st edition and 2nd edition&lt;br /&gt;
Aug 18 16:25:01 &amp;lt;Tantek&amp;gt;	Kragen, DanC, these are very good analyses&lt;br /&gt;
Aug 18 16:25:21 &amp;lt;Tantek&amp;gt;	could I ask you to summarize the pros/cons for each in a new section at end of http://microformats.org/wiki/datetime-design-pattern&lt;br /&gt;
Aug 18 16:25:22 &amp;lt;Tantek&amp;gt;	?&lt;br /&gt;
Aug 18 16:25:58 &amp;lt;KragenSitaker&amp;gt;	rfc 3339 is 4000 words, excluding the last two pages of boilerplate.&lt;br /&gt;
Aug 18 16:26:31 &amp;lt;KragenSitaker&amp;gt;	so it's actually longer than the datetime-relevant parts of XSD but it seems much more rigorous and clear&lt;br /&gt;
Aug 18 16:28:37 &amp;lt;DanC&amp;gt;	my advice is: normatively cite both, and claim they specify the same syntax, and let anybody who discovers otherwise send you a bug report with a test case&lt;br /&gt;
Aug 18 16:29:12 &amp;lt;KragenSitaker&amp;gt;	danc: nice hack&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The RFC3339 has a mandatory TIME portion of the DATE-TIME. Some vCard/iCalendar DATE-TIME stamps can omit the TIME. For instance, DTSTART, if that is a full day event, then you can omit the time. BDAY in vCard can be respresented by only a DATE. I like the idea of restricting the possible date formats, but i think that TIME should be optional, which it isn't in RFC3339. - [http://suda.co.uk/ brian suda]&lt;br /&gt;
&lt;br /&gt;
RFC 3339 allows lowercase 't' and 'z' while XSD doesn't. Specifying RFC 3339 plus 'T' and 'Z' MUST be caps will make them the same. - [http://bitworking.org Joe Gregorio]&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1587</id>
		<title>datetime-design-pattern</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1587"/>
		<updated>2005-09-05T17:43:27Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This page is a draft.'''&lt;br /&gt;
&lt;br /&gt;
This is a page for exploring a datetime design pattern.&lt;br /&gt;
&lt;br /&gt;
The pattern which is now used in [[hcalendar|hCalendar]] and [[hreview|hReview]] is something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt;abbr class=&amp;quot;foo&amp;quot; title=&amp;quot;YYYYMMDDTHH:MM:SS+ZZZZ&amp;quot;&amp;amp;gt;Date Time&amp;amp;lt;/abbr&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where foo is the semantic classname which is being applied to this date/time, the title of the &amp;amp;lt;abbr&amp;amp;gt; is an ISO 8601 date/time and &amp;quot;Date Time&amp;quot; is a human-friendly representation of the same date/time.&lt;br /&gt;
&lt;br /&gt;
This pattern is likely to be highly resuable.&lt;br /&gt;
&lt;br /&gt;
--[[RyanKing]]&lt;br /&gt;
&lt;br /&gt;
Can this not be viewed as a microformat in itself?&lt;br /&gt;
&lt;br /&gt;
--[[DimitriGlazkov]]&lt;br /&gt;
&lt;br /&gt;
It could, but inventing a microformat for the sake of inventing a microformat is against the microformat principles.&lt;br /&gt;
If there is a specific real world problem (and uses cases) that such an elemental microformat would solve, then it would be worth considering.&lt;br /&gt;
&lt;br /&gt;
Until then it is best to keep the &amp;amp;lt;abbr&amp;amp;gt; datetime concept merely as a microformat design pattern, to be used in _actual_ microformats that have a demonstrated practical need.&lt;br /&gt;
&lt;br /&gt;
-- [http://tantek.com/log/ Tantek]&lt;br /&gt;
&lt;br /&gt;
Excerpt from #microformats Aug 18th. Please edit!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 15:16:14 &amp;lt;Tantek&amp;gt;	DanC, what do you think of RFC3339?&lt;br /&gt;
Aug 18 15:17:14 &amp;lt;Tantek&amp;gt;	ISO8601 subset&lt;br /&gt;
Aug 18 15:17:19 &amp;lt;DanC&amp;gt;	Date and Time on the Internet: Timestamps http://www.ietf.org/rfc/rfc3339.txt&lt;br /&gt;
Aug 18 15:17:30 &amp;lt;DanC&amp;gt;	Klyne is a good guy. I wonder if I talked with him about this.&lt;br /&gt;
Aug 18 15:17:32 &amp;lt;Tantek&amp;gt;	compat with W3C-NOTE-DATETIME&lt;br /&gt;
Aug 18 15:17:50 &amp;lt;Tantek&amp;gt;	compat with xsd:dateTime&lt;br /&gt;
Aug 18 15:17:57 &amp;lt;Tantek&amp;gt;	it's a strict intersection subset&lt;br /&gt;
Aug 18 15:17:59 &amp;lt;DanC&amp;gt;	I consider W3C-NOTE-DATETIME obsoleted by XML Schema datatype-- yeah.. xsd:dateTime&lt;br /&gt;
Aug 18 15:18:32 &amp;lt;Tantek&amp;gt;	compare/contrast normatively using xsd:dateTime vs. RFC3339&lt;br /&gt;
Aug 18 15:18:41 &amp;lt;Tantek&amp;gt;	note: Atom 1.0 chose RFC3339&lt;br /&gt;
Aug 18 15:18:50 &amp;lt;Tantek&amp;gt;	i would like input from the microformats community on this&lt;br /&gt;
Aug 18 15:19:27 &amp;lt;DanC&amp;gt;	in what context are you evaluating RFC 3339?&lt;br /&gt;
Aug 18 15:19:28 &amp;lt;jcgregorio&amp;gt;	http://bitworking.org/news/Date_Constructs_in_the_Atom_Syndication_Format&lt;br /&gt;
Aug 18 15:21:24 &amp;lt;DanC&amp;gt;	which microformat is the question coming from, Tantek ?&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	&amp;quot;   The grammar element time-second may have the value &amp;quot;60&amp;quot; at the end of&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	   months in which a leap second occurs&amp;quot; The XML Schema WG is in the 27th level of leap-second-hell for the past few months, I gather.&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	yeah... here's the scary bit: &amp;quot;   Leap seconds cannot be predicted far into the future.  The&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   International Earth Rotation Service publishes bulletins [IERS] that&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   announce leap seconds with a few weeks' warning.&amp;quot;&lt;br /&gt;
Aug 18 15:26:03 &amp;lt;Tantek&amp;gt;	DanC, which microformats? any/all that use datetime fields.&lt;br /&gt;
Aug 18 15:26:36 &amp;lt;DanC&amp;gt;	hard to give useful advice, then.&lt;br /&gt;
Aug 18 15:26:58 &amp;lt;DanC&amp;gt;	I expect they'll use datetime fields for different things that have different cost/benefit trade-offs&lt;br /&gt;
Aug 18 15:27:26 &amp;lt;DanC&amp;gt;	do you know of any particular differences that matter to anybody?&lt;br /&gt;
Aug 18 15:56:43 &amp;lt;KragenSitaker&amp;gt;	RFC3339 suggests -07:00, which seems like an improvement over -0700 anyway&lt;br /&gt;
Aug 18 15:56:49 &amp;lt;Tantek&amp;gt;	Kragen, agreed&lt;br /&gt;
Aug 18 15:57:01 &amp;lt;Tantek&amp;gt;	RFC3339 is certainly preferable to the ISO8601 subset in iCalendar&lt;br /&gt;
Aug 18 16:05:57 &amp;lt;DanC&amp;gt;	Tantek's right, Kragen; iCalendar looks like it solves the local timezone problem but doesn't.&lt;br /&gt;
Aug 18 16:06:14 &amp;lt;DanC&amp;gt;	and it's true that there's no standard solution to the local timezone problem&lt;br /&gt;
Aug 18 16:06:39 &amp;lt;Tantek&amp;gt;	so instead of appearing to solve the problem but not solving it, we chose to provide the ability to *approximate* the local timezone using e.g. &amp;quot;-07:00&amp;quot;&lt;br /&gt;
Aug 18 16:06:49 &amp;lt;DanC&amp;gt;	the simplest thing is to have people use Z time in hCalendar. But I gather that's unacceptably unusable?&lt;br /&gt;
Aug 18 16:07:35 &amp;lt;Tantek&amp;gt;	DanC, yes, the simplest thing is to have everyone use UTC Z&lt;br /&gt;
Aug 18 16:07:38 &amp;lt;Tantek&amp;gt;	However&lt;br /&gt;
Aug 18 16:07:50 &amp;lt;Tantek&amp;gt;	it is not *nearly* as usuable/verifiable&lt;br /&gt;
Aug 18 16:07:55 &amp;lt;Tantek&amp;gt;	as -07:00 etc.&lt;br /&gt;
Aug 18 16:08:02 &amp;lt;Tantek&amp;gt;	hence the decision to go with the latter&lt;br /&gt;
Aug 18 16:08:12 &amp;lt;Tantek&amp;gt;	some degree of human verifiability is important here&lt;br /&gt;
Aug 18 16:14:21 &amp;lt;Tantek&amp;gt;	DanC, my perception is that RFC3339 is a subset&lt;br /&gt;
Aug 18 16:17:00 &amp;lt;DanC&amp;gt;	   time-numoffset  = (&amp;quot;+&amp;quot; / &amp;quot;-&amp;quot;) time-hour &amp;quot;:&amp;quot; time-minute&lt;br /&gt;
Aug 18 16:17:34 &amp;lt;DanC&amp;gt;	ok, then I can't see any differences. (modulo recent leap seconds issues that may affect xsd:dateTime )&lt;br /&gt;
Aug 18 16:18:07 &amp;lt;Tantek&amp;gt;	would be interesting to know why Atom 1.0 chose RFC3339 over xsd:dateTime&lt;br /&gt;
Aug 18 16:18:21 &amp;lt;Tantek&amp;gt;	if there was a &amp;quot;real&amp;quot; reason or if it was arbitrary / coin-flip.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's an exhaustive [http://www.imc.org/atom-syntax/mail-archive/msg13103.html comparison] from ndw. I think xsd:dateTime also allows unqualified local times, while RFC3339 allows only UTC with no known timezone (-00:00). In the end, Atompub followed the advice of [http://www.imc.org/atom-syntax/mail-archive/msg13244.html Sam Ruby] and [http://www.imc.org/atom-syntax/mail-archive/msg13248.html Scott Hollenbeck] our area director. Atom dates make some additional restrictions on RFC3339, such as uppercase T and Z characters for compatibility with xsd:dateTime, RFC3339, W3C-DTF, and ISO8601. --[http://franklinmint.fm Robert Sayre]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 16:18:43 &amp;lt;KragenSitaker&amp;gt;	rfc3339 is pretty short.&lt;br /&gt;
Aug 18 16:19:36 &amp;lt;Tantek&amp;gt;	DanC, BTW, which came first? REC for xsd:dateTime or RFC3339?&lt;br /&gt;
Aug 18 16:19:50 &amp;lt;DanC&amp;gt;	RFC3339 is dated July 2002 ...&lt;br /&gt;
Aug 18 16:19:54 &amp;lt;KragenSitaker&amp;gt;	Right --- and you might be able to understand xsd:dateTime without reading all of xml schema, you wouldn't be confident of it&lt;br /&gt;
Aug 18 16:20:25 &amp;lt;DanC&amp;gt;	W3C Recommendation 28 October 2004 ... but that's 2nd ed...&lt;br /&gt;
Aug 18 16:20:47 &amp;lt;DanC&amp;gt;	W3C Recommendation 02 May 2001&lt;br /&gt;
Aug 18 16:22:10 &amp;lt;DanC&amp;gt;	I don't see a BNF in http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime ...&lt;br /&gt;
Aug 18 16:22:43 &amp;lt;KragenSitaker&amp;gt;	yeah, appendix D of the current xml schema datatypes document seems a little scanty, actually&lt;br /&gt;
Aug 18 16:23:28 &amp;lt;DanC&amp;gt;	ah... 2nd ed of http://www.w3.org/TR/xmlschema-2/#date is much more explicit about syntax.&lt;br /&gt;
Aug 18 16:23:30 &amp;lt;KragenSitaker&amp;gt;	it's 1100 words but still doesn't give any examples&lt;br /&gt;
Aug 18 16:23:35 &amp;lt;DanC&amp;gt;	still, it's given in prose and not BNF&lt;br /&gt;
Aug 18 16:24:17 &amp;lt;KragenSitaker&amp;gt;	sections 3.2.9 through 3.2.14 seem to be the relevant ones around #date&lt;br /&gt;
Aug 18 16:24:29 &amp;lt;KragenSitaker&amp;gt;	which is another 2200 words&lt;br /&gt;
Aug 18 16:24:42 &amp;lt;DanC&amp;gt;	wow... they changed the canonical form of date from always-Z to timezone-allowed between 1st edition and 2nd edition&lt;br /&gt;
Aug 18 16:25:01 &amp;lt;Tantek&amp;gt;	Kragen, DanC, these are very good analyses&lt;br /&gt;
Aug 18 16:25:21 &amp;lt;Tantek&amp;gt;	could I ask you to summarize the pros/cons for each in a new section at end of http://microformats.org/wiki/datetime-design-pattern&lt;br /&gt;
Aug 18 16:25:22 &amp;lt;Tantek&amp;gt;	?&lt;br /&gt;
Aug 18 16:25:58 &amp;lt;KragenSitaker&amp;gt;	rfc 3339 is 4000 words, excluding the last two pages of boilerplate.&lt;br /&gt;
Aug 18 16:26:31 &amp;lt;KragenSitaker&amp;gt;	so it's actually longer than the datetime-relevant parts of XSD but it seems much more rigorous and clear&lt;br /&gt;
Aug 18 16:28:37 &amp;lt;DanC&amp;gt;	my advice is: normatively cite both, and claim they specify the same syntax, and let anybody who discovers otherwise send you a bug report with a test case&lt;br /&gt;
Aug 18 16:29:12 &amp;lt;KragenSitaker&amp;gt;	danc: nice hack&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The RFC3339 has a mandatory TIME portion of the DATE-TIME. Some vCard/iCalendar DATE-TIME stamps can omit the TIME. For instance, DTSTART, if that is a full day event, then you can omit the time. BDAY in vCard can be respresented by only a DATE. I like the idea of restricting the possible date formats, but i think that TIME should be optional, which it isn't in RFC3339. - [http://suda.co.uk/ brian suda]&lt;br /&gt;
&lt;br /&gt;
RFC 3339 allows lowercase 't' and 'z' while XSD doesn't. Specifying RFC 3339 plus 'T' and 'Z' MUST be caps will make them the same. - [http://bitworking.org Joe Gregorio]&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1586</id>
		<title>datetime-design-pattern</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1586"/>
		<updated>2005-09-05T17:43:11Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This page is a draft.'''&lt;br /&gt;
&lt;br /&gt;
This is a page for exploring a datetime design pattern.&lt;br /&gt;
&lt;br /&gt;
The pattern which is now used in [[hcalendar|hCalendar]] and [[hreview|hReview]] is something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt;abbr class=&amp;quot;foo&amp;quot; title=&amp;quot;YYYYMMDDTHH:MM:SS+ZZZZ&amp;quot;&amp;amp;gt;Date Time&amp;amp;lt;/abbr&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where foo is the semantic classname which is being applied to this date/time, the title of the &amp;amp;lt;abbr&amp;amp;gt; is an ISO 8601 date/time and &amp;quot;Date Time&amp;quot; is a human-friendly representation of the same date/time.&lt;br /&gt;
&lt;br /&gt;
This pattern is likely to be highly resuable.&lt;br /&gt;
&lt;br /&gt;
--[[RyanKing]]&lt;br /&gt;
&lt;br /&gt;
Can this not be viewed as a microformat in itself?&lt;br /&gt;
&lt;br /&gt;
--[[DimitriGlazkov]]&lt;br /&gt;
&lt;br /&gt;
It could, but inventing a microformat for the sake of inventing a microformat is against the microformat principles.&lt;br /&gt;
If there is a specific real world problem (and uses cases) that such an elemental microformat would solve, then it would be worth considering.&lt;br /&gt;
&lt;br /&gt;
Until then it is best to keep the &amp;amp;lt;abbr&amp;amp;gt; datetime concept merely as a microformat design pattern, to be used in _actual_ microformats that have a demonstrated practical need.&lt;br /&gt;
&lt;br /&gt;
-- [http://tantek.com/log/ Tantek]&lt;br /&gt;
&lt;br /&gt;
Excerpt from #microformats Aug 18th. Please edit!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 15:16:14 &amp;lt;Tantek&amp;gt;	DanC, what do you think of RFC3339?&lt;br /&gt;
Aug 18 15:17:14 &amp;lt;Tantek&amp;gt;	ISO8601 subset&lt;br /&gt;
Aug 18 15:17:19 &amp;lt;DanC&amp;gt;	Date and Time on the Internet: Timestamps http://www.ietf.org/rfc/rfc3339.txt&lt;br /&gt;
Aug 18 15:17:30 &amp;lt;DanC&amp;gt;	Klyne is a good guy. I wonder if I talked with him about this.&lt;br /&gt;
Aug 18 15:17:32 &amp;lt;Tantek&amp;gt;	compat with W3C-NOTE-DATETIME&lt;br /&gt;
Aug 18 15:17:50 &amp;lt;Tantek&amp;gt;	compat with xsd:dateTime&lt;br /&gt;
Aug 18 15:17:57 &amp;lt;Tantek&amp;gt;	it's a strict intersection subset&lt;br /&gt;
Aug 18 15:17:59 &amp;lt;DanC&amp;gt;	I consider W3C-NOTE-DATETIME obsoleted by XML Schema datatype-- yeah.. xsd:dateTime&lt;br /&gt;
Aug 18 15:18:32 &amp;lt;Tantek&amp;gt;	compare/contrast normatively using xsd:dateTime vs. RFC3339&lt;br /&gt;
Aug 18 15:18:41 &amp;lt;Tantek&amp;gt;	note: Atom 1.0 chose RFC3339&lt;br /&gt;
Aug 18 15:18:50 &amp;lt;Tantek&amp;gt;	i would like input from the microformats community on this&lt;br /&gt;
Aug 18 15:19:27 &amp;lt;DanC&amp;gt;	in what context are you evaluating RFC 3339?&lt;br /&gt;
Aug 18 15:19:28 &amp;lt;jcgregorio&amp;gt;	http://bitworking.org/news/Date_Constructs_in_the_Atom_Syndication_Format&lt;br /&gt;
Aug 18 15:21:24 &amp;lt;DanC&amp;gt;	which microformat is the question coming from, Tantek ?&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	&amp;quot;   The grammar element time-second may have the value &amp;quot;60&amp;quot; at the end of&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	   months in which a leap second occurs&amp;quot; The XML Schema WG is in the 27th level of leap-second-hell for the past few months, I gather.&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	yeah... here's the scary bit: &amp;quot;   Leap seconds cannot be predicted far into the future.  The&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   International Earth Rotation Service publishes bulletins [IERS] that&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   announce leap seconds with a few weeks' warning.&amp;quot;&lt;br /&gt;
Aug 18 15:26:03 &amp;lt;Tantek&amp;gt;	DanC, which microformats? any/all that use datetime fields.&lt;br /&gt;
Aug 18 15:26:36 &amp;lt;DanC&amp;gt;	hard to give useful advice, then.&lt;br /&gt;
Aug 18 15:26:58 &amp;lt;DanC&amp;gt;	I expect they'll use datetime fields for different things that have different cost/benefit trade-offs&lt;br /&gt;
Aug 18 15:27:26 &amp;lt;DanC&amp;gt;	do you know of any particular differences that matter to anybody?&lt;br /&gt;
Aug 18 15:56:43 &amp;lt;KragenSitaker&amp;gt;	RFC3339 suggests -07:00, which seems like an improvement over -0700 anyway&lt;br /&gt;
Aug 18 15:56:49 &amp;lt;Tantek&amp;gt;	Kragen, agreed&lt;br /&gt;
Aug 18 15:57:01 &amp;lt;Tantek&amp;gt;	RFC3339 is certainly preferable to the ISO8601 subset in iCalendar&lt;br /&gt;
Aug 18 16:05:57 &amp;lt;DanC&amp;gt;	Tantek's right, Kragen; iCalendar looks like it solves the local timezone problem but doesn't.&lt;br /&gt;
Aug 18 16:06:14 &amp;lt;DanC&amp;gt;	and it's true that there's no standard solution to the local timezone problem&lt;br /&gt;
Aug 18 16:06:39 &amp;lt;Tantek&amp;gt;	so instead of appearing to solve the problem but not solving it, we chose to provide the ability to *approximate* the local timezone using e.g. &amp;quot;-07:00&amp;quot;&lt;br /&gt;
Aug 18 16:06:49 &amp;lt;DanC&amp;gt;	the simplest thing is to have people use Z time in hCalendar. But I gather that's unacceptably unusable?&lt;br /&gt;
Aug 18 16:07:35 &amp;lt;Tantek&amp;gt;	DanC, yes, the simplest thing is to have everyone use UTC Z&lt;br /&gt;
Aug 18 16:07:38 &amp;lt;Tantek&amp;gt;	However&lt;br /&gt;
Aug 18 16:07:50 &amp;lt;Tantek&amp;gt;	it is not *nearly* as usuable/verifiable&lt;br /&gt;
Aug 18 16:07:55 &amp;lt;Tantek&amp;gt;	as -07:00 etc.&lt;br /&gt;
Aug 18 16:08:02 &amp;lt;Tantek&amp;gt;	hence the decision to go with the latter&lt;br /&gt;
Aug 18 16:08:12 &amp;lt;Tantek&amp;gt;	some degree of human verifiability is important here&lt;br /&gt;
Aug 18 16:14:21 &amp;lt;Tantek&amp;gt;	DanC, my perception is that RFC3339 is a subset&lt;br /&gt;
Aug 18 16:17:00 &amp;lt;DanC&amp;gt;	   time-numoffset  = (&amp;quot;+&amp;quot; / &amp;quot;-&amp;quot;) time-hour &amp;quot;:&amp;quot; time-minute&lt;br /&gt;
Aug 18 16:17:34 &amp;lt;DanC&amp;gt;	ok, then I can't see any differences. (modulo recent leap seconds issues that may affect xsd:dateTime )&lt;br /&gt;
Aug 18 16:18:07 &amp;lt;Tantek&amp;gt;	would be interesting to know why Atom 1.0 chose RFC3339 over xsd:dateTime&lt;br /&gt;
Aug 18 16:18:21 &amp;lt;Tantek&amp;gt;	if there was a &amp;quot;real&amp;quot; reason or if it was arbitrary / coin-flip.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's an exhaustive [http://www.imc.org/atom-syntax/mail-archive/msg13103.html comparison] from ndw. I think xsd:dateTime also allows unqualified local times, while RFC3339 allows only UTC with no known timezone (-00:00). In the end, Atompub followed the advice of [http://www.imc.org/atom-syntax/mail-archive/msg13244.html Sam Ruby] and [http://www.imc.org/atom-syntax/mail-archive/msg13248.html Scott Hollenbeck] our area director. Atom dates make some additional restrictions on RFC3339, such as uppercase T and Z characters for compatibility with xsd:dateTime, RFC3339, W3C-DTF, and ISO8601. [http://franklinmint.fm Robert Sayre]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 16:18:43 &amp;lt;KragenSitaker&amp;gt;	rfc3339 is pretty short.&lt;br /&gt;
Aug 18 16:19:36 &amp;lt;Tantek&amp;gt;	DanC, BTW, which came first? REC for xsd:dateTime or RFC3339?&lt;br /&gt;
Aug 18 16:19:50 &amp;lt;DanC&amp;gt;	RFC3339 is dated July 2002 ...&lt;br /&gt;
Aug 18 16:19:54 &amp;lt;KragenSitaker&amp;gt;	Right --- and you might be able to understand xsd:dateTime without reading all of xml schema, you wouldn't be confident of it&lt;br /&gt;
Aug 18 16:20:25 &amp;lt;DanC&amp;gt;	W3C Recommendation 28 October 2004 ... but that's 2nd ed...&lt;br /&gt;
Aug 18 16:20:47 &amp;lt;DanC&amp;gt;	W3C Recommendation 02 May 2001&lt;br /&gt;
Aug 18 16:22:10 &amp;lt;DanC&amp;gt;	I don't see a BNF in http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime ...&lt;br /&gt;
Aug 18 16:22:43 &amp;lt;KragenSitaker&amp;gt;	yeah, appendix D of the current xml schema datatypes document seems a little scanty, actually&lt;br /&gt;
Aug 18 16:23:28 &amp;lt;DanC&amp;gt;	ah... 2nd ed of http://www.w3.org/TR/xmlschema-2/#date is much more explicit about syntax.&lt;br /&gt;
Aug 18 16:23:30 &amp;lt;KragenSitaker&amp;gt;	it's 1100 words but still doesn't give any examples&lt;br /&gt;
Aug 18 16:23:35 &amp;lt;DanC&amp;gt;	still, it's given in prose and not BNF&lt;br /&gt;
Aug 18 16:24:17 &amp;lt;KragenSitaker&amp;gt;	sections 3.2.9 through 3.2.14 seem to be the relevant ones around #date&lt;br /&gt;
Aug 18 16:24:29 &amp;lt;KragenSitaker&amp;gt;	which is another 2200 words&lt;br /&gt;
Aug 18 16:24:42 &amp;lt;DanC&amp;gt;	wow... they changed the canonical form of date from always-Z to timezone-allowed between 1st edition and 2nd edition&lt;br /&gt;
Aug 18 16:25:01 &amp;lt;Tantek&amp;gt;	Kragen, DanC, these are very good analyses&lt;br /&gt;
Aug 18 16:25:21 &amp;lt;Tantek&amp;gt;	could I ask you to summarize the pros/cons for each in a new section at end of http://microformats.org/wiki/datetime-design-pattern&lt;br /&gt;
Aug 18 16:25:22 &amp;lt;Tantek&amp;gt;	?&lt;br /&gt;
Aug 18 16:25:58 &amp;lt;KragenSitaker&amp;gt;	rfc 3339 is 4000 words, excluding the last two pages of boilerplate.&lt;br /&gt;
Aug 18 16:26:31 &amp;lt;KragenSitaker&amp;gt;	so it's actually longer than the datetime-relevant parts of XSD but it seems much more rigorous and clear&lt;br /&gt;
Aug 18 16:28:37 &amp;lt;DanC&amp;gt;	my advice is: normatively cite both, and claim they specify the same syntax, and let anybody who discovers otherwise send you a bug report with a test case&lt;br /&gt;
Aug 18 16:29:12 &amp;lt;KragenSitaker&amp;gt;	danc: nice hack&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The RFC3339 has a mandatory TIME portion of the DATE-TIME. Some vCard/iCalendar DATE-TIME stamps can omit the TIME. For instance, DTSTART, if that is a full day event, then you can omit the time. BDAY in vCard can be respresented by only a DATE. I like the idea of restricting the possible date formats, but i think that TIME should be optional, which it isn't in RFC3339. - [http://suda.co.uk/ brian suda]&lt;br /&gt;
&lt;br /&gt;
RFC 3339 allows lowercase 't' and 'z' while XSD doesn't. Specifying RFC 3339 plus 'T' and 'Z' MUST be caps will make them the same. - [http://bitworking.org Joe Gregorio]&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
	<entry>
		<id>http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1585</id>
		<title>datetime-design-pattern</title>
		<link rel="alternate" type="text/html" href="http://microformats.org/wiki/index.php?title=datetime-design-pattern&amp;diff=1585"/>
		<updated>2005-09-05T17:42:19Z</updated>

		<summary type="html">&lt;p&gt;RobertSayre: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This page is a draft.'''&lt;br /&gt;
&lt;br /&gt;
This is a page for exploring a datetime design pattern.&lt;br /&gt;
&lt;br /&gt;
The pattern which is now used in [[hcalendar|hCalendar]] and [[hreview|hReview]] is something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt;abbr class=&amp;quot;foo&amp;quot; title=&amp;quot;YYYYMMDDTHH:MM:SS+ZZZZ&amp;quot;&amp;amp;gt;Date Time&amp;amp;lt;/abbr&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where foo is the semantic classname which is being applied to this date/time, the title of the &amp;amp;lt;abbr&amp;amp;gt; is an ISO 8601 date/time and &amp;quot;Date Time&amp;quot; is a human-friendly representation of the same date/time.&lt;br /&gt;
&lt;br /&gt;
This pattern is likely to be highly resuable.&lt;br /&gt;
&lt;br /&gt;
--[[RyanKing]]&lt;br /&gt;
&lt;br /&gt;
Can this not be viewed as a microformat in itself?&lt;br /&gt;
&lt;br /&gt;
--[[DimitriGlazkov]]&lt;br /&gt;
&lt;br /&gt;
It could, but inventing a microformat for the sake of inventing a microformat is against the microformat principles.&lt;br /&gt;
If there is a specific real world problem (and uses cases) that such an elemental microformat would solve, then it would be worth considering.&lt;br /&gt;
&lt;br /&gt;
Until then it is best to keep the &amp;amp;lt;abbr&amp;amp;gt; datetime concept merely as a microformat design pattern, to be used in _actual_ microformats that have a demonstrated practical need.&lt;br /&gt;
&lt;br /&gt;
-- [http://tantek.com/log/ Tantek]&lt;br /&gt;
&lt;br /&gt;
Excerpt from #microformats Aug 18th. Please edit!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 15:16:14 &amp;lt;Tantek&amp;gt;	DanC, what do you think of RFC3339?&lt;br /&gt;
Aug 18 15:17:14 &amp;lt;Tantek&amp;gt;	ISO8601 subset&lt;br /&gt;
Aug 18 15:17:19 &amp;lt;DanC&amp;gt;	Date and Time on the Internet: Timestamps http://www.ietf.org/rfc/rfc3339.txt&lt;br /&gt;
Aug 18 15:17:30 &amp;lt;DanC&amp;gt;	Klyne is a good guy. I wonder if I talked with him about this.&lt;br /&gt;
Aug 18 15:17:32 &amp;lt;Tantek&amp;gt;	compat with W3C-NOTE-DATETIME&lt;br /&gt;
Aug 18 15:17:50 &amp;lt;Tantek&amp;gt;	compat with xsd:dateTime&lt;br /&gt;
Aug 18 15:17:57 &amp;lt;Tantek&amp;gt;	it's a strict intersection subset&lt;br /&gt;
Aug 18 15:17:59 &amp;lt;DanC&amp;gt;	I consider W3C-NOTE-DATETIME obsoleted by XML Schema datatype-- yeah.. xsd:dateTime&lt;br /&gt;
Aug 18 15:18:32 &amp;lt;Tantek&amp;gt;	compare/contrast normatively using xsd:dateTime vs. RFC3339&lt;br /&gt;
Aug 18 15:18:41 &amp;lt;Tantek&amp;gt;	note: Atom 1.0 chose RFC3339&lt;br /&gt;
Aug 18 15:18:50 &amp;lt;Tantek&amp;gt;	i would like input from the microformats community on this&lt;br /&gt;
Aug 18 15:19:27 &amp;lt;DanC&amp;gt;	in what context are you evaluating RFC 3339?&lt;br /&gt;
Aug 18 15:19:28 &amp;lt;jcgregorio&amp;gt;	http://bitworking.org/news/Date_Constructs_in_the_Atom_Syndication_Format&lt;br /&gt;
Aug 18 15:21:24 &amp;lt;DanC&amp;gt;	which microformat is the question coming from, Tantek ?&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	&amp;quot;   The grammar element time-second may have the value &amp;quot;60&amp;quot; at the end of&lt;br /&gt;
Aug 18 15:23:31 &amp;lt;DanC&amp;gt;	   months in which a leap second occurs&amp;quot; The XML Schema WG is in the 27th level of leap-second-hell for the past few months, I gather.&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	yeah... here's the scary bit: &amp;quot;   Leap seconds cannot be predicted far into the future.  The&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   International Earth Rotation Service publishes bulletins [IERS] that&lt;br /&gt;
Aug 18 15:24:21 &amp;lt;DanC&amp;gt;	   announce leap seconds with a few weeks' warning.&amp;quot;&lt;br /&gt;
Aug 18 15:26:03 &amp;lt;Tantek&amp;gt;	DanC, which microformats? any/all that use datetime fields.&lt;br /&gt;
Aug 18 15:26:36 &amp;lt;DanC&amp;gt;	hard to give useful advice, then.&lt;br /&gt;
Aug 18 15:26:58 &amp;lt;DanC&amp;gt;	I expect they'll use datetime fields for different things that have different cost/benefit trade-offs&lt;br /&gt;
Aug 18 15:27:26 &amp;lt;DanC&amp;gt;	do you know of any particular differences that matter to anybody?&lt;br /&gt;
Aug 18 15:56:43 &amp;lt;KragenSitaker&amp;gt;	RFC3339 suggests -07:00, which seems like an improvement over -0700 anyway&lt;br /&gt;
Aug 18 15:56:49 &amp;lt;Tantek&amp;gt;	Kragen, agreed&lt;br /&gt;
Aug 18 15:57:01 &amp;lt;Tantek&amp;gt;	RFC3339 is certainly preferable to the ISO8601 subset in iCalendar&lt;br /&gt;
Aug 18 16:05:57 &amp;lt;DanC&amp;gt;	Tantek's right, Kragen; iCalendar looks like it solves the local timezone problem but doesn't.&lt;br /&gt;
Aug 18 16:06:14 &amp;lt;DanC&amp;gt;	and it's true that there's no standard solution to the local timezone problem&lt;br /&gt;
Aug 18 16:06:39 &amp;lt;Tantek&amp;gt;	so instead of appearing to solve the problem but not solving it, we chose to provide the ability to *approximate* the local timezone using e.g. &amp;quot;-07:00&amp;quot;&lt;br /&gt;
Aug 18 16:06:49 &amp;lt;DanC&amp;gt;	the simplest thing is to have people use Z time in hCalendar. But I gather that's unacceptably unusable?&lt;br /&gt;
Aug 18 16:07:35 &amp;lt;Tantek&amp;gt;	DanC, yes, the simplest thing is to have everyone use UTC Z&lt;br /&gt;
Aug 18 16:07:38 &amp;lt;Tantek&amp;gt;	However&lt;br /&gt;
Aug 18 16:07:50 &amp;lt;Tantek&amp;gt;	it is not *nearly* as usuable/verifiable&lt;br /&gt;
Aug 18 16:07:55 &amp;lt;Tantek&amp;gt;	as -07:00 etc.&lt;br /&gt;
Aug 18 16:08:02 &amp;lt;Tantek&amp;gt;	hence the decision to go with the latter&lt;br /&gt;
Aug 18 16:08:12 &amp;lt;Tantek&amp;gt;	some degree of human verifiability is important here&lt;br /&gt;
Aug 18 16:14:21 &amp;lt;Tantek&amp;gt;	DanC, my perception is that RFC3339 is a subset&lt;br /&gt;
Aug 18 16:17:00 &amp;lt;DanC&amp;gt;	   time-numoffset  = (&amp;quot;+&amp;quot; / &amp;quot;-&amp;quot;) time-hour &amp;quot;:&amp;quot; time-minute&lt;br /&gt;
Aug 18 16:17:34 &amp;lt;DanC&amp;gt;	ok, then I can't see any differences. (modulo recent leap seconds issues that may affect xsd:dateTime )&lt;br /&gt;
Aug 18 16:18:07 &amp;lt;Tantek&amp;gt;	would be interesting to know why Atom 1.0 chose RFC3339 over xsd:dateTime&lt;br /&gt;
Aug 18 16:18:21 &amp;lt;Tantek&amp;gt;	if there was a &amp;quot;real&amp;quot; reason or if it was arbitrary / coin-flip.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's an exhaustive [http://www.imc.org/atom-syntax/mail-archive/msg13103.html comparison] from ndw. I think xsd:dateTime also allows unqualified local times, while RFC3339 allows only UTC with no known timezone (-00:00). In the end, Atompub followed the advice of [http://www.imc.org/atom-syntax/mail-archive/msg13244.html Sam Ruby] and [http://www.imc.org/atom-syntax/mail-archive/msg13248.html Scott Hollenbeck] our area director. Atom dates make some additional restrictions on RFC3339, such as uppercase T and Z characters for compatibility with xsd:dateTime, RFC3339, W3C-DTF, and ISO8601.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Aug 18 16:18:43 &amp;lt;KragenSitaker&amp;gt;	rfc3339 is pretty short.&lt;br /&gt;
Aug 18 16:19:36 &amp;lt;Tantek&amp;gt;	DanC, BTW, which came first? REC for xsd:dateTime or RFC3339?&lt;br /&gt;
Aug 18 16:19:50 &amp;lt;DanC&amp;gt;	RFC3339 is dated July 2002 ...&lt;br /&gt;
Aug 18 16:19:54 &amp;lt;KragenSitaker&amp;gt;	Right --- and you might be able to understand xsd:dateTime without reading all of xml schema, you wouldn't be confident of it&lt;br /&gt;
Aug 18 16:20:25 &amp;lt;DanC&amp;gt;	W3C Recommendation 28 October 2004 ... but that's 2nd ed...&lt;br /&gt;
Aug 18 16:20:47 &amp;lt;DanC&amp;gt;	W3C Recommendation 02 May 2001&lt;br /&gt;
Aug 18 16:22:10 &amp;lt;DanC&amp;gt;	I don't see a BNF in http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime ...&lt;br /&gt;
Aug 18 16:22:43 &amp;lt;KragenSitaker&amp;gt;	yeah, appendix D of the current xml schema datatypes document seems a little scanty, actually&lt;br /&gt;
Aug 18 16:23:28 &amp;lt;DanC&amp;gt;	ah... 2nd ed of http://www.w3.org/TR/xmlschema-2/#date is much more explicit about syntax.&lt;br /&gt;
Aug 18 16:23:30 &amp;lt;KragenSitaker&amp;gt;	it's 1100 words but still doesn't give any examples&lt;br /&gt;
Aug 18 16:23:35 &amp;lt;DanC&amp;gt;	still, it's given in prose and not BNF&lt;br /&gt;
Aug 18 16:24:17 &amp;lt;KragenSitaker&amp;gt;	sections 3.2.9 through 3.2.14 seem to be the relevant ones around #date&lt;br /&gt;
Aug 18 16:24:29 &amp;lt;KragenSitaker&amp;gt;	which is another 2200 words&lt;br /&gt;
Aug 18 16:24:42 &amp;lt;DanC&amp;gt;	wow... they changed the canonical form of date from always-Z to timezone-allowed between 1st edition and 2nd edition&lt;br /&gt;
Aug 18 16:25:01 &amp;lt;Tantek&amp;gt;	Kragen, DanC, these are very good analyses&lt;br /&gt;
Aug 18 16:25:21 &amp;lt;Tantek&amp;gt;	could I ask you to summarize the pros/cons for each in a new section at end of http://microformats.org/wiki/datetime-design-pattern&lt;br /&gt;
Aug 18 16:25:22 &amp;lt;Tantek&amp;gt;	?&lt;br /&gt;
Aug 18 16:25:58 &amp;lt;KragenSitaker&amp;gt;	rfc 3339 is 4000 words, excluding the last two pages of boilerplate.&lt;br /&gt;
Aug 18 16:26:31 &amp;lt;KragenSitaker&amp;gt;	so it's actually longer than the datetime-relevant parts of XSD but it seems much more rigorous and clear&lt;br /&gt;
Aug 18 16:28:37 &amp;lt;DanC&amp;gt;	my advice is: normatively cite both, and claim they specify the same syntax, and let anybody who discovers otherwise send you a bug report with a test case&lt;br /&gt;
Aug 18 16:29:12 &amp;lt;KragenSitaker&amp;gt;	danc: nice hack&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The RFC3339 has a mandatory TIME portion of the DATE-TIME. Some vCard/iCalendar DATE-TIME stamps can omit the TIME. For instance, DTSTART, if that is a full day event, then you can omit the time. BDAY in vCard can be respresented by only a DATE. I like the idea of restricting the possible date formats, but i think that TIME should be optional, which it isn't in RFC3339. - [http://suda.co.uk/ brian suda]&lt;br /&gt;
&lt;br /&gt;
RFC 3339 allows lowercase 't' and 'z' while XSD doesn't. Specifying RFC 3339 plus 'T' and 'Z' MUST be caps will make them the same. - [http://bitworking.org Joe Gregorio]&lt;/div&gt;</summary>
		<author><name>RobertSayre</name></author>
	</entry>
</feed>