package com.limegroup.gnutella.xml; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collections; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; //import org.xml.sax.helpers.XMLReaderFactory; import com.util.LOG; //import com.sun.org.apache.xerces.internal.parsers.SAXParser; /** * Provides just enough functionality for our simple schemas, * based on SAX * @author tjones */ public class XMLParsingUtils { static final private String XML_START = "<?xml"; /** * a ThreadLocal to contain the instance of the Lime parser */ private static ThreadLocal _parserContainer = new ThreadLocal() { protected Object initialValue() { return new LimeParser(); } }; /** * Parses our simplified XML */ public static ParseResult parse(String xml, int responseCount) throws IOException, SAXException { // to fix xsi:noNamespaceSchemaLocation="http://www.limewire.com/schemas/audio.xsd" xml = xml.replaceFirst("xsi:noNamespaceSchemaLocation=\"http://www.limewire.com/schemas/audio.xsd\"", ""); return parse(new InputSource(new StringReader(xml)),responseCount); } public static ParseResult parse(InputSource inputSource) throws IOException,SAXException { return parse(inputSource, 8); } /** * Parses our simplified XML */ public static ParseResult parse(InputSource inputSource, int responseCount) throws IOException, SAXException { ParseResult result = new ParseResult(responseCount); LimeParser parser = (LimeParser)_parserContainer.get(); parser.parse(result,inputSource); return result; } /** * Splits an aggregated XML string into individual XML strings * @param aggregatedXmlDocuments * @return List of Strings */ public static List split(String aggregatedXmlDocuments) { List results = new ArrayList(); int begin=aggregatedXmlDocuments.indexOf(XML_START); int end=aggregatedXmlDocuments.indexOf(XML_START,begin+1); while(end!=-1) { results.add(aggregatedXmlDocuments.substring(begin,end)); begin = end; end = aggregatedXmlDocuments.indexOf(XML_START,begin+1); } if(begin!=-1) results.add(aggregatedXmlDocuments.substring(begin)); return results; } /** * A list of maps, also containing the Schema URI, the type and * the canonical key prefix */ public static class ParseResult extends ArrayList { public ParseResult(int size) { super(size*2/3); } public String schemaURI; //like http://www.limewire.com/schemas/audio.xsd public String type; //e.g. audio, video, etc. public String canonicalKeyPrefix; //like audios__audio__ } /** * this class does the actual parsing of the document. It is a reusable * DocumentHandler. */ // TODO: <?xml version="1.0"?><audios xsi:noNamespaceSchemaLocation="http://www.limewire.com/schemas/audio.xsd"><audio title="Kiss On Me" artist="Tyler Hilton" album="The Tracks Of" genre="General Pop" track="7" year="0" seconds="202" bitrate="192" index="0"/><audio title="Naturally" artist="Selena Gomez & The Scene" album="Kiss And Tell" seconds="190" bitrate="128" index="1"/><audio title="My First Kiss (Feat. Ke$ha)" artist="3OH!3" album="Streets of Gold" seconds="194" bitrate="128" index="2"/><audio title="Falling Down" artist="Selena Gomez & The Scene" album="Kiss & Tell" track="1/1" year="2009" seconds="181" bitrate="128" index="3"/></audios> // <audios xsi:noNamespaceSchemaLocation causing trouble SAX can't pass it with unbound prefix // if I setFeature, then get another error. private static class LimeParser extends DefaultHandler { private XMLReader _reader = null; private ParseResult _result; boolean _isFirstElement=true; LimeParser() { SAXParserFactory spf = SAXParserFactory.newInstance(); try { SAXParser parse = spf.newSAXParser(); _reader = parse.getXMLReader(); _reader.setContentHandler(this); // _reader.setFeature("http://xml.org/sax/features/namespaces", false); _reader.setErrorHandler(this); //_reader.setProperty(name, value) } catch (Exception ex) { LOG.logSp(ex.getMessage()); } /* XMLReader reader; try { reader = new SAXParser(); reader.setContentHandler(this); reader.setFeature("http://xml.org/sax/features/namespaces", false); }catch(SAXException bad) { ErrorService.error(bad); reader = null; } _reader=reader; */ } @Override public void warning(SAXParseException e) { //LOG.logSp(e.getMessage()); } @Override public void error(SAXParseException e) { //LOG.logSp(e.getMessage()); } @Override public void fatalError(org.xml.sax.SAXParseException e) { //LOG.logSp(e.getMessage()); } /** * parses the given document input. Any state from previous parsing is * discarded. */ public void parse(ParseResult dest, InputSource input) throws SAXException, IOException { //if parser creation failed, do not try to parse. if (_reader==null) return; _isFirstElement=true; _result = dest; _reader.parse(input); } @Override public void startElement(String namespaceUri, String localName, String qualifiedName, Attributes attributes) { if(_isFirstElement) { _isFirstElement=false; _result.canonicalKeyPrefix = qualifiedName; return; } if(_result.type==null) { _result.type = qualifiedName; _result.schemaURI = "http://www.limewire.com/schemas/"+_result.type+".xsd"; _result.canonicalKeyPrefix += "__"+qualifiedName+"__"; } int attributesLength = attributes.getLength(); if(attributesLength > 0) { Map attributeMap = new HashMap(attributesLength); for(int i = 0; i < attributesLength; i++) { attributeMap.put(_result.canonicalKeyPrefix + attributes.getQName(i) + "__", attributes.getValue(i).trim()); } _result.add(attributeMap); } else { _result.add(Collections.EMPTY_MAP); } } } }