/*
* Copyright (C) 2010-2011 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.opds;
import java.util.*;
import org.geometerplus.zlibrary.core.constants.XMLNamespaces;
import org.geometerplus.zlibrary.core.filesystem.ZLResourceFile;
import org.geometerplus.zlibrary.core.util.MimeType;
import org.geometerplus.zlibrary.core.xml.ZLStringMap;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.atom.*;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
import org.geometerplus.fbreader.network.authentication.litres.LitResAuthenticationManager;
import org.geometerplus.fbreader.network.urlInfo.*;
class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants {
private static class FeedHandler implements ATOMFeedHandler<OPDSFeedMetadata,OPDSEntry> {
private NetworkLibrary.OnNewLinkListener myListener;
private String myAuthenticationType;
private final LinkedList<URLRewritingRule> myUrlRewritingRules = new LinkedList<URLRewritingRule>();
private final HashMap<RelationAlias, String> myRelationAliases = new HashMap<RelationAlias, String>();
private final LinkedHashMap<String,String> myExtraData = new LinkedHashMap<String,String>();
private ATOMUpdated myUpdatedTime;
private ATOMUpdated myReadAfterTime;
public FeedHandler(NetworkLibrary.OnNewLinkListener listener, ATOMUpdated readAfter) {
myListener = listener;
myReadAfterTime = readAfter;
}
public void setAuthenticationType(String type) {
myAuthenticationType = type;
}
public void addUrlRewritingRule(URLRewritingRule rule) {
myUrlRewritingRules.add(rule);
}
public void addRelationAlias(RelationAlias alias, String relation) {
myRelationAliases.put(alias, relation);
}
public void putExtraData(String name, String value) {
myExtraData.put(name, value);
}
public void clear() {
myAuthenticationType = null;
myUrlRewritingRules.clear();
myRelationAliases.clear();
myExtraData.clear();
}
public ATOMUpdated getUpdatedTime() {
return myUpdatedTime;
}
private static final String ENTRY_ID_PREFIX = "urn:fbreader-org-catalog:";
public boolean processFeedEntry(OPDSEntry entry) {
final String id = entry.Id.Uri;
if (id == null || id.length() <= ENTRY_ID_PREFIX.length()
|| !id.startsWith(ENTRY_ID_PREFIX)) {
return false;
}
final String siteName = id.substring(ENTRY_ID_PREFIX.length());
final CharSequence title = entry.Title;
final CharSequence summary = entry.Content;
final String language = entry.DCLanguage;
final UrlInfoCollection<UrlInfoWithDate> infos =
new UrlInfoCollection<UrlInfoWithDate>();
for (ATOMLink link: entry.Links) {
final String href = link.getHref();
final MimeType type = MimeType.get(link.getType());
final String rel = link.getRel();
if (rel == REL_IMAGE_THUMBNAIL || rel == REL_THUMBNAIL) {
if (MimeType.IMAGE_PNG.equals(type) || MimeType.IMAGE_JPEG.equals(type)) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.Thumbnail, href));
}
} else if ((rel != null && rel.startsWith(REL_IMAGE_PREFIX)) || rel == REL_COVER) {
if (MimeType.IMAGE_PNG.equals(type) || MimeType.IMAGE_JPEG.equals(type)) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.Image, href));
}
} else if (rel == null) {
if (MimeType.APP_ATOM.equals(type)) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.Catalog, href));
}
} else if (rel == "search") {
if (MimeType.APP_ATOM.equals(type)) {
final OpenSearchDescription descr = OpenSearchDescription.createDefault(href);
if (descr.isValid()) {
// TODO: May be do not use '%s'??? Use Description instead??? (this needs to rewrite SEARCH engine logic a little)
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.Search, descr.makeQuery("%s")));
}
}
} else if (rel == REL_LINK_SIGN_IN) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.SignIn, href));
} else if (rel == REL_LINK_SIGN_OUT) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.SignOut, href));
} else if (rel == REL_LINK_SIGN_UP) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.SignUp, href));
} else if (rel == REL_LINK_TOPUP) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.TopUp, href));
} else if (rel == REL_LINK_RECOVER_PASSWORD) {
infos.addInfo(new UrlInfoWithDate(UrlInfo.Type.RecoverPassword, href));
}
}
final String sslCertificate;
final String path = "network/" + siteName + ".crt";
if (ZLResourceFile.createResourceFile(path).exists()) {
sslCertificate = path;
} else {
sslCertificate = null;
}
INetworkLink result = link(id, siteName, title, summary, language, infos, sslCertificate);
if (result != null) {
myListener.onNewLink(result);
}
return false;
}
private INetworkLink link(
String id,
String siteName,
CharSequence title,
CharSequence summary,
String language,
UrlInfoCollection<UrlInfoWithDate> infos,
String sslCertificate
) {
if (siteName == null || title == null || infos.getInfo(UrlInfo.Type.Catalog) == null) {
return null;
}
final String titleString = title.toString();
final String summaryString = summary != null ? summary.toString() : null;
OPDSNetworkLink opdsLink = new OPDSPredefinedNetworkLink(
OPDSNetworkLink.INVALID_ID,
id,
siteName,
titleString,
summaryString,
language,
infos
);
/*if (!mySearchType.empty()) {
opdsLink.setupAdvancedSearch(
mySearchType,
mySearchFields["titleOrSeries"],
mySearchFields["author"],
mySearchFields["tag"],
mySearchFields["annotation"]
);
}*/
opdsLink.setRelationAliases(myRelationAliases);
opdsLink.setUrlRewritingRules(myUrlRewritingRules);
opdsLink.setExtraData(myExtraData);
NetworkAuthenticationManager authManager = null;
if (myAuthenticationType == "basic") {
//authManager = NetworkAuthenticationManager.createManager(opdsLink, sslCertificate, BasicAuthenticationManager.class);
} else if (myAuthenticationType == "litres") {
authManager = NetworkAuthenticationManager.createManager(opdsLink, sslCertificate, LitResAuthenticationManager.class);
}
opdsLink.setAuthenticationManager(authManager);
return opdsLink;
}
public boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
myUpdatedTime = feed.Updated;
if (myUpdatedTime != null && myReadAfterTime != null
&& myUpdatedTime.compareTo(myReadAfterTime) <= 0) {
return true;
}
return myListener == null; // no listener -- no need to proceed
}
public void processFeedStart() {
myUpdatedTime = null;
}
public void processFeedEnd() {
}
}
public OPDSLinkXMLReader() {
super(new FeedHandler(null, null), false);
}
public OPDSLinkXMLReader(NetworkLibrary.OnNewLinkListener listener, ATOMUpdated readAfter) {
super(new FeedHandler(listener, readAfter), false);
}
private FeedHandler getFeedHandler() {
return (FeedHandler)getATOMFeedHandler();
}
public ATOMUpdated getUpdatedTime() {
return getFeedHandler().getUpdatedTime();
}
private static final String FBREADER_ADVANCED_SEARCH = "advancedSearch";
private static final String FBREADER_AUTHENTICATION = "authentication";
private static final String FBREADER_REWRITING_RULE = "urlRewritingRule";
private static final String FBREADER_RELATION_ALIAS = "relationAlias";
private static final String FBREADER_EXTRA = "extra";
@Override
public boolean startElementHandler(final String ns, final String tag,
final ZLStringMap attributes, final String bufferContent) {
switch (myState) {
case FEED:
if (ns == XMLNamespaces.Atom && tag == TAG_ENTRY) {
getFeedHandler().clear();
}
break;
case F_ENTRY:
if (ns == XMLNamespaces.FBReaderCatalogMetadata) {
if (tag == FBREADER_ADVANCED_SEARCH) {
return false;
} else if (tag == FBREADER_AUTHENTICATION) {
final String type = attributes.getValue("type");
getFeedHandler().setAuthenticationType(type);
return false;
} else if (tag == FBREADER_RELATION_ALIAS) {
final String name = attributes.getValue("name");
final String type = attributes.getValue("type");
String alias = attributes.getValue("alias");
if (alias != null && name != null) {
if (alias.length() == 0) {
alias = null;
}
getFeedHandler().addRelationAlias(new RelationAlias(alias, type), name);
}
return false;
} else if (tag == FBREADER_REWRITING_RULE) {
getFeedHandler().addUrlRewritingRule(new URLRewritingRule(attributes));
return false;
} else if (tag == FBREADER_EXTRA) {
final String name = attributes.getValue("name");
final String value = attributes.getValue("value");
if (name != null && value != null) {
getFeedHandler().putExtraData(name, value);
}
}
}
break;
}
return super.startElementHandler(ns, tag, attributes, bufferContent);
}
}