Java tutorial
/** * 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.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; 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.Comment; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; 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.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; import de.geeksfactory.opacclient.i18n.StringProvider; import de.geeksfactory.opacclient.networking.HttpClientFactory; import de.geeksfactory.opacclient.networking.HttpUtils; 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.DetailledItem; 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.objects.Volume; import de.geeksfactory.opacclient.searchfields.DropdownSearchField; import de.geeksfactory.opacclient.searchfields.SearchField; import de.geeksfactory.opacclient.searchfields.SearchField.Meaning; import de.geeksfactory.opacclient.searchfields.SearchQuery; import de.geeksfactory.opacclient.searchfields.TextSearchField; import de.geeksfactory.opacclient.utils.JsonKeyIterator; /** * OpacApi implementation for Bibliotheca Web Opacs, originally developed by BOND, now owned by * OCLC. Known to work well with Web Opac versions from 2.6, maybe older, to 2.8 */ public class Bibliotheca extends BaseApi { protected static HashMap<String, MediaType> defaulttypes = new HashMap<>(); static { defaulttypes.put("mbuchs", MediaType.BOOK); defaulttypes.put("cdkl", MediaType.CD); defaulttypes.put("cd", MediaType.CD); defaulttypes.put("cdromkl", MediaType.CD_SOFTWARE); defaulttypes.put("mcdroms", MediaType.CD); defaulttypes.put("ekl", MediaType.EBOOK); defaulttypes.put("emedium", MediaType.EBOOK); defaulttypes.put("monleihe", MediaType.EBOOK); defaulttypes.put("mdivis", MediaType.EBOOK); defaulttypes.put("mbmonos", MediaType.PACKAGE_BOOKS); defaulttypes.put("mbuechers", MediaType.PACKAGE_BOOKS); defaulttypes.put("mdvds", MediaType.DVD); defaulttypes.put("mdvd", MediaType.DVD); defaulttypes.put("blu-ray--disc_s_35x35", MediaType.BLURAY); defaulttypes.put("mblurays", MediaType.BLURAY); defaulttypes.put("mfilms", MediaType.MOVIE); defaulttypes.put("mvideos", MediaType.MOVIE); defaulttypes.put("mhoerbuchs", MediaType.AUDIOBOOK); defaulttypes.put("mmusikcds", MediaType.CD_MUSIC); defaulttypes.put("mcdns", MediaType.CD_MUSIC); defaulttypes.put("mnoten1s", MediaType.SCORE_MUSIC); defaulttypes.put("munselbs", MediaType.UNKNOWN); defaulttypes.put("mztgs", MediaType.NEWSPAPER); defaulttypes.put("zeitung", MediaType.NEWSPAPER); defaulttypes.put("spielekl", MediaType.BOARDGAME); defaulttypes.put("mspiels", MediaType.BOARDGAME); defaulttypes.put("tafelkl", MediaType.SCHOOL_VERSION); defaulttypes.put("spiel_konsol", MediaType.GAME_CONSOLE); defaulttypes.put("wii", MediaType.GAME_CONSOLE); } protected final long SESSION_LIFETIME = 1000 * 60 * 3; protected String opac_url = ""; protected JSONObject data; protected Library library; protected long logged_in; protected Account logged_in_as; protected String _res_target = "vorbesttranskonto"; protected String branch_inputfield = "zstauswahl"; @Override public List<SearchField> getSearchFields() throws IOException, JSONException { if (!initialised) { start(); } List<SearchField> fields = new ArrayList<>(); // Read branches and media types List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("link_profis.x", "0")); nameValuePairs.add(new BasicNameValuePair("link_profis.y", "1")); String html = httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); Document doc = Jsoup.parse(html); Elements fieldElems = doc.select(".suchfeldinhalt"); for (Element fieldElem : fieldElems) { String name = fieldElem.select(".suchfeld_inhalt_titel label").text(); String hint = ""; if (fieldElem.select(".suchfeld_inhalt_input").size() > 0) { List<TextNode> textNodes = fieldElem.select(".suchfeld_inhalt_input").first().textNodes(); if (textNodes.size() > 0) { for (TextNode node : textNodes) { String text = node.getWholeText().replace("\n", ""); if (!text.equals("")) { hint = node.getWholeText().replace("\n", ""); break; } } } } Elements inputs = fieldElem .select(".suchfeld_inhalt_input input[type=text], " + ".suchfeld_inhalt_input select"); if (inputs.size() == 1) { fields.add(createSearchField(name, hint, inputs.get(0))); } else if (inputs.size() == 2 && inputs.select("input[type=text]").size() == 2) { // Two text fields, e.g. year from/to or two keywords fields.add(createSearchField(name, hint, inputs.get(0))); TextSearchField secondField = (TextSearchField) createSearchField(name, hint, inputs.get(1)); secondField.setHalfWidth(true); fields.add(secondField); } else if (inputs.size() == 2 && inputs.get(0).tagName().equals("select") && inputs.get(1).tagName().equals("input") && inputs.get(0).attr("name").equals("feld1")) { // A dropdown to select from different search field types. // Break it down into single text fields. for (Element option : inputs.get(0).select("option")) { TextSearchField field = new TextSearchField(); field.setHint(hint); field.setDisplayName(option.text()); field.setId(inputs.get(1).attr("name") + "$" + option.attr("value")); JSONObject data = new JSONObject(); JSONObject params = new JSONObject(); params.put(inputs.get(0).attr("name"), option.attr("value")); data.put("additional_params", params); field.setData(data); fields.add(field); } } } DropdownSearchField orderField = new DropdownSearchField("orderselect", stringProvider.getString(StringProvider.ORDER), false, null); orderField.addDropdownValue("1", stringProvider.getString(StringProvider.ORDER_DEFAULT)); orderField.addDropdownValue("2:desc", stringProvider.getString(StringProvider.ORDER_YEAR_DESC)); orderField.addDropdownValue("2:asc", stringProvider.getString(StringProvider.ORDER_YEAR_ASC)); orderField.addDropdownValue("3:desc", stringProvider.getString(StringProvider.ORDER_CATEGORY_DESC)); orderField.addDropdownValue("3:asc", stringProvider.getString(StringProvider.ORDER_CATEGORY_ASC)); orderField.setMeaning(Meaning.ORDER); fields.add(orderField); return fields; } private SearchField createSearchField(String name, String hint, Element input) { if (input.tagName().equals("input") && input.attr("type").equals("text")) { TextSearchField field = new TextSearchField(); field.setDisplayName(name); field.setHint(hint); field.setId(input.attr("name")); return field; } else if (input.tagName().equals("select")) { DropdownSearchField field = new DropdownSearchField(); field.setDisplayName(name); field.setId(input.attr("name")); for (Element option : input.select("option")) { field.addDropdownValue(option.attr("value"), option.text()); } return field; } else { return null; } } @Override public void start() throws IOException { String db = ""; if (data.has("db")) { try { db = "&db=" + data.getString("db"); } catch (JSONException e) { e.printStackTrace(); } } httpGet(opac_url + "/woload.asp?lkz=1&nextpage=" + db, getDefaultEncoding()); super.start(); } @Override public void init(Library lib, HttpClientFactory httpClientFactory) { super.init(lib, httpClientFactory); this.library = lib; this.data = lib.getData(); try { this.opac_url = data.getString("baseurl"); } catch (JSONException e) { throw new RuntimeException(e); } } @Override public SearchRequestResult search(List<SearchQuery> queries) throws IOException, JSONException, OpacErrorException { if (!initialised) { start(); } List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("stichtit", "stich")); int ifeldCount = 0; for (SearchQuery query : queries) { if (query.getValue().equals("")) { continue; } String key = query.getKey(); if (key.contains("$")) { key = key.substring(0, key.indexOf("$")); } if (key.contains("ifeld")) { ifeldCount++; if (ifeldCount > 1) { throw new OpacErrorException( stringProvider.getString(StringProvider.COMBINATION_NOT_SUPPORTED)); } } if (key.equals("orderselect") && query.getValue().contains(":")) { nameValuePairs.add(new BasicNameValuePair("orderselect", query.getValue().split(":")[0])); nameValuePairs.add(new BasicNameValuePair("order", query.getValue().split(":")[1])); } else { nameValuePairs.add(new BasicNameValuePair(key, query.getValue())); } if (query.getSearchField().getData() != null) { JSONObject data = query.getSearchField().getData(); if (data.has("additional_params")) { JSONObject params = data.getJSONObject("additional_params"); Iterator<String> keys = new JsonKeyIterator(params); while (keys.hasNext()) { String additionalKey = keys.next(); nameValuePairs.add(new BasicNameValuePair(additionalKey, params.getString(additionalKey))); } } } } nameValuePairs.add(new BasicNameValuePair("suche_starten.x", "1")); nameValuePairs.add(new BasicNameValuePair("suche_starten.y", "1")); if (data.has("db")) { try { nameValuePairs.add(new BasicNameValuePair("dbase", data.getString("db"))); } catch (JSONException e) { e.printStackTrace(); } } String html = httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); return parse_search(html, 1); } @Override public SearchRequestResult searchGetPage(int page) throws IOException { if (!initialised) { start(); } String html = httpGet(opac_url + "/index.asp?scrollAction=" + page, getDefaultEncoding()); return parse_search(html, page); } protected SearchRequestResult parse_search(String html, int page) { Document doc = Jsoup.parse(html); doc.setBaseUri(opac_url); Elements table = doc.select(".resulttab tr.result_trefferX, .resulttab tr.result_treffer"); List<SearchResult> results = new ArrayList<>(); for (int i = 0; i < table.size(); i++) { Element tr = table.get(i); SearchResult sr = new SearchResult(); int contentindex = 1; if (tr.select("td a img").size() > 0) { String[] fparts = tr.select("td a img").get(0).attr("src").split("/"); String fname = fparts[fparts.length - 1]; if (data.has("mediatypes")) { try { sr.setType(MediaType.valueOf(data.getJSONObject("mediatypes").getString(fname))); } catch (JSONException | IllegalArgumentException e) { sr.setType(defaulttypes.get(fname.toLowerCase(Locale.GERMAN).replace(".jpg", "") .replace(".gif", "").replace(".png", ""))); } } else { sr.setType(defaulttypes.get(fname.toLowerCase(Locale.GERMAN).replace(".jpg", "") .replace(".gif", "").replace(".png", ""))); } } else { if (tr.children().size() == 3) { contentindex = 2; } } sr.setInnerhtml(tr.child(contentindex).child(0).html()); sr.setNr(i); Element link = tr.child(contentindex).select("a").first(); try { if (link != null && link.attr("href").contains("detmediennr")) { Map<String, String> params = getQueryParamsFirst(link.attr("abs:href")); String nr = params.get("detmediennr"); if (Integer.parseInt(nr) > i + 1) { // Seems to be an ID if (params.get("detDB") != null) { sr.setId("&detmediennr=" + nr + "&detDB=" + params.get("detDB")); } else { sr.setId("&detmediennr=" + nr); } } } } catch (Exception e) { } try { if (tr.child(1).childNode(0) instanceof Comment) { Comment c = (Comment) tr.child(1).childNode(0); String comment = c.getData().trim(); String id = comment.split(": ")[1]; sr.setId(id); } } catch (Exception e) { e.printStackTrace(); } results.add(sr); } int results_total = -1; if (doc.select(".result_gefunden").size() > 0) { try { results_total = Integer.parseInt( doc.select(".result_gefunden").text().trim().replaceAll(".*[^0-9]+([0-9]+).*", "$1")); } catch (NumberFormatException e) { e.printStackTrace(); results_total = -1; } } return new SearchRequestResult(results, results_total, page); } @Override public DetailledItem getResultById(String a, String homebranch) throws IOException { if (!initialised) { start(); } String html = httpGet(opac_url + "/index.asp?MedienNr=" + a, getDefaultEncoding()); DetailledItem result = parse_result(html); if (result.getId() == null) { result.setId(a); } return result; } @Override public DetailledItem getResult(int nr) throws IOException { String html = httpGet(opac_url + "/index.asp?detmediennr=" + nr, getDefaultEncoding()); return parse_result(html); } protected DetailledItem parse_result(String html) { Document doc = Jsoup.parse(html); doc.setBaseUri(opac_url); DetailledItem result = new DetailledItem(); if (doc.select(".detail_cover img").size() == 1) { result.setCover(doc.select(".detail_cover img").get(0).attr("src")); } result.setTitle(doc.select(".detail_titel").text()); Elements detailtrs = doc.select(".detailzeile table tr"); for (int i = 0; i < detailtrs.size(); i++) { Element tr = detailtrs.get(i); if (tr.child(0).hasClass("detail_feld")) { String title = tr.child(0).text(); String content = tr.child(1).text(); if (title.equals("Gesamtwerk:") || title.equals("Erschienen in:")) { try { if (tr.child(1).select("a").size() > 0) { Element link = tr.child(1).select("a").first(); List<NameValuePair> query = URLEncodedUtils.parse(new URI(link.absUrl("href")), "UTF-8"); for (NameValuePair q : query) { if (q.getName().equals("MedienNr")) { result.setCollectionId(q.getValue()); } } } } catch (URISyntaxException e) { } } else { if (content.contains("hier klicken") && tr.child(1).select("a").size() > 0) { content += " " + tr.child(1).select("a").first().attr("href"); } result.addDetail(new Detail(title, content)); } } } Elements detailcenterlinks = doc.select(".detailzeile_center a.detail_link"); for (int i = 0; i < detailcenterlinks.size(); i++) { Element a = detailcenterlinks.get(i); result.addDetail(new Detail(a.text().trim(), a.absUrl("href"))); } try { JSONObject copymap = new JSONObject(); if (data.has("copiestable")) { copymap = data.getJSONObject("copiestable"); } else { Elements ths = doc.select(".exemplartab .exemplarmenubar th"); for (int i = 0; i < ths.size(); i++) { Element th = ths.get(i); String head = th.text().trim(); if (head.equals("Zweigstelle")) { copymap.put("branch", i); } else if (head.equals("Abteilung")) { copymap.put("department", i); } else if (head.equals("Bereich") || head.equals("Standort")) { copymap.put("location", i); } else if (head.equals("Signatur")) { copymap.put("signature", i); } else if (head.equals("Barcode") || head.equals("Medien-Nummer")) { copymap.put("barcode", i); } else if (head.equals("Status")) { copymap.put("status", i); } else if (head.equals("Frist") || head.matches("Verf.+gbar")) { copymap.put("returndate", i); } else if (head.equals("Vorbestellungen") || head.equals("Reservierungen")) { copymap.put("reservations", i); } } } Elements exemplartrs = doc.select(".exemplartab .tabExemplar, .exemplartab .tabExemplar_"); DateTimeFormatter fmt = DateTimeFormat.forPattern("dd.MM.yyyy").withLocale(Locale.GERMAN); for (int i = 0; i < exemplartrs.size(); i++) { Element tr = exemplartrs.get(i); Copy copy = new Copy(); Iterator<?> keys = copymap.keys(); while (keys.hasNext()) { String key = (String) keys.next(); int index; try { index = copymap.has(key) ? copymap.getInt(key) : -1; } catch (JSONException e1) { index = -1; } if (index >= 0) { try { copy.set(key, tr.child(index).text(), fmt); } catch (IllegalArgumentException e) { e.printStackTrace(); } } } result.addCopy(copy); } } catch (Exception e) { e.printStackTrace(); } try { Elements bandtrs = doc.select("table .tabBand a"); for (int i = 0; i < bandtrs.size(); i++) { Element tr = bandtrs.get(i); Volume volume = new Volume(); volume.setId(tr.attr("href").split("=")[1]); volume.setTitle(tr.text()); result.addVolume(volume); } } catch (Exception e) { e.printStackTrace(); } if (doc.select(".detail_vorbest a").size() == 1) { result.setReservable(true); result.setReservation_info(doc.select(".detail_vorbest a").attr("href")); } return result; } @Override public ReservationResult reservation(DetailledItem item, Account acc, int useraction, String selection) throws IOException { String reservation_info = item.getReservation_info(); Document doc = null; if (useraction == MultiStepResult.ACTION_CONFIRMATION) { List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("make_allvl", "Bestaetigung")); nameValuePairs.add(new BasicNameValuePair("target", "makevorbest")); httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); return new ReservationResult(MultiStepResult.Status.OK); } else if (selection == null || useraction == 0) { String html = httpGet(opac_url + "/" + reservation_info, getDefaultEncoding()); doc = Jsoup.parse(html); if (doc.select("input[name=AUSWEIS]").size() > 0) { // Needs login List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("AUSWEIS", acc.getName())); nameValuePairs.add(new BasicNameValuePair("PWD", acc.getPassword())); if (data.has("db")) { try { nameValuePairs.add(new BasicNameValuePair("vkontodb", data.getString("db"))); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } nameValuePairs.add(new BasicNameValuePair("B1", "weiter")); nameValuePairs.add(new BasicNameValuePair("target", doc.select("input[name=target]").val())); nameValuePairs.add(new BasicNameValuePair("type", "VT2")); html = httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); doc = Jsoup.parse(html); } if (doc.select("select[name=" + branch_inputfield + "]").size() == 0) { if (doc.select("select[name=VZST]").size() > 0) { branch_inputfield = "VZST"; } } if (doc.select("select[name=" + branch_inputfield + "]").size() > 0) { List<Map<String, String>> branches = new ArrayList<>(); for (Element option : doc.select("select[name=" + branch_inputfield + "]").first().children()) { String value = option.text().trim(); String key; if (option.hasAttr("value")) { key = option.attr("value"); } else { key = value; } Map<String, String> selopt = new HashMap<>(); selopt.put("key", key); selopt.put("value", value); branches.add(selopt); } _res_target = doc.select("input[name=target]").attr("value"); 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("button2", "weiter")); nameValuePairs.add(new BasicNameValuePair("target", _res_target)); String html = httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); doc = Jsoup.parse(html); } if (doc == null) { return new ReservationResult(MultiStepResult.Status.ERROR); } if (doc.select("input[name=target]").size() > 0) { if (doc.select("input[name=target]").attr("value").equals("makevorbest")) { List<String[]> details = new ArrayList<>(); if (doc.getElementsByClass("kontomeldung").size() == 1) { details.add(new String[] { doc.getElementsByClass("kontomeldung").get(0).text().trim() }); } Pattern p = Pattern.compile("geb.hr", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); for (Element div : doc.select(".kontozeile_center")) { for (String text : Jsoup.parse(div.html().replaceAll("(?i)<br[^>]*>", "br2n")).text() .split("br2n")) { if (p.matcher(text).find() && !text.contains("usstehend") && text.contains("orbestellung")) { details.add(new String[] { text.trim() }); } } } if (doc.select("#vorbest").size() > 0 && doc.select("#vorbest").val().contains("(")) { // Erlangen uses "Kostenpflichtige Vorbestellung (1 Euro)" // as the label of its reservation button details.add(new String[] { doc.select("#vorbest").val().trim() }); } for (Element row : doc.select(".kontozeile_center table tr")) { if (row.select(".konto_feld").size() == 1 && row.select(".konto_feldinhalt").size() == 1) { details.add(new String[] { row.select(".konto_feld").text().trim(), row.select(".konto_feldinhalt").text().trim() }); } } ReservationResult result = new ReservationResult(MultiStepResult.Status.CONFIRMATION_NEEDED); result.setDetails(details); return result; } } if (doc.getElementsByClass("kontomeldung").size() == 1) { return new ReservationResult(MultiStepResult.Status.ERROR, doc.getElementsByClass("kontomeldung").get(0).text()); } return new ReservationResult(MultiStepResult.Status.ERROR, stringProvider.getString(StringProvider.UNKNOWN_ERROR)); } @Override public ProlongResult prolong(String a, 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 ProlongResult(MultiStepResult.Status.ERROR, stringProvider.getString(StringProvider.COULD_NOT_LOAD_ACCOUNT)); } 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, stringProvider.getString(StringProvider.COULD_NOT_LOAD_ACCOUNT)); } catch (OpacErrorException e) { return new ProlongResult(MultiStepResult.Status.ERROR, e.getMessage()); } } if (useraction == MultiStepResult.ACTION_CONFIRMATION) { List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("target", "make_vl")); nameValuePairs.add(new BasicNameValuePair("verlaengern", "Besttigung")); httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); return new ProlongResult(MultiStepResult.Status.OK); } else { String html = httpGet(opac_url + "/" + a, getDefaultEncoding()); Document doc = Jsoup.parse(html); if (doc.getElementsByClass("kontomeldung").size() == 1) { return new ProlongResult(MultiStepResult.Status.ERROR, doc.getElementsByClass("kontomeldung").get(0).text()); } if (doc.select("#verlaengern").size() == 1) { if (doc.select(".kontozeile_center table").size() == 1) { Element table = doc.select(".kontozeile_center table").first(); ProlongResult res = new ProlongResult(MultiStepResult.Status.CONFIRMATION_NEEDED); List<String[]> details = new ArrayList<>(); for (Element row : table.select("tr")) { if (row.select(".konto_feld").size() == 1 && row.select(".konto_feldinhalt").size() == 1) { details.add(new String[] { row.select(".konto_feld").text().trim(), row.select(".konto_feldinhalt").text().trim() }); } } res.setDetails(details); return res; } else { List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("target", "make_vl")); nameValuePairs.add(new BasicNameValuePair("verlaengern", "Besttigung")); httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); return new ProlongResult(MultiStepResult.Status.OK); } } } return new ProlongResult(MultiStepResult.Status.ERROR, "??"); } @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, stringProvider.getString(StringProvider.CONNECTION_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, stringProvider.getString(StringProvider.CONNECTION_ERROR)); } catch (OpacErrorException e) { return new ProlongAllResult(MultiStepResult.Status.ERROR, e.getMessage()); } } String html = httpGet(opac_url + "/index.asp?target=alleverl", getDefaultEncoding()); Document doc = Jsoup.parse(html); if (doc.getElementsByClass("kontomeldung").size() == 1) { String err = doc.getElementsByClass("kontomeldung").get(0).text(); return new ProlongAllResult(MultiStepResult.Status.ERROR, err); } if (doc.select(".kontozeile table").size() == 1) { Map<Integer, String> colmap = new HashMap<>(); List<Map<String, String>> result = new ArrayList<>(); for (Element tr : doc.select(".kontozeile table tr")) { if (tr.select(".tabHeaderKonto").size() > 0) { int i = 0; for (Element th : tr.select("th")) { if (th.text().contains("Verfasser")) { colmap.put(i, OpacApi.ProlongAllResult.KEY_LINE_AUTHOR); } else if (th.text().contains("Titel")) { colmap.put(i, OpacApi.ProlongAllResult.KEY_LINE_TITLE); } else if (th.text().contains("Neue")) { colmap.put(i, OpacApi.ProlongAllResult.KEY_LINE_NEW_RETURNDATE); } else if (th.text().contains("Frist")) { colmap.put(i, OpacApi.ProlongAllResult.KEY_LINE_OLD_RETURNDATE); } else if (th.text().contains("Status")) { colmap.put(i, OpacApi.ProlongAllResult.KEY_LINE_MESSAGE); } i++; } } else { Map<String, String> line = new HashMap<>(); for (Entry<Integer, String> entry : colmap.entrySet()) { line.put(entry.getValue(), tr.child(entry.getKey()).text().trim()); } result.add(line); } } if (doc.select("input#make_allvl").size() > 0) { List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("target", "make_allvl_flag")); nameValuePairs.add(new BasicNameValuePair("make_allvl", "Bestaetigung")); httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); } return new ProlongAllResult(MultiStepResult.Status.OK, result); } return new ProlongAllResult(MultiStepResult.Status.ERROR, stringProvider.getString(StringProvider.INTERNAL_ERROR)); } @Override public CancelResult cancel(String media, Account account, int useraction, String selection) throws IOException, OpacErrorException { if (!initialised) { start(); } if (System.currentTimeMillis() - logged_in > SESSION_LIFETIME || logged_in_as == null) { try { account(account); } catch (JSONException e) { return new CancelResult(MultiStepResult.Status.ERROR, stringProvider.getString(StringProvider.COULD_NOT_LOAD_ACCOUNT)); } } else if (logged_in_as.getId() != account.getId()) { try { account(account); } catch (JSONException e) { return new CancelResult(MultiStepResult.Status.ERROR, stringProvider.getString(StringProvider.COULD_NOT_LOAD_ACCOUNT)); } } httpGet(opac_url + "/" + media, getDefaultEncoding()); List<NameValuePair> nameValuePairs = new ArrayList<>(2); nameValuePairs.add(new BasicNameValuePair("target", "delvorbest")); nameValuePairs.add(new BasicNameValuePair("vorbdelbest", "Besttigung")); httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), getDefaultEncoding()); return new CancelResult(MultiStepResult.Status.OK); } @Override public AccountData account(Account acc) throws IOException, JSONException, OpacErrorException { if (!initialised) { start(); } if (acc.getName() == null || acc.getName().equals("null")) { return null; } List<NameValuePair> nameValuePairs; String html = httpGet(opac_url + "/index.asp?kontofenster=start", "ISO-8859-1"); Document doc = Jsoup.parse(html); if (doc.select("input[name=AUSWEIS]").size() > 0) { // Login vonnten nameValuePairs = new ArrayList<>(); nameValuePairs.add(new BasicNameValuePair("AUSWEIS", acc.getName())); nameValuePairs.add(new BasicNameValuePair("PWD", acc.getPassword())); if (data.has("db")) { nameValuePairs.add(new BasicNameValuePair("vkontodb", data.getString("db"))); } nameValuePairs.add(new BasicNameValuePair("B1", "weiter")); nameValuePairs.add(new BasicNameValuePair("kontofenster", "true")); nameValuePairs.add(new BasicNameValuePair("target", "konto")); nameValuePairs.add(new BasicNameValuePair("type", "K")); html = httpPost(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), "ISO-8859-1", true); doc = Jsoup.parse(html); } if (doc.getElementsByClass("kontomeldung").size() == 1) { throw new OpacErrorException(doc.getElementsByClass("kontomeldung").get(0).text()); } logged_in_as = acc; logged_in = System.currentTimeMillis(); return parse_account(acc, doc, data); } public static AccountData parse_account(Account acc, Document doc, JSONObject data) throws JSONException { JSONObject copymap = data.getJSONObject("accounttable"); List<LentItem> media = new ArrayList<>(); if (doc.select(".kontozeile_center table").size() == 0) { return null; } Elements exemplartrs = doc.select(".kontozeile_center table").get(0).select("tr.tabKonto"); DateTimeFormatter fmt = DateTimeFormat.forPattern("dd.MM.yyyy").withLocale(Locale.GERMAN); for (int i = 0; i < exemplartrs.size(); i++) { Element tr = exemplartrs.get(i); LentItem item = new LentItem(); Iterator<?> keys = copymap.keys(); while (keys.hasNext()) { String key = (String) keys.next(); int index; try { index = copymap.has(key) ? copymap.getInt(key) : -1; } catch (JSONException e1) { index = -1; } if (index >= 0) { if (key.equals("prolongurl")) { if (tr.child(index).children().size() > 0) { item.setProlongData(tr.child(index).child(0).attr("href")); item.setRenewable(tr.child(index).child(0).attr("href").contains("vermsg")); } } else if (key.equals("returndate")) { try { item.setDeadline(fmt.parseLocalDate(tr.child(index).text())); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } } else { item.set(key, tr.child(index).text()); } } } media.add(item); } assert (doc.select(".kontozeile_center table").get(0).select("tr").size() > 0); assert (exemplartrs.size() == media.size()); copymap = data.getJSONObject("reservationtable"); List<ReservedItem> reservations = new ArrayList<>(); exemplartrs = doc.select(".kontozeile_center table").get(1).select("tr.tabKonto"); for (int i = 0; i < exemplartrs.size(); i++) { Element tr = exemplartrs.get(i); ReservedItem item = new ReservedItem(); Iterator<?> keys = copymap.keys(); while (keys.hasNext()) { String key = (String) keys.next(); int index; try { index = copymap.has(key) ? copymap.getInt(key) : -1; } catch (JSONException e1) { index = -1; } if (index >= 0) { if (key.equals("cancelurl")) { if (tr.child(index).children().size() > 0) { item.setCancelData(tr.child(index).child(0).attr("href")); } } else if (key.equals("availability")) { try { item.setReadyDate(fmt.parseLocalDate(tr.child(index).text())); } catch (IllegalArgumentException e1) { item.setStatus(tr.child(index).text()); } } else if (key.equals("expirationdate")) { try { item.setExpirationDate(fmt.parseLocalDate(tr.child(index).text())); } catch (IllegalArgumentException e1) { item.setStatus(tr.child(index).text()); } } else { item.set(key, tr.child(index).text()); } } } reservations.add(item); } assert (doc.select(".kontozeile_center table").get(1).select("tr").size() > 0); assert (exemplartrs.size() == reservations.size()); AccountData res = new AccountData(acc.getId()); for (Element row : doc.select(".kontozeile_center, div[align=center]")) { String text = row.text().trim(); if (text.matches(".*Ausstehende Geb.+hren:[^0-9]+([0-9.,]+)[^0-9A-Z]*(|EUR|CHF|Fr.).*")) { text = text.replaceAll( ".*Ausstehende Geb.+hren:[^0-9]+([0-9.," + "]+)[^0-9A-Z]*(|EUR|CHF|Fr.).*", "$1 $2"); res.setPendingFees(text); } if (text.matches("Ihr Ausweis ist g.ltig bis:.*")) { text = text.replaceAll("Ihr Ausweis ist g.ltig bis:[^A-Za-z0-9]+", ""); res.setValidUntil(text); } else if (text.matches("Ausweis g.ltig bis:.*")) { text = text.replaceAll("Ausweis g.ltig bis:[^A-Za-z0-9]+", ""); res.setValidUntil(text); } } res.setLent(media); res.setReservations(reservations); return res; } @Override public String getShareUrl(String id, String title) { try { return "http://opacapp.de/:" + library.getIdent() + ":" + id + ":" + URLEncoder.encode(title, "UTF-8"); } catch (UnsupportedEncodingException e) { return "http://opacapp.de/:" + library.getIdent() + ":" + id + ":" + title; } } @Override public int getSupportFlags() { int flags = SUPPORT_FLAG_CHANGE_ACCOUNT | SUPPORT_FLAG_WARN_RESERVATION_FEES; flags |= SUPPORT_FLAG_ENDLESS_SCROLLING; if (!data.has("disableProlongAll")) { flags |= SUPPORT_FLAG_ACCOUNT_PROLONG_ALL; } return flags; } @Override public SearchRequestResult filterResults(Filter filter, Option option) { // TODO Auto-generated method stub return null; } @Override public void checkAccountData(Account acc) throws IOException, JSONException, OpacErrorException { start(); List<NameValuePair> nameValuePairs = new ArrayList<>(); nameValuePairs.add(new BasicNameValuePair("AUSWEIS", acc.getName())); nameValuePairs.add(new BasicNameValuePair("PWD", acc.getPassword())); if (data.has("db")) { nameValuePairs.add(new BasicNameValuePair("vkontodb", data.getString("db"))); } nameValuePairs.add(new BasicNameValuePair("B1", "weiter")); nameValuePairs.add(new BasicNameValuePair("target", "konto")); nameValuePairs.add(new BasicNameValuePair("type", "K")); String html = httpPostWithRedirect(opac_url + "/index.asp", new UrlEncodedFormEntity(nameValuePairs), "ISO-8859-1"); Document doc = Jsoup.parse(html); if (doc.select(".kontomeldung").size() > 0) { throw new OpacErrorException(doc.select(".kontomeldung").text()); } } private String httpPostWithRedirect(String url, UrlEncodedFormEntity data, String encoding) throws IOException { HttpPost httppost = new HttpPost(url); httppost.setEntity(data); HttpResponse response = http_client.execute(httppost); if (response.getStatusLine().getStatusCode() == 302) { HttpGet httpget = new HttpGet( url.substring(0, url.lastIndexOf("/") + 1) + response.getFirstHeader("Location").getValue()); HttpUtils.consume(response.getEntity()); response = http_client.execute(httpget); } return convertStreamToString(response.getEntity().getContent(), encoding); } @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; } }