xoxo-sample-code-java
Jump to navigation
Jump to search
XOXO Sample Code - Java
- this is sub-page of xoxo-sample-code
XOXOWriter.java
/*
* Copyright 2005 Robert Sayre
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Portions of this code are derived from the Apache-licensed Python XOXO
* module by Kevin Marks. <http://microformats.org/wiki/xoxo-sample-code>
*/
package org.atompub.draft.xoxo;
import java.util.*;
public class XOXOWriter {
public String[] attrs = {"title","rel","type"};
public String makeXOXO(List struct, String className){
return makeXOXO(struct, className, 0, true);
}
public String makeXOXO(List struct, String className,
boolean doNSDeclaration){
return makeXOXO(struct, className, 0, doNSDeclaration);
}
public String makeXOXO(List struct){
return makeXOXO(struct, "xoxo", 0, true);
}
public String makeXOXO(Object struct, int depth){
return makeXOXO(struct, null, 0, false);
}
public String makeXOXO(Object struct, String className,
int depth, boolean doNSDeclaration){
if(struct == null) return "";
StringBuffer sb = new StringBuffer();
if(struct instanceof Object[]){
struct = Arrays.asList((Object[]) struct);
}
if(struct instanceof List){
sb.append("<ol");
if(doNSDeclaration)
sb.append(" xmlns=\"http://www.w3.org/1999/xhtml\"");
if(className != null){
sb.append(" class=\"");
sb.append(className);
sb.append("\"");
}
sb.append(">");
}
if(struct instanceof Map){
Map d = new LinkedHashMap((Map) struct);
if(d.containsKey("url")){
sb.append("<a href=\"" + d.get("url") + "\" ");
Object text;
if(d.containsKey("text")){
text = d.get("text");
}else if(d.containsKey("title")){
text = d.get("title");
}else{
text = d.get("url");
}
for(int i=0; i<attrs.length; i++){
String xVal = makeXOXO(d.get(attrs[i]),depth+1);
if(xVal != null && !xVal.equals("")){
sb.append(attrs[i] + "=\"" + xVal + "\" ");
}
d.remove(attrs[i]);
}
sb.append(">" + makeXOXO(text, depth+1) + "</a>");
d.remove("text");
d.remove("url");
}
if(!d.isEmpty()){
sb.append("<dl>");
for(Iterator i = d.entrySet().iterator(); i.hasNext();){
Map.Entry entry = (Map.Entry)i.next();
String ddVal = makeXOXO(entry.getValue(),depth+1);
sb.append("<dt>" + entry.getKey() + "</dt>");
sb.append("<dd>" + ddVal + "</dd>");
}
sb.append("</dl>");
}
}else if(struct instanceof List){
List l = (List) struct;
for(Iterator i = l.iterator(); i.hasNext();){
Object item = i.next();
sb.append("<li>" + makeXOXO(item,depth+1) + "</li>");
}
sb.append("</ol>");
}else{
sb.append(struct);
}
return sb.toString();
}
public String toXOXO(List struct){
return toXOXO(struct, false, null);
}
public String toXOXO(Object struct){
List alist = new ArrayList();
alist.add(struct);
return toXOXO(alist);
}
public String toXOXO(Object struct,
boolean addHTMLWrapper,
String cssUrl){
List alist = new ArrayList();
alist.add(struct);
return toXOXO(alist, addHTMLWrapper, cssUrl);
}
public String toXOXO(List struct,
boolean addHTMLWrapper,
String cssUrl){
String startHTML = "<!DOCTYPE html PUBLIC \"-//W3C//DTD"
+ "XHTML 1.0 Transitional//EN\n"
+ "http://www.w3.org/TR/xhtml1/DTD/"
+ "xhtml1-transitional.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head>";
if(addHTMLWrapper){
String s = startHTML;
if(cssUrl != null){
s += "<style type=\"text/css\">@import \""
+ cssUrl + "\";</style>";
}
s += "</head><body>" + makeXOXO(struct, "xoxo", false)
+ "</body></html>";
return s;
}else{
return makeXOXO(struct, "xoxo");
}
}
}
XOXOParser.java
This needs some small additions to handle the XHTML DTD and named character entities.
/*
* Copyright 2005 Robert Sayre
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Portions of this code are derived from the Apache-licensed Python XOXO
* module by Kevin Marks. <http://microformats.org/wiki/xoxo-sample-code>
*/
package org.atompub.draft.xoxo;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;
import java.util.*;
import java.io.InputStream;
import java.io.StringReader;
import java.io.IOException;
public class XOXOParser extends DefaultHandler {
protected String XHTML_NS = "http://www.w3.org/1999/xhtml";
protected List elStack;
protected Map listEls;
public List structs;
public List xoStack;
public List textStack;
public XOXOParser() {
reset();
}
protected void pushStruct(Object struct){
if(struct instanceof Map && !((Map) struct).isEmpty()
&& structs.get(structs.size()-1) instanceof Map
&& ((Map) struct).containsKey("url")){
// put back the <a>-made one for extra defs
xoStack.add(structs.get(structs.size()-1));
}else{
structs.add(struct);
xoStack.add(struct);
}
}
public void startElement(String nsUri, String localName,
String qName, Attributes atts){
// bounce non-XHTML elements
if(nsUri.equals(XHTML_NS)){
elStack.add(localName);
}else{
elStack.add("foo");
return;
}
if(localName.equals("a")){
Map attmap = new LinkedHashMap();
int len = atts.getLength();
for(int i=0; i<len; i++){
attmap.put(atts.getQName(i),atts.getValue(i));
}
if(attmap.containsKey("href")){
attmap.put("url",attmap.get("href"));
attmap.remove("href");
}
pushStruct(attmap);
textStack.add("");
}else if(localName.equals("dl")){
pushStruct(new LinkedHashMap());
}else if(localName.equals("ol")){
pushStruct(new ArrayList());
}else if(localName.equals("ul")){
pushStruct(new ArrayList());
}else if(localName.equals("li")){
textStack.add("");
}else if(localName.equals("dt")){
textStack.add("");
}else if(localName.equals("dd")){
textStack.add("");
}
}
public void endElement(String nsUri, String localName,
String qName){
elStack.remove(elStack.size()-1);
// bounce non-XHTML elements
if(nsUri != XHTML_NS){
return;
}
if(localName.equals("a")){
String val = (String) textStack.remove(textStack.size()-1);
if (val.length() > 0){
Map defs = (Map) xoStack.get(xoStack.size()-1);
String defVal = (String) defs.get("title");
if(defVal != null && val.equals(defVal)){
val = "";
}
defVal = (String) defs.get("url");
if(defVal != null && val.equals(defVal)){
val = "";
}
if(val.length() > 0){
defs.put("text",val);
}
}
xoStack.remove(xoStack.size()-1);
}else if(localName.equals("dl")){
xoStack.remove(xoStack.size()-1);
}else if(localName.equals("ol")){
xoStack.remove(xoStack.size()-1);
}else if(localName.equals("ul")){
xoStack.remove(xoStack.size()-1);
}else if(localName.equals("li")){
Object val = textStack.remove(textStack.size()-1);
List last = (List) xoStack.get(xoStack.size()-1);
if(structs.get(structs.size()-1) != last){
val = structs.remove(structs.size()-1);
}
last.add(val);
}else if(localName.equals("dd")){
Object val = textStack.remove(textStack.size()-1);
Object key = textStack.remove(textStack.size()-1);
Map last = (Map) xoStack.get(xoStack.size()-1);
if(structs.get(structs.size()-1) != last){
val = structs.remove(structs.size()-1);
}
last.put(key,val);
}
}
public void characters(char[] ch, int start, int length){
if(!xoStack.isEmpty()
&& !listEls.containsKey(elStack.get(elStack.size()-1))){
String text = (String) textStack.get(textStack.size()-1);
String test = new String(ch,start,length);
textStack.set(textStack.size()-1,text+test);
}
}
public Object parse(String s) throws SAXException, IOException{
return parse(new InputSource(new StringReader(s)));
}
public Object parse(InputStream is) throws SAXException, IOException {
return parse(new InputSource(is));
}
public Object parse(InputSource in) throws SAXException, IOException {
XMLReader parser = XMLReaderFactory.createXMLReader();
parser.setContentHandler(this);
parser.parse(in);
List returnList = new ArrayList();
for(Iterator i = this.structs.iterator(); i.hasNext();){
Object thing = i.next();
if(thing != null){
returnList.add(thing);
}
}
while(returnList.size()==1){
if(returnList.get(0) instanceof List){
returnList = (List) returnList.get(0);
}else{
reset();
return returnList.get(0);
}
}
reset();
return returnList;
}
protected void reset(){
elStack = new ArrayList();
listEls = new HashMap();
structs = new ArrayList();
xoStack = new ArrayList();
textStack = new ArrayList();
listEls.put("ol","ol");
listEls.put("ul","ul");
listEls.put("dl","dl");
}
}
XOXOTest.java
/*
* Copyright 2005 Robert Sayre
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Portions of this code are derived from the Apache-licensed Python XOXO
* module by Kevin Marks. <http://microformats.org/wiki/xoxo-sample-code>
*/
package org.atompub.draft.xoxo.tests;
import junit.framework.TestSuite;
import junit.framework.TestCase;
import junit.textui.TestRunner;
import org.atompub.draft.xoxo.XOXOWriter;
import org.atompub.draft.xoxo.XOXOParser;
import java.util.*;
public class XOXOTest extends TestCase {
public static void main(String[] args) {
new TestRunner().doRun(new TestSuite(XOXOTest.class));
}
String XHTML_DEC = "xmlns=\"http://www.w3.org/1999/xhtml\" ";
public String simpleListHTML = "<ol "
+ XHTML_DEC
+ "class=\"xoxo\">"
+ "<li>1</li><li>2</li><li>3</li></ol>";
public void testSimpleList(){
String [] numbers = {"1","2","3"};
XOXOWriter xoxo = new XOXOWriter();
assertEquals(simpleListHTML,
xoxo.toXOXO(Arrays.asList(numbers)));
}
public void testStringIntegerList(){
Object[] numbers = {new Integer(1),"2","3"};
XOXOWriter xoxo = new XOXOWriter();
assertEquals(simpleListHTML,
xoxo.toXOXO(Arrays.asList(numbers)));
}
public String nestedListHTML = "<ol "
+ XHTML_DEC
+ "class=\"xoxo\"><li>1</li><li>"
+ "<ol><li>2</li><li>3</li></ol></li></ol>";
public void testNestedList(){
Object[] arr = {"2","3"};
Object[] nested = {"1",Arrays.asList(arr)};
XOXOWriter xoxo = new XOXOWriter();
assertEquals(nestedListHTML,
xoxo.toXOXO(Arrays.asList(nested)));
}
public void testNestedArray(){
Object[] arr = {"2","3"};
Object[] nested = {"1",arr};
XOXOWriter xoxo = new XOXOWriter();
assertEquals(nestedListHTML,
xoxo.toXOXO(Arrays.asList(nested)));
}
public String dictHTML = "<ol "
+ XHTML_DEC
+ "class=\"xoxo\">"
+ "<li><dl><dt>test</dt><dd>1</dd><dt>name</dt>"
+ "<dd>Kevin</dd></dl></li></ol>";
public void testDictionary(){
Map dict = new LinkedHashMap();
dict.put("test", new Integer(1));
dict.put("name", "Kevin");
XOXOWriter xoxo = new XOXOWriter();
assertEquals(dictHTML,
xoxo.toXOXO(dict));
}
public String singleHTML = "<ol "
+ XHTML_DEC
+ "class=\"xoxo\">"
+ "<li>test</li></ol>";
public void testSingleItem(){
String item = "test";
XOXOWriter xoxo = new XOXOWriter();
assertEquals(singleHTML,
xoxo.toXOXO(item));
}
public void testWrapDiffers(){
String item = "test";
XOXOWriter xoxo = new XOXOWriter();
String nowrap = xoxo.toXOXO(item);
Object[] itemArr = {item};
String wrap = xoxo.toXOXO(Arrays.asList(itemArr),true,null);
assertFalse(wrap.equals(nowrap));
}
String startHTML = "<!DOCTYPE html PUBLIC \"-//W3C//DTD"
+ "XHTML 1.0 Transitional//EN\n"
+ "http://www.w3.org/TR/xhtml1/DTD/"
+ "xhtml1-transitional.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head></head><body>";
public String singleWrapHTML = "<ol "
+ "class=\"xoxo\">"
+ "<li>test</li></ol>";
public String endHTML = "</body></html>";
public void testWrapSingleItem(){
String item = "test";
XOXOWriter xoxo = new XOXOWriter();
assertEquals(startHTML + singleWrapHTML + endHTML,
xoxo.toXOXO(item,true,null));
}
public void testXOXOParser(){
XOXOParser parser = new XOXOParser();
try{
parser.parse(dictHTML);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testDictRoundTrip(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("test", "1");
dict.put("name", "Kevin");
String html = xoxo.toXOXO(dict);
try{
Object newDict = parser.parse(html);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testListRoundTrip(){
Object[] obj = {"1","2","3"};
List testList = Arrays.asList(obj);
XOXOWriter xoxo = new XOXOWriter();
String html = xoxo.toXOXO(testList);
XOXOParser parser = new XOXOParser();
try{
Object newList = parser.parse(html);
assertEquals(testList,newList);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testListOfDictsRoundTrip(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("test", "1");
dict.put("name", "Kevin");
Map dict2 = new LinkedHashMap();
dict2.put("one", "two");
dict2.put("three", "four");
dict2.put("five", "six");
Object[] obj = {"1",dict,dict2};
List testList = Arrays.asList(obj);
String html = xoxo.toXOXO(testList);
try{
Object newList = parser.parse(html);
assertEquals(testList,newList);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testListOfListsRoundTrip(){
Object[] list1 = {"1","2","3"};
Object[] list2 = {"4","5","6", Arrays.asList(list1)};
Object[] list3 = {"7", Arrays.asList(list2)};
Object[] list4 = {"8", Arrays.asList(list3)};
List testList = Arrays.asList(list4);
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
String html = xoxo.toXOXO(testList);
try{
Object newList = parser.parse(html);
assertEquals(testList,newList);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testDictOfListsRoundTrip(){
Object[] list1 = {"1","2","3"};
Object[] list2 = {"4","5","6"};
Object[] list3 = {"7"};
Object[] list4 = {"8", "9"};
Map dict = new LinkedHashMap();
dict.put("foo", Arrays.asList(list1));
dict.put("bar", Arrays.asList(list2));
dict.put("baz", Arrays.asList(list3));
dict.put("qux", Arrays.asList(list4));
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
String html = xoxo.toXOXO(dict);
try{
Object newDict = parser.parse(html);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public String junkXOXO = "<ol "
+ XHTML_DEC
+ "class=\"xoxo\">"
+ "bad<li><dl>worse<dt>good</dt><dd>buy</dd> now</dl></li></ol>";
public void testXOXOJunkInContainers(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("good","buy");
try{
Object newDict = parser.parse(junkXOXO);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public String junkElementXOXO = "<ol "
+ XHTML_DEC
+ "><li>bad<dl><dt>good</dt><dd>buy</dd></dl>"
+ "worse</li><li>bag<ol><li>OK</li></ol>fish</li></ol>";
public void testXOXOjunkInElements(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("good","buy");
Object[] ok = {"OK"};
Object[] obj ={dict, Arrays.asList(ok)};
List testList = Arrays.asList(obj);
try{
Object newList = parser.parse(junkElementXOXO);
assertEquals(testList,newList);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public String xoxoSpacesNewlines = "<ol " + XHTML_DEC +
" class='xoxo'> \n" +
" <li>\n" +
" <dl>\n" +
" <dt>text</dt>\n" +
" <dd>item 1</dd>\n" +
" <dt>description</dt>\n" +
" <dd> This item represents the main" +
" point we're trying to make.</dd>\n" +
" <dt>url</dt>\n" +
" <dd>http://example.com/more.xoxo</dd>\n" +
" <dt>title</dt>\n" +
" <dd>title of item 1</dd>\n" +
" <dt>type</dt>\n" +
" <dd>text/xml</dd>\n" +
" <dt>rel</dt>\n" +
" <dd>help</dd>\n" +
" </dl>\n" +
" </li>\n" +
"</ol>";
public void testXOXOWithSpacesAndNewlines(){
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("text","item 1");
dict.put("description"," This item represents the main" +
" point we're trying to make.");
dict.put("url","http://example.com/more.xoxo");
dict.put("title","title of item 1");
dict.put("type","text/xml");
dict.put("rel","help");
try{
Object newDict = parser.parse(xoxoSpacesNewlines);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public String xoxoSample = "<ol " + XHTML_DEC +
" class='xoxo'> \n" +
" <li>\n" +
" <dl>\n" +
" <dt>text</dt>\n" +
" <dd>item 1</dd>\n" +
" <dt>url</dt>\n" +
" <dd>http://example.com/more.xoxo</dd>\n" +
" <dt>title</dt>\n" +
" <dd>title of item 1</dd>\n" +
" <dt>type</dt>\n" +
" <dd>text/xml</dd>\n" +
" <dt>rel</dt>\n" +
" <dd>help</dd>\n" +
" </dl>\n" +
" </li>\n" +
"</ol>";
public String smartXOXOSample = "<ol " + XHTML_DEC +
"class=\"xoxo\"> \n" +
" <li><a href=\"http://example.com/more.xoxo\"\n" +
" title=\"title of item 1\"\n" +
" type=\"text/xml\"\n" +
" rel=\"help\">item 1</a> \n" +
"<!-- note how the \"text\" property is simply" +
" the contents of the <a> element -->\n" +
" </li>\n" +
"</ol>";
public void testSpecialAttributeDecoding(){
XOXOParser parser = new XOXOParser();
try{
Object xoxoDict = parser.parse(xoxoSample);
Object xoxoDict2 = parser.parse(smartXOXOSample);
assertEquals(xoxoDict,xoxoDict2);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public String specialAttrHTML = "<ol " + XHTML_DEC +
"class=\"xoxo\">" +
"<li><a href=\"http://example.com/more.xoxo\" title=\"sample url\" " +
"rel=\"help\" type=\"text/xml\" >an example</a></li></ol>";
public void testSpecialAttributeEncode(){
XOXOWriter xoxo = new XOXOWriter();
Map dict = new LinkedHashMap();
dict.put("url","http://example.com/more.xoxo");
dict.put("title","sample url");
dict.put("type","text/xml");
dict.put("rel","help");
dict.put("text","an example");
String html = xoxo.toXOXO(dict);
assertEquals(specialAttrHTML,html);
}
public void testSpecialAttributeRoundTripFull(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("url","http://example.com/more.xoxo");
dict.put("title","sample url");
dict.put("type","text/xml");
dict.put("rel","help");
dict.put("text","an example");
String html = xoxo.toXOXO(dict);
try{
Object newDict = parser.parse(html);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testSpecialAttributeRoundTripNoText(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("url","http://example.com/more.xoxo");
dict.put("title","sample url");
dict.put("type","text/xml");
dict.put("rel","help");
String html = xoxo.toXOXO(dict);
try{
Object newDict = parser.parse(html);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testSpecialAttributeRoundTripNoTextOrTitle(){
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
Map dict = new LinkedHashMap();
dict.put("url","http://example.com/more.xoxo");
dict.put("type","text/xml");
dict.put("rel","help");
String html = xoxo.toXOXO(dict);
try{
Object newDict = parser.parse(html);
assertEquals(dict,newDict);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
public void testUnicodeRoundTrip(){
String s = "Tantek Çelik and a snowman ?";
XOXOWriter xoxo = new XOXOWriter();
XOXOParser parser = new XOXOParser();
String html = xoxo.toXOXO(s);
try{
Object newString = parser.parse(html);
assertEquals(s,newString);
}catch (Exception e){
fail(e.getMessage());
e.printStackTrace();
}
}
}