/*- * Copyright (C) 2011 Peter Baldwin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.peterbaldwin.vlcremote.net.xml; import android.net.Uri; import android.text.Html; import java.io.IOException; import java.net.URLConnection; import java.util.Stack; import org.peterbaldwin.vlcremote.model.Playlist; import org.peterbaldwin.vlcremote.model.Track; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; public final class XmlPlaylistContentHandler extends XmlContentHandler<Playlist> implements ContentHandler { private static final String[] TEXT_ELEMENTS = { "title", "artist", "genre", "copyright", "album", "track", "description", "rating", "date", "url", "language", "now_playing", "publisher", "encoded_by", "art_url", "track_id" }; private static final Playlist EMPTY_PLAYLIST = new Playlist(1, "Undefined"); private final Stack<Playlist> mNodeStack; private final StringBuilder mBuilder; private Playlist mRoot; private boolean mCapture = false; private Track mTrack; public XmlPlaylistContentHandler() { mNodeStack = new Stack<Playlist>(); mBuilder = new StringBuilder(); } /** {@inheritDoc} */ public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { if ("leaf".equals(localName)) { mTrack = createTrack(attributes); if ("vlc://nop".equals(attributes.getValue("uri"))) { // Don't include nop tracks in the output } else { mRoot.add(mTrack); } } else if ("node".equals(localName)) { Playlist playlist = createPlaylist(attributes); if (mNodeStack.isEmpty()) { mRoot = playlist; } mNodeStack.push(playlist); } else if (mTrack != null && isTextElement(localName)) { mBuilder.setLength(0); mCapture = true; } } private static String unescape(CharSequence text) { // TODO: Do this more efficiently return Html.fromHtml(text.toString()).toString(); } private String getText() { if (mBuilder.length() == 0) { return null; } else { if (mBuilder.indexOf("&") != -1) { // Text is escaped twice so that it can be used in HTML. return unescape(mBuilder); } else { return mBuilder.toString(); } } } /** {@inheritDoc} */ public void endElement(String uri, String localName, String name) throws SAXException { if ("node".equals(localName)) { mNodeStack.pop(); } else if ("leaf".equals(localName)) { mTrack = null; } else if (mTrack != null) { if ("title".equals(localName)) { mTrack.setTitle(getText()); } else if ("artist".equals(localName)) { mTrack.setArtist(getText()); } else if ("genre".equals(localName)) { mTrack.setGenre(getText()); } else if ("copyright".equals(localName)) { mTrack.setCopyright(getText()); } else if ("album".equals(localName)) { mTrack.setAlbum(getText()); } else if ("track".equals(localName)) { mTrack.setTrack(getText()); } else if ("description".equals(localName)) { mTrack.setDescription(getText()); } else if ("rating".equals(localName)) { mTrack.setRating(getText()); } else if ("date".equals(localName)) { mTrack.setDate(getText()); } else if ("url".equals(localName)) { mTrack.setUrl(getText()); } else if ("language".equals(localName)) { mTrack.setLanguage(getText()); } else if ("now_playing".equals(localName)) { mTrack.setNowPlaying(getText()); } else if ("publisher".equals(localName)) { mTrack.setPublisher(getText()); } else if ("encoded_by".equals(localName)) { mTrack.setEncodedBy(getText()); } else if ("art_url".equals(localName)) { mTrack.setArtUrl(getText()); } else if ("track_id".equals(localName)) { mTrack.setTrackId(getText()); } } mCapture = false; } private static boolean isTextElement(String localName) { for (int i = 0; i < TEXT_ELEMENTS.length; i++) { if (TEXT_ELEMENTS[i].equals(localName)) { return true; } } return false; } /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (mCapture) { mBuilder.append(ch, start, length); } } /** {@inheritDoc} */ public void endDocument() throws SAXException { } /** {@inheritDoc} */ public void endPrefixMapping(String prefix) throws SAXException { } /** {@inheritDoc} */ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } /** {@inheritDoc} */ public void processingInstruction(String target, String data) throws SAXException { } /** {@inheritDoc} */ public void setDocumentLocator(Locator locator) { } /** {@inheritDoc} */ public void skippedEntity(String name) throws SAXException { } /** {@inheritDoc} */ public void startDocument() throws SAXException { } /** {@inheritDoc} */ public void startPrefixMapping(String prefix, String uri) throws SAXException { } private static Playlist createPlaylist(Attributes attributes) { int id = Integer.parseInt(attributes.getValue("", "id")); String name = attributes.getValue("", "name"); return new Playlist(id, name); } private static Track createTrack(Attributes attributes) { Track track = new Track(); int id = Integer.parseInt(attributes.getValue("", "id")); track.setId(id); boolean current = "current".equals(attributes.getValue("", "current")); track.setCurrent(current); String uri = attributes.getValue("", "uri"); track.setUri(Uri.decode(uri)); String name = attributes.getValue("", "name"); track.setName(name); long duration = Long.parseLong(attributes.getValue("", "duration")); track.setDuration(duration); return track; } @Override public Object getContent(URLConnection connection) throws IOException { parse(connection, this); return (mRoot != null) ? mRoot : EMPTY_PLAYLIST; } }