/**
* Copyright (C) 2013 by Raphael Michel under the MIT license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package de.geeksfactory.opacclient.apis;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.geeksfactory.opacclient.apis.OpacApi.MultiStepResult.Status;
import de.geeksfactory.opacclient.i18n.StringProvider;
import de.geeksfactory.opacclient.networking.HttpClientFactory;
import de.geeksfactory.opacclient.networking.NotReachableException;
import de.geeksfactory.opacclient.objects.Account;
import de.geeksfactory.opacclient.objects.AccountData;
import de.geeksfactory.opacclient.objects.Copy;
import de.geeksfactory.opacclient.objects.Detail;
import de.geeksfactory.opacclient.objects.DetailedItem;
import de.geeksfactory.opacclient.objects.Filter;
import de.geeksfactory.opacclient.objects.Filter.Option;
import de.geeksfactory.opacclient.objects.LentItem;
import de.geeksfactory.opacclient.objects.Library;
import de.geeksfactory.opacclient.objects.ReservedItem;
import de.geeksfactory.opacclient.objects.SearchRequestResult;
import de.geeksfactory.opacclient.objects.SearchResult;
import de.geeksfactory.opacclient.objects.SearchResult.MediaType;
import de.geeksfactory.opacclient.searchfields.DropdownSearchField;
import de.geeksfactory.opacclient.searchfields.SearchField;
import de.geeksfactory.opacclient.searchfields.SearchQuery;
import de.geeksfactory.opacclient.searchfields.TextSearchField;
/**
* OpacApi implementation for Web Opacs of the SISIS SunRise product, developed by OCLC.
*
* Restrictions: Bookmarks are only constantly supported if the library uses the BibTip extension.
*/
public class SISIS extends BaseApi implements OpacApi {
protected static HashMap<String, MediaType> defaulttypes = new HashMap<>();
static {
defaulttypes.put("g", MediaType.EBOOK);
defaulttypes.put("d", MediaType.CD);
defaulttypes.put("Buch", MediaType.BOOK);
defaulttypes.put("Bücher", MediaType.BOOK);
defaulttypes.put("Printmedien", MediaType.BOOK);
defaulttypes.put("Zeitschrift", MediaType.MAGAZINE);
defaulttypes.put("Zeitschriften", MediaType.MAGAZINE);
defaulttypes.put("zeitung", MediaType.NEWSPAPER);
defaulttypes.put(
"Einzelband einer Serie, siehe auch übergeordnete Titel",
MediaType.BOOK);
defaulttypes.put("0", MediaType.BOOK);
defaulttypes.put("1", MediaType.BOOK);
defaulttypes.put("2", MediaType.BOOK);
defaulttypes.put("3", MediaType.BOOK);
defaulttypes.put("4", MediaType.BOOK);
defaulttypes.put("5", MediaType.BOOK);
defaulttypes.put("Buch-Kinderbuch", MediaType.BOOK);
defaulttypes.put("6", MediaType.SCORE_MUSIC);
defaulttypes.put("7", MediaType.CD_MUSIC);
defaulttypes.put("8", MediaType.CD_MUSIC);
defaulttypes.put("Tonträger", MediaType.CD_MUSIC);
defaulttypes.put("12", MediaType.CD);
defaulttypes.put("13", MediaType.CD);
defaulttypes.put("CD", MediaType.CD);
defaulttypes.put("DVD", MediaType.DVD);
defaulttypes.put("14", MediaType.CD);
defaulttypes.put("15", MediaType.DVD);
defaulttypes.put("16", MediaType.CD);
defaulttypes.put("audiocd", MediaType.CD);
defaulttypes.put("Film", MediaType.MOVIE);
defaulttypes.put("Filme", MediaType.MOVIE);
defaulttypes.put("17", MediaType.MOVIE);
defaulttypes.put("18", MediaType.MOVIE);
defaulttypes.put("19", MediaType.MOVIE);
defaulttypes.put("20", MediaType.DVD);
defaulttypes.put("dvd", MediaType.DVD);
defaulttypes.put("21", MediaType.SCORE_MUSIC);
defaulttypes.put("Noten", MediaType.SCORE_MUSIC);
defaulttypes.put("22", MediaType.BOARDGAME);
defaulttypes.put("26", MediaType.CD);
defaulttypes.put("27", MediaType.CD);
defaulttypes.put("28", MediaType.EBOOK);
defaulttypes.put("31", MediaType.BOARDGAME);
defaulttypes.put("35", MediaType.MOVIE);
defaulttypes.put("36", MediaType.DVD);
defaulttypes.put("37", MediaType.CD);
defaulttypes.put("29", MediaType.AUDIOBOOK);
defaulttypes.put("41", MediaType.GAME_CONSOLE);
defaulttypes.put("42", MediaType.GAME_CONSOLE);
defaulttypes.put("46", MediaType.GAME_CONSOLE_NINTENDO);
defaulttypes.put("52", MediaType.EBOOK);
defaulttypes.put("56", MediaType.EBOOK);
defaulttypes.put("96", MediaType.EBOOK);
defaulttypes.put("97", MediaType.EBOOK);
defaulttypes.put("99", MediaType.EBOOK);
defaulttypes.put("EB", MediaType.EBOOK);
defaulttypes.put("ebook", MediaType.EBOOK);
defaulttypes.put("buch01", MediaType.BOOK);
defaulttypes.put("buch02", MediaType.PACKAGE_BOOKS);
defaulttypes.put("Medienpaket", MediaType.PACKAGE);
defaulttypes.put("datenbank", MediaType.PACKAGE);
defaulttypes
.put("Medienpaket, Lernkiste, Lesekiste", MediaType.PACKAGE);
defaulttypes.put("buch03", MediaType.BOOK);
defaulttypes.put("buch04", MediaType.PACKAGE_BOOKS);
defaulttypes.put("buch05", MediaType.PACKAGE_BOOKS);
defaulttypes.put("Web-Link", MediaType.URL);
defaulttypes.put("ejournal", MediaType.EDOC);
defaulttypes.put("karte", MediaType.MAP);
}
protected final long SESSION_LIFETIME = 1000 * 60 * 3;
protected String opac_url = "";
protected JSONObject data;
protected String CSId;
protected String identifier;
protected int resultcount = 10;
protected long logged_in;
protected Account logged_in_as;
protected static final String ENCODING = "UTF-8";
public List<SearchField> parseSearchFields() throws IOException,
JSONException {
if (!initialised) {
start();
}
String html = httpGet(opac_url
+ "/search.do?methodToCall=switchSearchPage&SearchType=2",
ENCODING);
Document doc = Jsoup.parse(html);
List<SearchField> fields = new ArrayList<>();
Elements options = doc
.select("select[name=searchCategories[0]] option");
for (Element option : options) {
TextSearchField field = new TextSearchField();
field.setDisplayName(option.text());
field.setId(option.attr("value"));
field.setHint("");
fields.add(field);
}
for (Element dropdown : doc.select("#tab-content select")) {
parseDropdown(dropdown, fields);
}
return fields;
}
private void parseDropdown(Element dropdownElement,
List<SearchField> fields) throws JSONException {
Elements options = dropdownElement.select("option");
DropdownSearchField dropdown = new DropdownSearchField();
if (dropdownElement.parent().select("input[type=hidden]").size() > 0) {
dropdown.setId(dropdownElement.parent()
.select("input[type=hidden]").attr("value"));
dropdown.setData(new JSONObject("{\"restriction\": true}"));
} else {
dropdown.setId(dropdownElement.attr("name"));
dropdown.setData(new JSONObject("{\"restriction\": false}"));
}
for (Element option : options) {
dropdown.addDropdownValue(option.attr("value"), option.text());
}
dropdown.setDisplayName(dropdownElement.parent().select("label").text());
fields.add(dropdown);
}
@Override
public void start() throws
IOException {
// Some libraries require start parameters for start.do, like Login=foo
String startparams = "";
if (data.has("startparams")) {
try {
startparams = "?" + data.getString("startparams");
} catch (JSONException e) {
e.printStackTrace();
}
}
String html = httpGet(opac_url + "/start.do" + startparams, ENCODING);
initialised = true;
Document doc = Jsoup.parse(html);
CSId = doc.select("input[name=CSId]").val();
super.start();
}
@Override
public void init(Library lib, HttpClientFactory httpClientFactory) {
super.init(lib, httpClientFactory);
this.data = lib.getData();
try {
this.opac_url = data.getString("baseurl");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public SearchRequestResult search(List<SearchQuery> query)
throws IOException, OpacErrorException,
JSONException {
List<NameValuePair> params = new ArrayList<>();
int index = 0;
int restrictionIndex = 0;
start();
params.add(new BasicNameValuePair("methodToCall", "submit"));
params.add(new BasicNameValuePair("CSId", CSId));
params.add(new BasicNameValuePair("methodToCallParameter",
"submitSearch"));
for (SearchQuery entry : query) {
if (entry.getValue().equals("")) {
continue;
}
if (entry.getSearchField() instanceof DropdownSearchField) {
JSONObject data = entry.getSearchField().getData();
if (data.optBoolean("restriction", false)) {
params.add(new BasicNameValuePair("searchRestrictionID["
+ restrictionIndex + "]", entry.getSearchField()
.getId()));
params.add(new BasicNameValuePair(
"searchRestrictionValue1[" + restrictionIndex + "]",
entry.getValue()));
restrictionIndex++;
} else {
params.add(new BasicNameValuePair(entry.getKey(), entry
.getValue()));
}
} else {
if (index != 0) {
params.add(new BasicNameValuePair("combinationOperator["
+ index + "]", "AND"));
}
params.add(new BasicNameValuePair("searchCategories[" + index
+ "]", entry.getKey()));
params.add(new BasicNameValuePair(
"searchString[" + index + "]", entry.getValue()));
index++;
}
}
if (index == 0) {
throw new OpacErrorException(
stringProvider.getString(StringProvider.NO_CRITERIA_INPUT));
}
if (index > 4) {
throw new OpacErrorException(stringProvider.getQuantityString(
StringProvider.LIMITED_NUM_OF_CRITERIA, 4, 4));
}
params.add(new BasicNameValuePair("submitSearch", "Suchen"));
params.add(new BasicNameValuePair("callingPage", "searchParameters"));
params.add(new BasicNameValuePair("numberOfHits", "10"));
String html = httpGet(
opac_url + "/search.do?"
+ URLEncodedUtils.format(params, "UTF-8"), ENCODING);
return parse_search_wrapped(html, 1);
}
public SearchRequestResult volumeSearch(Map<String, String> query)
throws IOException, OpacErrorException {
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("methodToCall", "volumeSearch"));
params.add(new BasicNameValuePair("dbIdentifier", query
.get("dbIdentifier")));
params.add(new BasicNameValuePair("catKey", query.get("catKey")));
params.add(new BasicNameValuePair("periodical", "N"));
String html = httpGet(
opac_url + "/search.do?"
+ URLEncodedUtils.format(params, "UTF-8"), ENCODING);
return parse_search_wrapped(html, 1);
}
@Override
public SearchRequestResult searchGetPage(int page) throws IOException,
OpacErrorException {
if (!initialised) {
start();
}
String html = httpGet(opac_url
+ "/hitList.do?methodToCall=pos&identifier=" + identifier
+ "&curPos=" + (((page - 1) * resultcount) + 1), ENCODING);
return parse_search_wrapped(html, page);
}
public class SingleResultFound extends Exception {
}
protected SearchRequestResult parse_search_wrapped(String html, int page)
throws IOException, OpacErrorException {
try {
return parse_search(html, page);
} catch (SingleResultFound e) {
html = httpGet(opac_url + "/hitList.do?methodToCall=backToPrimaryHitList", ENCODING);
try {
return parse_search(html, page);
} catch (SingleResultFound e1) {
throw new NotReachableException();
}
}
}
public SearchRequestResult parse_search(String html, int page)
throws OpacErrorException, SingleResultFound {
Document doc = Jsoup.parse(html);
doc.setBaseUri(opac_url + "/searchfoo");
if (doc.select(".error").size() > 0) {
throw new OpacErrorException(doc.select(".error").text().trim());
} else if (doc.select(".nohits").size() > 0) {
throw new OpacErrorException(doc.select(".nohits").text().trim());
} else if (doc.select(".box-header h2, #nohits").text()
.contains("keine Treffer")) {
return new SearchRequestResult(new ArrayList<SearchResult>(), 0, 1,
1);
}
int results_total = -1;
String resultnumstr = doc.select(".box-header h2").first().text();
if (resultnumstr.contains("(1/1)") || resultnumstr.contains(" 1/1")) {
throw new SingleResultFound();
} else if (resultnumstr.contains("(")) {
results_total = Integer.parseInt(resultnumstr.replaceAll(
".*\\(([0-9]+)\\).*", "$1"));
} else if (resultnumstr.contains(": ")) {
results_total = Integer.parseInt(resultnumstr.replaceAll(
".*: ([0-9]+)$", "$1"));
}
Elements table = doc.select("table.data tbody tr");
identifier = null;
Elements links = doc.select("table.data a");
boolean haslink = false;
for (int i = 0; i < links.size(); i++) {
Element node = links.get(i);
if (node.hasAttr("href")
& node.attr("href").contains("singleHit.do") && !haslink) {
haslink = true;
try {
List<NameValuePair> anyurl = URLEncodedUtils.parse(
new URI(node.attr("href").replace(" ", "%20")
.replace("&", "&")), ENCODING);
for (NameValuePair nv : anyurl) {
if (nv.getName().equals("identifier")) {
identifier = nv.getValue();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
List<SearchResult> results = new ArrayList<>();
for (int i = 0; i < table.size(); i++) {
Element tr = table.get(i);
SearchResult sr = new SearchResult();
if (tr.select("td img[title]").size() > 0) {
String title = tr.select("td img").get(0).attr("title");
String[] fparts = tr.select("td img").get(0).attr("src")
.split("/");
String fname = fparts[fparts.length - 1];
MediaType default_by_fname = defaulttypes.get(fname
.toLowerCase(Locale.GERMAN).replace(".jpg", "")
.replace(".gif", "").replace(".png", ""));
MediaType default_by_title = defaulttypes.get(title);
MediaType default_name = default_by_title != null ? default_by_title
: default_by_fname;
if (data.has("mediatypes")) {
try {
sr.setType(MediaType.valueOf(data.getJSONObject(
"mediatypes").getString(fname)));
} catch (JSONException | IllegalArgumentException e) {
sr.setType(default_name);
}
} else {
sr.setType(default_name);
}
}
String alltext = tr.text();
if (alltext.contains("eAudio") || alltext.contains("eMusic")) {
sr.setType(MediaType.MP3);
} else if (alltext.contains("eVideo")) {
sr.setType(MediaType.EVIDEO);
} else if (alltext.contains("eBook")) {
sr.setType(MediaType.EBOOK);
} else if (alltext.contains("Munzinger")) {
sr.setType(MediaType.EDOC);
}
if (tr.children().size() > 3
&& tr.child(3).select("img[title*=cover]").size() == 1) {
sr.setCover(tr.child(3).select("img[title*=cover]")
.attr("abs:src"));
if (sr.getCover().contains("showCover.do")) {
downloadCover(sr);
}
}
Element middlething;
if (tr.children().size() > 2 && tr.child(2).select("a").size() > 0) {
middlething = tr.child(2);
} else {
middlething = tr.child(1);
}
List<Node> children = middlething.childNodes();
if (middlething.select("div")
.not("#hlrightblock,.bestellfunktionen").size() == 1) {
Element indiv = middlething.select("div")
.not("#hlrightblock,.bestellfunktionen").first();
if (indiv.children().size() > 1) {
children = indiv.childNodes();
}
} else if (middlething.select("span.titleData").size() == 1) {
children = middlething.select("span.titleData").first()
.childNodes();
}
int childrennum = children.size();
List<String[]> strings = new ArrayList<>();
for (int ch = 0; ch < childrennum; ch++) {
Node node = children.get(ch);
if (node instanceof TextNode) {
String text = ((TextNode) node).text().trim();
if (text.length() > 3) {
strings.add(new String[]{"text", "", text});
}
} else if (node instanceof Element) {
List<Node> subchildren = node.childNodes();
for (int j = 0; j < subchildren.size(); j++) {
Node subnode = subchildren.get(j);
if (subnode instanceof TextNode) {
String text = ((TextNode) subnode).text().trim();
if (text.length() > 3) {
strings.add(new String[]{
((Element) node).tag().getName(),
"text", text,
((Element) node).className(),
node.attr("style")});
}
} else if (subnode instanceof Element) {
String text = ((Element) subnode).text().trim();
if (text.length() > 3) {
strings.add(new String[]{
((Element) node).tag().getName(),
((Element) subnode).tag().getName(),
text, ((Element) node).className(),
node.attr("style")});
}
}
}
}
}
StringBuilder description = null;
if (tr.select("span.Z3988").size() == 1) {
// Sometimes there is a <span class="Z3988"> item which provides
// data in a standardized format.
List<NameValuePair> z3988data;
boolean hastitle = false;
try {
description = new StringBuilder();
z3988data = URLEncodedUtils.parse(new URI("http://dummy/?"
+ tr.select("span.Z3988").attr("title")), "UTF-8");
for (NameValuePair nv : z3988data) {
if (nv.getValue() != null) {
if (!nv.getValue().trim().equals("")) {
if (nv.getName().equals("rft.btitle")
&& !hastitle) {
description.append("<b>").append(nv.getValue()).append("</b>");
hastitle = true;
} else if (nv.getName().equals("rft.atitle")
&& !hastitle) {
description.append("<b>").append(nv.getValue()).append("</b>");
hastitle = true;
} else if (nv.getName().equals("rft.au")) {
description.append("<br />").append(nv.getValue());
} else if (nv.getName().equals("rft.date")) {
description.append("<br />").append(nv.getValue());
}
}
}
}
} catch (URISyntaxException e) {
description = null;
}
}
boolean described = false;
if (description != null && description.length() > 0) {
sr.setInnerhtml(description.toString());
described = true;
} else {
description = new StringBuilder();
}
int k = 0;
boolean yearfound = false;
boolean titlefound = false;
boolean sigfound = false;
for (String[] part : strings) {
if (!described) {
if (part[0].equals("a") && (k == 0 || !titlefound)) {
if (k != 0) {
description.append("<br />");
}
description.append("<b>").append(part[2]).append("</b>");
titlefound = true;
} else if (part[2].matches("\\D*[0-9]{4}\\D*")
&& part[2].length() <= 10) {
yearfound = true;
if (k != 0) {
description.append("<br />");
}
description.append(part[2]);
} else if (k == 1 && !yearfound
&& part[2].matches("^\\s*\\([0-9]{4}\\)$")) {
if (k != 0) {
description.append("<br />");
}
description.append(part[2]);
} else if (k == 1 && !yearfound
&& part[2].matches("^\\s*\\([0-9]{4}\\)$")) {
if (k != 0) {
description.append("<br />");
}
description.append(part[2]);
} else if (k == 1 && !yearfound) {
description.append("<br />");
description.append(part[2]);
} else if (k > 1 && k < 4 && !sigfound
&& part[0].equals("text")
&& part[2].matches("^[A-Za-z0-9,\\- ]+$")) {
description.append("<br />");
description.append(part[2]);
}
}
if (part.length == 4) {
if (part[0].equals("span") && part[3].equals("textgruen")) {
sr.setStatus(SearchResult.Status.GREEN);
} else if (part[0].equals("span")
&& part[3].equals("textrot")) {
sr.setStatus(SearchResult.Status.RED);
}
} else if (part.length == 5) {
if (part[4].contains("purple")) {
sr.setStatus(SearchResult.Status.YELLOW);
}
}
if (sr.getStatus() == null) {
if ((part[2].contains("entliehen") && part[2]
.startsWith("Vormerkung ist leider nicht möglich"))
|| part[2]
.contains(
"nur in anderer Zweigstelle ausleihbar und nicht bestellbar")) {
sr.setStatus(SearchResult.Status.RED);
} else if (part[2].startsWith("entliehen")
|| part[2]
.contains("Ein Exemplar finden Sie in einer anderen Zweigstelle")) {
sr.setStatus(SearchResult.Status.YELLOW);
} else if ((part[2].startsWith("bestellbar") && !part[2]
.contains("nicht bestellbar"))
|| (part[2].startsWith("vorbestellbar") && !part[2]
.contains("nicht vorbestellbar"))
|| (part[2].startsWith("vorbestellbar") && !part[2]
.contains("nicht vorbestellbar"))
|| (part[2].startsWith("vormerkbar") && !part[2]
.contains("nicht vormerkbar"))
|| (part[2].contains("heute zurückgebucht"))
|| (part[2].contains("ausleihbar") && !part[2]
.contains("nicht ausleihbar"))) {
sr.setStatus(SearchResult.Status.GREEN);
}
if (sr.getType() != null) {
if (sr.getType().equals(MediaType.EBOOK)
|| sr.getType().equals(MediaType.EVIDEO)
|| sr.getType().equals(MediaType.MP3))
// Especially Onleihe.de ebooks are often marked
// green though they are not available.
{
sr.setStatus(SearchResult.Status.UNKNOWN);
}
}
}
k++;
}
if (!described) {
sr.setInnerhtml(description.toString());
}
sr.setNr(10 * (page - 1) + i);
sr.setId(null);
results.add(sr);
}
resultcount = results.size();
return new SearchRequestResult(results, results_total, page);
}
@Override
public DetailedItem getResultById(String id, String homebranch)
throws IOException {
// Some libraries require start parameters for start.do, like Login=foo
String startparams = "";
if (data.has("startparams")) {
try {
startparams = data.getString("startparams") + "&";
} catch (JSONException e) {
e.printStackTrace();
}
}
String hbp = "";
if (homebranch != null) {
hbp = "&selectedViewBranchlib=" + homebranch;
}
String html = httpGet(opac_url + "/start.do?" + startparams
+ "searchType=1&Query=0%3D%22" + id + "%22" + hbp, ENCODING);
return loadDetail(html);
}
@Override
public DetailedItem getResult(int nr) throws IOException {
String html = httpGet(
opac_url
+ "/singleHit.do?tab=showExemplarActive&methodToCall=showHit&curPos="
+ (nr + 1) + "&identifier=" + identifier, ENCODING);
return loadDetail(html);
}
protected DetailedItem loadDetail(String html) throws IOException {
String html2 = httpGet(opac_url
+ "/singleHit.do?methodToCall=activateTab&tab=showTitleActive",
ENCODING);
String html3 = httpGet(
opac_url
+ "/singleHit.do?methodToCall=activateTab&tab=showAvailabilityActive",
ENCODING);
String coverJs = null;
Pattern coverPattern = Pattern.compile("\\$\\.ajax\\(\\{[\\n\\s]*url: '(jsp/result/cover" +
".jsp\\?[^']+')");
Matcher coverMatcher = coverPattern.matcher(html);
if (coverMatcher.find()) {
coverJs = httpGet(opac_url + "/" + coverMatcher.group(1), ENCODING);
}
DetailedItem result = parseDetail(html, html2, html3, coverJs, data, stringProvider);
try {
if (!result.getCover().contains("amazon")) downloadCover(result);
} catch (Exception e) {
}
return result;
}
static DetailedItem parseDetail(String html, String html2, String html3, String coverJs,
JSONObject data,
StringProvider stringProvider)
throws IOException {
Document doc = Jsoup.parse(html);
String opac_url = data.optString("baseurl", "");
doc.setBaseUri(opac_url);
Document doc2 = Jsoup.parse(html2);
doc2.setBaseUri(opac_url);
Document doc3 = Jsoup.parse(html3);
doc3.setBaseUri(opac_url);
DetailedItem result = new DetailedItem();
try {
result.setId(doc.select("#bibtip_id").text().trim());
} catch (Exception ex) {
ex.printStackTrace();
}
List<String> reservationlinks = new ArrayList<>();
for (Element link : doc3.select("#vormerkung a, #tab-content a")) {
String href = link.absUrl("href");
Map<String, String> hrefq = getQueryParamsFirst(href);
if (result.getId() == null) {
// ID retrieval
String key = hrefq.get("katkey");
if (key != null) {
result.setId(key);
break;
}
}
// Vormerken
if (hrefq.get("methodToCall") != null) {
if (hrefq.get("methodToCall").equals("doVormerkung")
|| hrefq.get("methodToCall").equals("doBestellung")) {
reservationlinks.add(href.split("\\?")[1]);
}
}
}
if (reservationlinks.size() == 1) {
result.setReservable(true);
result.setReservation_info(reservationlinks.get(0));
} else if (reservationlinks.size() == 0) {
result.setReservable(false);
} else {
// TODO: Multiple options - handle this case!
}
if (coverJs != null) {
Pattern srcPattern = Pattern.compile("<img .* src=\"([^\"]+)\">");
Matcher matcher = srcPattern.matcher(coverJs);
if (matcher.find()) {
result.setCover(matcher.group(1));
}
} else if (doc.select(".data td img").size() == 1) {
result.setCover(doc.select(".data td img").first().attr("abs:src"));
}
if (doc.select(".aw_teaser_title").size() == 1) {
result.setTitle(doc.select(".aw_teaser_title").first().text()
.trim());
} else if (doc.select(".data td strong").size() > 0) {
result.setTitle(doc.select(".data td strong").first().text().trim());
} else {
result.setTitle("");
}
if (doc.select(".aw_teaser_title_zusatz").size() > 0) {
result.addDetail(new Detail("Titelzusatz", doc
.select(".aw_teaser_title_zusatz").text().trim()));
}
String title = "";
String text = "";
boolean takeover = false;
Element detailtrs = doc2.select(".box-container .data td").first();
for (Node node : detailtrs.childNodes()) {
if (node instanceof Element) {
Element element = (Element) node;
if (element.tagName().equals("strong")) {
if (element.hasClass("c2")) {
if (!title.equals("")) {
result.addDetail(new Detail(title, text.trim()));
}
title = element.text().trim();
text = "";
} else {
text = text + element.text();
}
} else {
if (element.tagName().equals("a")) {
if (element.text().trim().contains("hier klicken") ||
title.contains("Link")) {
text = text + node.attr("href");
takeover = true;
break;
} else {
text = text + element.text();
}
}
}
} else if (node instanceof TextNode) {
text = text + ((TextNode) node).text();
}
}
if (!takeover) {
text = "";
title = "";
}
detailtrs = doc2.select("#tab-content .data td").first();
if (detailtrs != null) {
for (Node node : detailtrs.childNodes()) {
if (node instanceof Element) {
if (((Element) node).tagName().equals("strong")) {
if (!text.equals("") && !title.equals("")) {
result.addDetail(new Detail(title.trim(), text.trim()));
if (title.equals("Titel:")) {
result.setTitle(text.trim());
}
text = "";
}
title = ((Element) node).text().trim();
} else {
if (((Element) node).tagName().equals("a")
&& (((Element) node).text().trim()
.contains("hier klicken") || title
.equals("Link:"))) {
text = text + node.attr("href");
} else {
text = text + ((Element) node).text();
}
}
} else if (node instanceof TextNode) {
text = text + ((TextNode) node).text();
}
}
} else {
if (doc2.select("#tab-content .fulltitle tr").size() > 0) {
Elements rows = doc2.select("#tab-content .fulltitle tr");
for (Element tr : rows) {
if (tr.children().size() == 2) {
Element valcell = tr.child(1);
String value = valcell.text().trim();
if (valcell.select("a").size() == 1) {
value = valcell.select("a").first().absUrl("href");
}
result.addDetail(new Detail(tr.child(0).text().trim(),
value));
}
}
} else {
result.addDetail(new Detail(stringProvider
.getString(StringProvider.ERROR), stringProvider
.getString(StringProvider.COULD_NOT_LOAD_DETAIL)));
}
}
if (!text.equals("") && !title.equals("")) {
result.addDetail(new Detail(title.trim(), text.trim()));
if (title.equals("Titel:")) {
result.setTitle(text.trim());
}
}
for (Element link : doc3.select("#tab-content a")) {
Map<String, String> hrefq = getQueryParamsFirst(link.absUrl("href"));
if (result.getId() == null) {
// ID retrieval
String key = hrefq.get("katkey");
if (key != null) {
result.setId(key);
break;
}
}
}
for (Element link : doc3.select(".box-container a")) {
if (link.text().trim().equals("Download")) {
result.addDetail(new Detail(stringProvider
.getString(StringProvider.DOWNLOAD), link
.absUrl("href")));
}
}
Map<String, Integer> copy_columnmap = new HashMap<>();
// Default values
copy_columnmap.put("barcode", 1);
copy_columnmap.put("branch", 3);
copy_columnmap.put("status", 4);
Elements copy_columns = doc.select("#tab-content .data tr#bg2 th");
for (int i = 0; i < copy_columns.size(); i++) {
Element th = copy_columns.get(i);
String head = th.text().trim();
if (head.contains("Status")) {
copy_columnmap.put("status", i);
}
if (head.contains("Zweigstelle")) {
copy_columnmap.put("branch", i);
}
if (head.contains("Mediennummer")) {
copy_columnmap.put("barcode", i);
}
if (head.contains("Standort")) {
copy_columnmap.put("location", i);
}
if (head.contains("Signatur")) {
copy_columnmap.put("signature", i);
}
}
Pattern status_lent = Pattern
.compile(
"^(entliehen) bis ([0-9]{1,2}.[0-9]{1,2}.[0-9]{2," +
"4}) \\(gesamte Vormerkungen: ([0-9]+)\\)$");
Pattern status_and_barcode = Pattern.compile("^(.*) ([0-9A-Za-z]+)$");
Elements exemplartrs = doc.select("#tab-content .data tr").not("#bg2");
DateTimeFormatter fmt =
DateTimeFormat.forPattern("dd.MM.yyyy").withLocale(Locale.GERMAN);
for (Element tr : exemplartrs) {
try {
Copy copy = new Copy();
Element status = tr.child(copy_columnmap.get("status"));
Element barcode = tr.child(copy_columnmap.get("barcode"));
String barcodetext = barcode.text().trim()
.replace(" Wegweiser", "");
// STATUS
String statustext;
if (status.getElementsByTag("b").size() > 0) {
statustext = status.getElementsByTag("b").text().trim();
} else {
statustext = status.text().trim();
}
if (copy_columnmap.get("status").equals(copy_columnmap
.get("barcode"))) {
Matcher matcher1 = status_and_barcode.matcher(statustext);
if (matcher1.matches()) {
statustext = matcher1.group(1);
barcodetext = matcher1.group(2);
}
}
Matcher matcher = status_lent.matcher(statustext);
if (matcher.matches()) {
copy.setStatus(matcher.group(1));
copy.setReservations(matcher.group(3));
copy.setReturnDate(fmt.parseLocalDate(matcher.group(2)));
} else {
copy.setStatus(statustext);
}
copy.setBarcode(barcodetext);
if (status.select("a[href*=doVormerkung]").size() == 1) {
copy.setResInfo(status.select("a[href*=doVormerkung]").attr("href")
.split("\\?")[1]);
}
String branchtext = tr
.child(copy_columnmap.get("branch")).text()
.trim().replace(" Wegweiser", "");
copy.setBranch(branchtext);
if (copy_columnmap.containsKey("location")) {
copy.setLocation(tr.child(copy_columnmap.get("location"))
.text().trim().replace(" Wegweiser", ""));
}
if (copy_columnmap
.containsKey("signature")) {
copy.setShelfmark(
tr.child(copy_columnmap.get("signature"))
.text().trim().replace(" Wegweiser", ""));
}
result.addCopy(copy);
} catch (Exception ex) {
ex.printStackTrace();
}
}
try {
Element isvolume = null;
Map<String, String> volume = new HashMap<>();
Elements links = doc.select(".data td a");
int elcount = links.size();
for (int eli = 0; eli < elcount; eli++) {
List<NameValuePair> anyurl = URLEncodedUtils.parse(new URI(
links.get(eli).attr("href")), "UTF-8");
for (NameValuePair nv : anyurl) {
if (nv.getName().equals("methodToCall")
&& nv.getValue().equals("volumeSearch")) {
isvolume = links.get(eli);
} else if (nv.getName().equals("catKey")) {
volume.put("catKey", nv.getValue());
} else if (nv.getName().equals("dbIdentifier")) {
volume.put("dbIdentifier", nv.getValue());
}
}
if (isvolume != null) {
volume.put("volume", "true");
result.setVolumesearch(volume);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
@Override
public ReservationResult reservation(DetailedItem item, Account acc,
int useraction, String selection) throws IOException {
String reservation_info = item.getReservation_info();
final String branch_inputfield = "issuepoint";
Document doc = null;
String action = "reservation";
if (reservation_info.contains("doBestellung")) {
action = "order";
}
if (useraction == MultiStepResult.ACTION_CONFIRMATION) {
List<NameValuePair> nameValuePairs = new ArrayList<>(2);
nameValuePairs.add(new BasicNameValuePair("methodToCall", action));
nameValuePairs.add(new BasicNameValuePair("CSId", CSId));
String html = httpPost(opac_url + "/" + action + ".do",
new UrlEncodedFormEntity(nameValuePairs), ENCODING);
doc = Jsoup.parse(html);
} else if (selection == null || useraction == 0) {
String html = httpGet(opac_url + "/availability.do?"
+ reservation_info, ENCODING);
doc = Jsoup.parse(html);
if (doc.select("input[name=username]").size() > 0) {
// Login vonnöten
List<NameValuePair> nameValuePairs = new ArrayList<>(
2);
nameValuePairs.add(new BasicNameValuePair("username", acc
.getName()));
nameValuePairs.add(new BasicNameValuePair("password", acc
.getPassword()));
nameValuePairs.add(new BasicNameValuePair("methodToCall",
"submit"));
nameValuePairs.add(new BasicNameValuePair("CSId", CSId));
nameValuePairs.add(new BasicNameValuePair("login_action",
"Login"));
html = handleLoginMessage(httpPost(opac_url + "/login.do",
new UrlEncodedFormEntity(nameValuePairs), ENCODING));
doc = Jsoup.parse(html);
if (doc.getElementsByClass("error").size() == 0) {
logged_in = System.currentTimeMillis();
logged_in_as = acc;
}
}
if (doc.select("input[name=expressorder]").size() > 0) {
List<NameValuePair> nameValuePairs = new ArrayList<>(
2);
nameValuePairs.add(new BasicNameValuePair(branch_inputfield,
selection));
nameValuePairs.add(new BasicNameValuePair("methodToCall",
action));
nameValuePairs.add(new BasicNameValuePair("CSId", CSId));
nameValuePairs.add(new BasicNameValuePair("expressorder", " "));
html = httpPost(opac_url + "/" + action + ".do",
new UrlEncodedFormEntity(nameValuePairs), ENCODING);
doc = Jsoup.parse(html);
}
if (doc.select("input[name=" + branch_inputfield + "]").size() > 0) {
List<Map<String, String>> branches = new ArrayList<>();
for (Element option : doc
.select("input[name=" + branch_inputfield + "]")
.first().parent().parent().parent().select("td")) {
if (option.select("input").size() != 1) {
continue;
}
String value = option.text().trim();
String key = option.select("input").val();
Map<String, String> selopt = new HashMap<>();
selopt.put("key", key);
selopt.put("value", value);
branches.add(selopt);
}
ReservationResult result = new ReservationResult(
MultiStepResult.Status.SELECTION_NEEDED);
result.setActionIdentifier(ReservationResult.ACTION_BRANCH);
result.setSelection(branches);
return result;
}
} else if (useraction == ReservationResult.ACTION_BRANCH) {
List<NameValuePair> nameValuePairs = new ArrayList<>(2);
nameValuePairs.add(new BasicNameValuePair(branch_inputfield,
selection));
nameValuePairs.add(new BasicNameValuePair("methodToCall", action));
nameValuePairs.add(new BasicNameValuePair("CSId", CSId));
String html = httpPost(opac_url + "/" + action + ".do",
new UrlEncodedFormEntity(nameValuePairs), ENCODING);
doc = Jsoup.parse(html);
}
if (doc == null) {
return new ReservationResult(MultiStepResult.Status.ERROR);
}
if (doc.getElementsByClass("error").size() >= 1) {
return new ReservationResult(MultiStepResult.Status.ERROR, doc
.getElementsByClass("error").get(0).text());
}
if (doc.select("#CirculationForm p").size() > 0
&& doc.select("input[type=button]").size() >= 2) {
List<String[]> details = new ArrayList<>();
for (String row : doc.select("#CirculationForm p").first().html()
.split("<br>")) {
Document frag = Jsoup.parseBodyFragment(row);
if (frag.text().contains(":")) {
String[] split = frag.text().split(":");
if (split.length >= 2) {
details.add(new String[]{split[0].trim() + ":",
split[1].trim()});
}
} else {
details.add(new String[]{"", frag.text().trim()});
}
}
ReservationResult result = new ReservationResult(
Status.CONFIRMATION_NEEDED);
result.setDetails(details);
return result;
}
if (doc.select("#CirculationForm .textrot").size() >= 1) {
String errmsg = doc.select("#CirculationForm .textrot").get(0).text();
if (errmsg
.contains("Dieses oder andere Exemplare in anderer Zweigstelle ausleihbar")) {
Copy best = null;
for (Copy copy : item.getCopies()) {
if (copy.getResInfo() == null) {
continue;
}
if (best == null) {
best = copy;
continue;
}
try {
if (Integer.parseInt(copy.getReservations()) <
Long.parseLong(best.getReservations())) {
best = copy;
} else if (Integer.parseInt(copy
.getReservations()) == Long
.parseLong(best.getReservations())) {
if (copy.getReturnDate().isBefore(best.getReturnDate())) {
best = copy;
}
}
} catch (NumberFormatException e) {
}
}
if (best != null) {
item.setReservation_info(best
.getResInfo());
return reservation(item, acc, 0, null);
}
}
return new ReservationResult(MultiStepResult.Status.ERROR, errmsg);
}
if (doc.select("#CirculationForm td[colspan=2] strong").size() >= 1) {
return new ReservationResult(MultiStepResult.Status.OK, doc
.select("#CirculationForm td[colspan=2] strong").get(0)
.text());
}
return new ReservationResult(Status.OK);
}
@Override
public ProlongResult prolong(String a, Account account, int useraction,
String Selection) throws IOException {
// Internal convention: a is either a § followed by an error message or
// the URI of the page this item was found on and the query string the
// prolonging link links to, seperated by a $.
if (a.startsWith("§")) {
return new ProlongResult(MultiStepResult.Status.ERROR,
a.substring(1));
}
String[] parts = a.split("\\$");
String offset = parts[0];
String query = parts[1];
if (!initialised) {
start();
}
if (System.currentTimeMillis() - logged_in > SESSION_LIFETIME
|| logged_in_as == null) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
return new ProlongResult(MultiStepResult.Status.ERROR);
} catch (OpacErrorException e) {
return new ProlongResult(MultiStepResult.Status.ERROR,
e.getMessage());
}
} else if (logged_in_as.getId() != account.getId()) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
return new ProlongResult(MultiStepResult.Status.ERROR);
} catch (OpacErrorException e) {
return new ProlongResult(MultiStepResult.Status.ERROR,
e.getMessage());
}
}
// We have to call the page we originally found the link on first...
httpGet(opac_url + "/userAccount.do?methodToCall=showAccount&typ=1",
ENCODING);
if (!offset.equals("1")) {
httpGet(opac_url
+ "/userAccount.do?methodToCall=pos&accountTyp=AUSLEIHEN&anzPos="
+ offset, ENCODING);
}
String html = httpGet(opac_url + "/userAccount.do?" + query, ENCODING);
Document doc = Jsoup.parse(html);
if (doc.select("#middle .textrot").size() > 0) {
return new ProlongResult(MultiStepResult.Status.ERROR, doc
.select("#middle .textrot").first().text());
}
return new ProlongResult(MultiStepResult.Status.OK);
}
@Override
public CancelResult cancel(String media, Account account, int useraction,
String selection) throws IOException, OpacErrorException {
if (!initialised) {
start();
}
String[] parts = media.split("\\$");
String type = parts[0];
String offset = parts[1];
String query = parts[2];
if (System.currentTimeMillis() - logged_in > SESSION_LIFETIME
|| logged_in_as == null) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
throw new OpacErrorException(
stringProvider.getString(StringProvider.INTERNAL_ERROR));
}
} else if (logged_in_as.getId() != account.getId()) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
throw new OpacErrorException(
stringProvider.getString(StringProvider.INTERNAL_ERROR));
}
}
// We have to call the page we originally found the link on first...
httpGet(opac_url + "/userAccount.do?methodToCall=showAccount&typ="
+ type, ENCODING);
if (!offset.equals("1")) {
httpGet(opac_url + "/userAccount.do?methodToCall=pos&anzPos="
+ offset, ENCODING);
}
httpGet(opac_url + "/userAccount.do?" + query, ENCODING);
return new CancelResult(MultiStepResult.Status.OK);
}
protected String handleLoginMessage(String html)
throws IOException {
if (html.contains("methodToCall=done")) {
return httpGet(opac_url + "/login.do?methodToCall=done", ENCODING);
} else {
return html;
}
}
protected boolean login(Account acc) throws OpacErrorException {
String html;
List<NameValuePair> nameValuePairs = new ArrayList<>(2);
try {
String loginPage;
loginPage = httpGet(opac_url
+ "/userAccount.do?methodToCall=show&type=1", ENCODING);
Document loginPageDoc = Jsoup.parse(loginPage);
if (loginPageDoc.select("input[name=as_fid]").size() > 0) {
nameValuePairs.add(new BasicNameValuePair("as_fid",
loginPageDoc.select("input[name=as_fid]").first()
.attr("value")));
}
} catch (IOException e1) {
e1.printStackTrace();
}
nameValuePairs.add(new BasicNameValuePair("username", acc.getName()));
nameValuePairs
.add(new BasicNameValuePair("password", acc.getPassword()));
nameValuePairs.add(new BasicNameValuePair("CSId", CSId));
nameValuePairs.add(new BasicNameValuePair("methodToCall", "submit"));
try {
html = handleLoginMessage(httpPost(opac_url + "/login.do",
new UrlEncodedFormEntity(nameValuePairs), ENCODING));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return false;
} catch (ClientProtocolException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
Document doc = Jsoup.parse(html);
if (doc.getElementsByClass("error").size() > 0) {
throw new OpacErrorException(doc.getElementsByClass("error").get(0)
.text());
}
logged_in = System.currentTimeMillis();
logged_in_as = acc;
return true;
}
public static void parse_medialist(List<LentItem> media, Document doc, int offset,
JSONObject data) {
Elements copytrs = doc.select(".data tr");
doc.setBaseUri(data.optString("baseurl"));
DateTimeFormatter fmt = DateTimeFormat.forPattern("dd.MM.yyyy").withLocale(Locale.GERMAN);
int trs = copytrs.size();
if (trs == 1) {
return;
}
assert (trs > 0);
for (int i = 1; i < trs; i++) {
Element tr = copytrs.get(i);
LentItem item = new LentItem();
if (tr.text().contains("keine Daten") || (trs == 2 && tr.children().size() == 1)) {
// Dresden: Konto entält keine <Ausleihen>. [sic!]
return;
}
item.setTitle(tr.child(1).select("strong").text().trim());
try {
item.setAuthor(tr.child(1).html().split("<br[ /]*>")[1].trim());
String[] col2split = tr.child(2).html().split("<br[ /]*>");
String deadline = col2split[0].trim();
if (deadline.contains("-")) {
deadline = deadline.split("-")[1].trim();
}
try {
item.setDeadline(fmt.parseLocalDate(deadline).toString());
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
}
if (col2split.length > 1) {
item.setHomeBranch(col2split[1].trim());
}
if (tr.select("a").size() > 0) {
for (Element link : tr.select("a")) {
String href = link.attr("abs:href");
Map<String, String> hrefq = getQueryParamsFirst(href);
if (hrefq.get("methodToCall").equals("renewalPossible")) {
item.setProlongData(offset + "$" + href.split("\\?")[1]);
item.setRenewable(true);
break;
}
}
} else if (tr.select(".textrot, .textgruen, .textdunkelblau")
.size() > 0) {
item.setProlongData(
"§" + tr.select(".textrot, .textgruen, .textdunkelblau").text());
item.setRenewable(false);
}
} catch (Exception ex) {
ex.printStackTrace();
}
media.add(item);
}
assert (media.size() == trs - 1);
}
protected void parse_reslist(String type,
List<ReservedItem> reservations, Document doc, int offset) {
Elements copytrs = doc.select(".data tr");
doc.setBaseUri(opac_url);
int trs = copytrs.size();
if (trs == 1) {
return;
}
assert (trs > 0);
for (int i = 1; i < trs; i++) {
Element tr = copytrs.get(i);
ReservedItem item = new ReservedItem();
if (tr.text().contains("keine Daten") || tr.children().size() == 1) {
return;
}
item.setTitle(tr.child(1).select("strong").text().trim());
try {
String[] rowsplit1 = tr.child(1).html().split("<br[ /]*>");
String[] rowsplit2 = tr.child(2).html().split("<br[ /]*>");
if (rowsplit1.length > 1) item.setAuthor(rowsplit1[1].trim());
if (rowsplit2.length > 2) item.setBranch(rowsplit2[2].trim());
if (rowsplit2.length > 2) item.setStatus(rowsplit2[0].trim());
if (tr.select("a").size() == 1) {
item.setCancelData(type + "$" + offset + "$" +
tr.select("a").attr("abs:href").split("\\?")[1]);
}
} catch (Exception e) {
e.printStackTrace();
}
reservations.add(item);
}
assert (reservations.size() == trs - 1);
}
@Override
public AccountData account(Account acc) throws IOException,
JSONException,
OpacErrorException {
start(); // TODO: Is this necessary?
int resultNum;
if (!login(acc)) {
return null;
}
// Geliehene Medien
String html = httpGet(opac_url
+ "/userAccount.do?methodToCall=showAccount&typ=1", ENCODING);
List<LentItem> medien = new ArrayList<>();
Document doc = Jsoup.parse(html);
doc.setBaseUri(opac_url);
parse_medialist(medien, doc, 1, data);
if (doc.select(".box-right").size() > 0) {
for (Element link : doc.select(".box-right").first().select("a")) {
String href = link.attr("abs:href");
Map<String, String> hrefq = getQueryParamsFirst(href);
if (hrefq == null || hrefq.get("methodToCall") == null) {
continue;
}
if (hrefq.get("methodToCall").equals("pos")
&& !"1".equals(hrefq.get("anzPos"))) {
html = httpGet(href, ENCODING);
parse_medialist(medien, Jsoup.parse(html),
Integer.parseInt(hrefq.get("anzPos")), data);
}
}
}
if (doc.select("#label1").size() > 0) {
resultNum = 0;
String rNum = doc.select("#label1").first().text().trim()
.replaceAll(".*\\(([0-9]*)\\).*", "$1");
if (rNum.length() > 0) {
resultNum = Integer.parseInt(rNum);
}
assert (resultNum == medien.size());
}
// Ordered media ("Bestellungen")
html = httpGet(opac_url + "/userAccount.do?methodToCall=showAccount&typ=6", ENCODING);
List<ReservedItem> reserved = new ArrayList<>();
doc = Jsoup.parse(html);
doc.setBaseUri(opac_url);
parse_reslist("6", reserved, doc, 1);
Elements label6 = doc.select("#label6");
if (doc.select(".box-right").size() > 0) {
for (Element link : doc.select(".box-right").first().select("a")) {
String href = link.attr("abs:href");
Map<String, String> hrefq = getQueryParamsFirst(href);
if (hrefq == null || hrefq.get("methodToCall") == null) {
break;
}
if (hrefq.get("methodToCall").equals("pos")
&& !"1".equals(hrefq.get("anzPos"))) {
html = httpGet(href, ENCODING);
parse_reslist("6", reserved, Jsoup.parse(html),
Integer.parseInt(hrefq.get("anzPos")));
}
}
}
// Prebooked media ("Vormerkungen")
html = httpGet(opac_url
+ "/userAccount.do?methodToCall=showAccount&typ=7", ENCODING);
doc = Jsoup.parse(html);
doc.setBaseUri(opac_url);
parse_reslist("7", reserved, doc, 1);
if (doc.select(".box-right").size() > 0) {
for (Element link : doc.select(".box-right").first().select("a")) {
String href = link.attr("abs:href");
Map<String, String> hrefq = getQueryParamsFirst(href);
if (hrefq == null || hrefq.get("methodToCall") == null) {
break;
}
if (hrefq.get("methodToCall").equals("pos")
&& !"1".equals(hrefq.get("anzPos"))) {
html = httpGet(href, ENCODING);
parse_reslist("7", reserved, Jsoup.parse(html),
Integer.parseInt(hrefq.get("anzPos")));
}
}
}
if (label6.size() > 0 && doc.select("#label7").size() > 0) {
resultNum = 0;
String rNum = label6.text().trim()
.replaceAll(".*\\(([0-9]*)\\).*", "$1");
if (rNum.length() > 0) {
resultNum = Integer.parseInt(rNum);
}
rNum = doc.select("#label7").text().trim()
.replaceAll(".*\\(([0-9]*)\\).*", "$1");
if (rNum.length() > 0) {
resultNum += Integer.parseInt(rNum);
}
assert (resultNum == reserved.size());
}
AccountData res = new AccountData(acc.getId());
if (doc.select("#label8").size() > 0) {
String text = doc.select("#label8").first().text().trim();
if (text.matches("Geb.+hren[^\\(]+\\(([0-9.,]+)[^0-9€A-Z]*(€|EUR|CHF|Fr)\\)")) {
text = text
.replaceAll(
"Geb.+hren[^\\(]+\\(([0-9.,]+)[^0-9€A-Z]*(€|EUR|CHF|Fr)\\)",
"$1 $2");
res.setPendingFees(text);
}
}
Pattern p = Pattern.compile("[^0-9.]*", Pattern.MULTILINE);
if (doc.select(".box3").size() > 0) {
for (Element box : doc.select(".box3")) {
if (box.select("strong").size() == 1) {
String text = box.select("strong").text();
if (text.equals("Jahresgebühren")) {
text = box.text();
text = p.matcher(text).replaceAll("");
res.setValidUntil(text);
}
}
}
}
res.setLent(medien);
res.setReservations(reserved);
return res;
}
@Override
public String getShareUrl(String id, String title) {
String startparams = "";
if (data.has("startparams")) {
try {
startparams = data.getString("startparams") + "&";
} catch (JSONException e) {
e.printStackTrace();
}
}
if (id != null && !id.equals("")) {
return opac_url + "/start.do?" + startparams
+ "searchType=1&Query=0%3D%22" + id + "%22";
} else {
try {
title = URLEncoder.encode(title, getDefaultEncoding());
} catch (UnsupportedEncodingException e) {
//noinspection deprecation
title = URLEncoder.encode(title);
}
return opac_url + "/start.do?" + startparams
+ "searchType=1&Query=-1%3D%22" + title + "%22";
}
}
@Override
public int getSupportFlags() {
int flags = SUPPORT_FLAG_ACCOUNT_PROLONG_ALL
| SUPPORT_FLAG_CHANGE_ACCOUNT;
flags |= SUPPORT_FLAG_ENDLESS_SCROLLING;
return flags;
}
@Override
public ProlongAllResult prolongAll(Account account, int useraction,
String selection) throws IOException {
if (!initialised) {
start();
}
if (System.currentTimeMillis() - logged_in > SESSION_LIFETIME
|| logged_in_as == null) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
return new ProlongAllResult(MultiStepResult.Status.ERROR);
} catch (OpacErrorException e) {
return new ProlongAllResult(MultiStepResult.Status.ERROR,
e.getMessage());
}
} else if (logged_in_as.getId() != account.getId()) {
try {
account(account);
} catch (JSONException e) {
e.printStackTrace();
return new ProlongAllResult(MultiStepResult.Status.ERROR);
} catch (OpacErrorException e) {
return new ProlongAllResult(MultiStepResult.Status.ERROR,
e.getMessage());
}
}
// We have to call the page we originally found the link on first...
String html = httpGet(
opac_url
+ "/userAccount.do?methodToCall=renewalPossible&renewal=account",
ENCODING);
Document doc = Jsoup.parse(html);
if (doc.select("table.data").size() > 0) {
List<Map<String, String>> result = new ArrayList<>();
for (Element td : doc.select("table.data tr td")) {
Map<String, String> line = new HashMap<>();
if (!td.text().contains("Titel")
|| !td.text().contains("Status")) {
continue;
}
String nextNodeIs = "";
for (Node n : td.childNodes()) {
String text;
if (n instanceof Element) {
text = ((Element) n).text();
} else if (n instanceof TextNode) {
text = ((TextNode) n).text();
} else {
continue;
}
if (text.trim().length() == 0) {
continue;
}
if (text.contains("Titel:")) {
nextNodeIs = ProlongAllResult.KEY_LINE_TITLE;
} else if (text.contains("Verfasser:")) {
nextNodeIs = ProlongAllResult.KEY_LINE_AUTHOR;
} else if (text.contains("Leihfristende:")) {
nextNodeIs = ProlongAllResult.KEY_LINE_NEW_RETURNDATE;
} else if (text.contains("Status:")) {
nextNodeIs = ProlongAllResult.KEY_LINE_MESSAGE;
} else if (text.contains("Mediennummer:")
|| text.contains("Signatur:")) {
nextNodeIs = "";
} else if (nextNodeIs.length() > 0) {
line.put(nextNodeIs, text.trim());
nextNodeIs = "";
}
}
result.add(line);
}
return new ProlongAllResult(MultiStepResult.Status.OK, result);
}
return new ProlongAllResult(MultiStepResult.Status.ERROR,
stringProvider.getString(StringProvider.COULD_NOT_LOAD_ACCOUNT));
}
@Override
public SearchRequestResult filterResults(Filter filter, Option option)
throws IOException, OpacErrorException {
// TODO Auto-generated method stub
return null;
}
@Override
public void checkAccountData(Account account) throws IOException,
JSONException, OpacErrorException {
start(); // TODO: Is this necessary?
boolean success = login(account);
if (!success) {
throw new NotReachableException("Login unsuccessful");
}
}
@Override
public void setLanguage(String language) {
// TODO Auto-generated method stub
}
@Override
public Set<String> getSupportedLanguages() throws IOException {
// TODO Auto-generated method stub
return null;
}
}