/*
* Copyright (C) 2010-2014 Geometer Plus <contact@geometerplus.com>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.fbreader.network.rss;
import java.util.Map;
import org.geometerplus.zlibrary.core.xml.ZLStringMap;
import org.geometerplus.zlibrary.core.xml.ZLXMLReaderAdapter;
import org.geometerplus.fbreader.network.atom.*;
public class RSSXMLReader<MetadataType extends RSSChannelMetadata,EntryType extends RSSItem> extends ZLXMLReaderAdapter {
public RSSXMLReader(ATOMFeedHandler<MetadataType,EntryType> handler, boolean readEntryNotFeed) {
myFeedHandler = handler;
myState = START;
}
protected int myState;
private final ATOMFeedHandler<MetadataType,EntryType> myFeedHandler;
private Map<String,String> myNamespaceMap;
private final StringBuilder myBuffer = new StringBuilder();
protected final FormattedBuffer myFormattedBuffer = new FormattedBuffer();
private EntryType myItem;
private RSSAuthor myAuthor;
private RSSCategory myCategory;
private ATOMId myId;
private static final int START = 0;
protected static final int RSS = 1;
protected static final int CHANNEL = 2;
protected static final int C_TITLE = 3;
protected static final int C_LINK = 4;
protected static final int ITEM = 5;
protected static final int TITLE = 6;
protected static final int LINK = 7;
protected static final int COMMENTS = 8;
protected static final int PUBDATE = 9;
protected static final int CATEGORY = 10;
protected static final int GUID = 11;
protected static final int DESCRIPTION = 12;
protected static final int CONTENT = 13;
protected static final int COMMENTS_RSS = 14;
protected static final String TAG_RSS = "rss";
protected static final String TAG_CHANNEL = "channel";
protected static final String TAG_ITEM = "item";
protected static final String TAG_TITLE = "title";
protected static final String TAG_CATEGORY = "category";
protected static final String TAG_LINK = "link";
protected static final String TAG_GUID = "guid";
protected static final String TAG_DESCRIPTION = "description";
protected static final String TAG_PUBDATE = "pubDate";
@Override
public final boolean startElementHandler(String tag, ZLStringMap attributes) {
final int index = tag.indexOf(':');
final String tagPrefix;
if (index != -1) {
tagPrefix = tag.substring(0, index).intern();
tag = tag.substring(index + 1).intern();
} else {
tagPrefix = "";
tag = tag.intern();
}
return startElementHandler(getNamespace(tagPrefix), tag, attributes, extractBufferContent());
}
@Override
public final boolean endElementHandler(String tag) {
final int index = tag.indexOf(':');
final String tagPrefix;
if (index != -1) {
tagPrefix = tag.substring(0, index).intern();
tag = tag.substring(index + 1).intern();
} else {
tagPrefix = "";
tag = tag.intern();
}
return endElementHandler(getNamespace(tagPrefix), tag, extractBufferContent());
}
@Override
public final void characterDataHandler(char[] data, int start, int length) {
myBuffer.append(data, start, length);
}
public boolean startElementHandler(String ns, String tag, ZLStringMap attributes, String bufferContent) {
switch (myState) {
case START:
if(testTag(TAG_RSS, tag, ns, null)) {
myState = RSS;
}
break;
case RSS:
if (testTag(TAG_CHANNEL, tag, ns, null)) {
myState = CHANNEL;
}
break;
case CHANNEL:
if (testTag(TAG_TITLE, tag, ns, null)) {
myState = C_TITLE;
}
if (testTag(TAG_LINK, tag, ns, null)) {
myState = C_LINK;
}
if (testTag(TAG_ITEM, tag, ns, null)) {
myItem = myFeedHandler.createEntry(attributes);
myState = ITEM;
}
break;
case ITEM:
if (testTag(TAG_TITLE, tag, ns, null)) {
myAuthor = new RSSAuthor(attributes);
myState = TITLE;
}
if (testTag(TAG_LINK, tag, ns, null)) {
myState = LINK;
}
if (testTag(TAG_DESCRIPTION, tag, ns, null)) {
myState = DESCRIPTION;
}
if (testTag(TAG_CATEGORY, tag, ns, null)) {
myState = CATEGORY;
}
if (testTag(TAG_GUID, tag, ns, null)) {
myId = new ATOMId();
myState = GUID;
}
if (testTag(TAG_PUBDATE, tag, ns, null)) {
myState = PUBDATE;
}
}
return false;
}
public boolean endElementHandler(String ns, String tag, String bufferContent) {
switch (myState) {
case START:
break;
case RSS:
if (testTag(TAG_RSS, tag, ns, null)) {
myState = START;
}
break;
case CHANNEL:
if (testTag(TAG_CHANNEL, tag, ns, null)) {
myState = RSS;
}
break;
case C_TITLE:
if (testTag(TAG_TITLE, tag, ns, null)) {
myState = CHANNEL;
}
break;
case C_LINK:
if (testTag(TAG_LINK, tag, ns, null)) {
myState = CHANNEL;
}
break;
case ITEM:
if (testTag(TAG_ITEM, tag, ns, null)) {
myFeedHandler.processFeedEntry(myItem);
myState = CHANNEL;
}
case TITLE:
if (testTag(TAG_TITLE, tag, ns, null)) {
parseTitle(bufferContent);
myState = ITEM;
}
break;
case GUID:
if (testTag(TAG_GUID, tag, ns, null)) {
if (myId != null) {
myId.Uri = bufferContent;
myItem.Id = myId;
myId = null;
}
myState = ITEM;
}
break;
case DESCRIPTION:
if (testTag(TAG_DESCRIPTION, tag, ns, null)) {
myFormattedBuffer.reset(FormattedBuffer.Type.Html);
myFormattedBuffer.appendText(makeFormat(bufferContent));
myItem.Summary = myFormattedBuffer.getText();
myState = ITEM;
}
break;
case CATEGORY:
if (testTag(TAG_CATEGORY, tag, ns, null)) {
String[] tokens = bufferContent.split(", ");
for (String str : tokens) {
ZLStringMap source = new ZLStringMap();
source.put(RSSCategory.LABEL, str);
myCategory = new RSSCategory(source);
if (myCategory != null) {
myItem.Categories.add(myCategory);
}
myCategory = null;
}
myState = ITEM;
}
break;
case PUBDATE:
if (testTag(TAG_PUBDATE, tag, ns, null)) {
myState = ITEM;
}
break;
case LINK:
if (testTag(TAG_LINK, tag, ns, null)) {
myState = ITEM;
}
break;
}
return false;
}
private void parseTitle(String bufferContent) {
String[] marks = {"~ by:", "By"};
boolean found = false;
for (int i = 0; i < marks.length; i++) {
int foundIndex = bufferContent.indexOf(marks[i]);
if (foundIndex >= 0) {
if (myAuthor != null) {
String title = bufferContent.substring(0, foundIndex);
myItem.Title = title;
String authorName = bufferContent.substring(foundIndex+marks[i].length());
myAuthor.Name = authorName.trim();
myItem.Authors.add(myAuthor);
myAuthor = null;
}
found = true;
break;
}
}
if (!found) {
myItem.Title = bufferContent;
}
}
private String makeFormat(String buffer) {
//TODO: maybe need to make the text more readable?
StringBuffer s1 = new StringBuffer(buffer);
int index;
String[] marks = {"Author:", "Price:", "Rating:"};
for (int i = 0; i < marks.length; i++) {
index = s1.indexOf(marks[i]);
if (index >= 0) {
s1.insert(index, "<br/>");
}
}
return s1.toString();
}
public boolean testTag(String name, String tag, String ns, String nsName) {
return name == tag && ns == nsName;
}
@Override
public final boolean processNamespaces() {
return true;
}
@Override
public final void namespaceMapChangedHandler(Map<String,String> namespaceMap) {
myNamespaceMap = namespaceMap;
}
protected final String getNamespace(String prefix) {
if (myNamespaceMap == null) {
return null;
}
final String ns = myNamespaceMap.get(prefix);
return ns != null ? ns.intern() : null;
}
private final String extractBufferContent() {
final char[] bufferContentArray = myBuffer.toString().toCharArray();
myBuffer.delete(0, myBuffer.length());
if (bufferContentArray.length == 0) {
return null;
}
return new String(bufferContentArray);
}
}