/** * Copyright (C) 2013 by Johan von Forstner 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.CookieStore; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.message.BasicNameValuePair; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.json.JSONArray; 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.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; 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.i18n.StringProvider; import de.geeksfactory.opacclient.networking.HttpClientFactory; 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.Library; import de.geeksfactory.opacclient.objects.SearchRequestResult; import de.geeksfactory.opacclient.objects.SearchResult; import de.geeksfactory.opacclient.objects.SearchResult.MediaType; import de.geeksfactory.opacclient.searchfields.BarcodeSearchField; import de.geeksfactory.opacclient.searchfields.CheckboxSearchField; import de.geeksfactory.opacclient.searchfields.DropdownSearchField; import de.geeksfactory.opacclient.searchfields.SearchField; import de.geeksfactory.opacclient.searchfields.SearchQuery; import de.geeksfactory.opacclient.searchfields.TextSearchField; import de.geeksfactory.opacclient.utils.ISBNTools; /** * @author Johan von Forstner, 16.09.2013 */ public abstract class Pica extends BaseApi implements OpacApi { protected static HashMap<String, MediaType> defaulttypes = new HashMap<>(); protected static HashMap<String, String> languageCodes = new HashMap<>(); static { defaulttypes.put("book", MediaType.BOOK); defaulttypes.put("article", MediaType.BOOK); defaulttypes.put("binary", MediaType.EBOOK); defaulttypes.put("periodical", MediaType.MAGAZINE); defaulttypes.put("onlineper", MediaType.EBOOK); defaulttypes.put("letter", MediaType.UNKNOWN); defaulttypes.put("handwriting", MediaType.UNKNOWN); defaulttypes.put("map", MediaType.MAP); defaulttypes.put("picture", MediaType.ART); defaulttypes.put("audiovisual", MediaType.MOVIE); defaulttypes.put("score", MediaType.SCORE_MUSIC); defaulttypes.put("sound", MediaType.CD_MUSIC); defaulttypes.put("software", MediaType.CD_SOFTWARE); defaulttypes.put("microfilm", MediaType.UNKNOWN); defaulttypes.put("empty", MediaType.UNKNOWN); languageCodes.put("de", "DU"); languageCodes.put("en", "EN"); languageCodes.put("nl", "NE"); languageCodes.put("fr", "FR"); } protected String opac_url = ""; protected String https_url = ""; protected JSONObject data; protected Library library; protected int resultcount = 10; protected String reusehtml; protected Integer searchSet; protected String db; protected String pwEncoded; protected String languageCode; protected CookieStore cookieStore = new BasicCookieStore(); protected String lor_reservations; @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"); this.db = data.getString("db"); if (library.isAccountSupported()) { if (data.has("httpsbaseurl")) { this.https_url = data.getString("httpsbaseurl"); } else { this.https_url = this.opac_url; } } } catch (JSONException e) { throw new RuntimeException(e); } } protected int addParameters(SearchQuery query, List<NameValuePair> params, int index) throws JSONException { if (query.getValue().equals("") || query.getValue().equals("false")) { return index; } if (query.getSearchField() instanceof TextSearchField || query.getSearchField() instanceof BarcodeSearchField) { if (query.getSearchField().getData().getBoolean("ADI")) { params.add(new BasicNameValuePair(query.getKey(), query .getValue())); } else { if (index == 0) { params.add(new BasicNameValuePair("ACT" + index, "SRCH")); } else { params.add(new BasicNameValuePair("ACT" + index, "*")); } params.add(new BasicNameValuePair("IKT" + index, query.getKey())); params.add(new BasicNameValuePair("TRM" + index, query .getValue())); return index + 1; } } else if (query.getSearchField() instanceof CheckboxSearchField) { boolean checked = Boolean.valueOf(query.getValue()); if (checked) { params.add(new BasicNameValuePair(query.getKey(), "Y")); } } else if (query.getSearchField() instanceof DropdownSearchField) { params.add(new BasicNameValuePair(query.getKey(), query.getValue())); } return index; } @Override public SearchRequestResult search(List<SearchQuery> query) throws IOException, OpacErrorException, JSONException { if (!initialised) { start(); } List<NameValuePair> params = new ArrayList<>(); int index = 0; start(); params.add(new BasicNameValuePair("ACT", "SRCHM")); params.add(new BasicNameValuePair("MATCFILTER", "Y")); params.add(new BasicNameValuePair("MATCSET", "Y")); params.add(new BasicNameValuePair("NOSCAN", "Y")); params.add(new BasicNameValuePair("PARSE_MNEMONICS", "N")); params.add(new BasicNameValuePair("PARSE_OPWORDS", "N")); params.add(new BasicNameValuePair("PARSE_OLDSETS", "N")); for (SearchQuery singleQuery : query) { index = addParameters(singleQuery, params, 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)); } String html = httpGet( opac_url + "/LNG=" + getLang() + "/DB=" + db + "/SET=1/TTL=1/CMD?" + URLEncodedUtils.format(params, getDefaultEncoding()), getDefaultEncoding(), false, cookieStore); return parse_search(html, 1); } public SearchRequestResult volumeSearch(Map<String, String> query) throws IOException, OpacErrorException { String html = httpGet( opac_url + "/LNG=" + getLang() + "/DB=" + db + "/SET=1/TTL=1" + "/FAM?PPN=" + query.get("ppn"), getDefaultEncoding(), false, cookieStore); return parse_search(html, 1); } protected SearchRequestResult parse_search(String html, int page) throws OpacErrorException { Document doc = Jsoup.parse(html); updateSearchSetValue(doc); if (doc.select(".error").size() > 0) { String error = doc.select(".error").first().text().trim(); if (error.equals("Es wurde nichts gefunden.") || error.equals("Nothing has been found") || error.equals("Er is niets gevonden.") || error.equals("Rien n'a été trouvé.")) { // nothing found return new SearchRequestResult(new ArrayList<SearchResult>(), 0, 1, 1); } else { // error throw new OpacErrorException(error); } } reusehtml = html; int results_total; String resultnumstr = doc.select(".pages").first().text(); Pattern p = Pattern.compile("[0-9]+$"); Matcher m = p.matcher(resultnumstr); if (m.find()) { resultnumstr = m.group(); } 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")); } else { results_total = Integer.parseInt(resultnumstr); } List<SearchResult> results = new ArrayList<>(); if (results_total == 1) { // Only one result DetailedItem singleResult = parse_result(html); SearchResult sr = new SearchResult(); sr.setType(getMediaTypeInSingleResult(html)); sr.setInnerhtml("<b>" + singleResult.getTitle() + "</b><br>" + singleResult.getDetails().get(0).getContent()); results.add(sr); } Elements table = doc .select("table[summary=hitlist] tbody tr[valign=top]"); // identifier = null; Elements links = doc.select("table[summary=hitlist] 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("SHW?") && !haslink) { haslink = true; try { List<NameValuePair> anyurl = URLEncodedUtils.parse(new URI( node.attr("href")), getDefaultEncoding()); for (NameValuePair nv : anyurl) { if (nv.getName().equals("identifier")) { // identifier = nv.getValue(); break; } } } catch (Exception e) { e.printStackTrace(); } } } for (int i = 0; i < table.size(); i++) { Element tr = table.get(i); SearchResult sr = new SearchResult(); if (tr.select("td.hit img").size() > 0) { String[] fparts = tr.select("td 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", ""))); } } Element middlething = tr.child(2); List<Node> children = middlething.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 = new StringBuilder(); int k = 0; for (String[] part : strings) { if (part[0].equals("a") && k == 0) { description.append("<b>").append(part[2]).append("</b>"); } else if (k < 3) { description.append("<br />").append(part[2]); } k++; } 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 SearchRequestResult searchGetPage(int page) throws IOException, OpacErrorException { if (!initialised) { start(); } String html = httpGet(opac_url + "/LNG=" + getLang() + "/DB=" + db + "/SET=" + searchSet + "/TTL=1/NXT?FRST=" + (((page - 1) * resultcount) + 1), getDefaultEncoding(), false, cookieStore); return parse_search(html, page); } @Override public SearchRequestResult filterResults(Filter filter, Option option) throws IOException { return null; } @Override public DetailedItem getResultById(String id, String homebranch) throws IOException { if (id == null && reusehtml != null) { return parse_result(reusehtml); } if (!initialised) { start(); } if (id.startsWith("http")) { return parse_result(httpGet(id, getDefaultEncoding())); } else { try { return parse_result(httpGet(opac_url + "/LNG=" + getLang() + "/DB=" + data.getString("db") + "/PPNSET?PPN=" + id, getDefaultEncoding())); } catch (JSONException e) { e.printStackTrace(); } } return null; } @Override public DetailedItem getResult(int position) throws IOException { if (!initialised) { start(); } String html = httpGet(opac_url + "/LNG=" + getLang() + "/DB=" + db + "/LNG=" + getLang() + "/SET=" + searchSet + "/TTL=1/SHW?FRST=" + (position + 1), getDefaultEncoding(), false, cookieStore); return parse_result(html); } protected DetailedItem parse_result(String html) { Document doc = Jsoup.parse(html); doc.setBaseUri(opac_url); DetailedItem result = new DetailedItem(); for (Element a : doc.select("a[href*=PPN")) { Map<String, String> hrefq = getQueryParamsFirst(a .absUrl("href")); String ppn = hrefq.get("PPN"); result.setId(ppn); break; } // GET COVER if (doc.select("td.preslabel:contains(ISBN) + td.presvalue").size() > 0) { Element isbnElement = doc.select( "td.preslabel:contains(ISBN) + td.presvalue").first(); String isbn = ""; for (Node child : isbnElement.childNodes()) { if (child instanceof TextNode) { isbn = ((TextNode) child).text().trim(); break; } } result.setCover(ISBNTools.getAmazonCoverURL(isbn, true)); } // GET TITLE AND SUBTITLE String titleAndSubtitle; Element titleAndSubtitleElem = null; String titleRegex = ".*(Titel|Aufsatz|Zeitschrift|Gesamttitel" + "|Title|Article|Periodical|Collective\\stitle" + "|Titre|Article|P.riodique|Titre\\sg.n.ral).*"; String selector = "td.preslabel:matches(" + titleRegex + ") + td.presvalue"; if (doc.select(selector).size() > 0) { titleAndSubtitleElem = doc.select(selector).first(); titleAndSubtitle = titleAndSubtitleElem.text().trim(); int slashPosition = Math.min(titleAndSubtitle.indexOf("/"), titleAndSubtitle.indexOf(":")); String title; if (slashPosition > 0) { title = titleAndSubtitle.substring(0, slashPosition).trim(); String subtitle = titleAndSubtitle.substring(slashPosition + 1).trim(); result.addDetail(new Detail(stringProvider .getString(StringProvider.SUBTITLE), subtitle)); } else { title = titleAndSubtitle; } result.setTitle(title); } else { result.setTitle(""); } // Details int line = 0; Elements lines = doc.select("td.preslabel + td.presvalue"); if (titleAndSubtitleElem != null) { lines.remove(titleAndSubtitleElem); } for (Element element : lines) { Element titleElem = element.firstElementSibling(); String detail = ""; if (element.select("div").size() > 1 && element.select("div").text().equals(element.text())) { boolean first = true; for (Element div : element.select("div")) { if (!div.text().replace("\u00a0", " ").trim().equals("")) { if (!first) { detail += "\n" + div.text().replace("\u00a0", " ").trim(); } else { detail += div.text().replace("\u00a0", " ").trim(); first = false; } } } } else { detail = element.text().replace("\u00a0", " ").trim(); } String title = titleElem.text().replace("\u00a0", " ").trim(); if (element.select("hr").size() > 0 || element.text().trim().equals("")) // after the separator we get the copies { break; } if (detail.length() == 0 && title.length() == 0) { line++; continue; } if (title.contains(":")) { title = title.substring(0, title.indexOf(":")); // remove colon } result.addDetail(new Detail(title, detail)); if (element.select("a").size() == 1 && !element.select("a").get(0).text().trim().equals("")) { String url = element.select("a").first().absUrl("href"); if (!url.startsWith(opac_url)) { result.addDetail( new Detail(stringProvider.getString(StringProvider.LINK), url)); } } line++; } line++; // next line after separator // Copies Copy copy = new Copy(); String location = ""; // reservation info will be stored as JSON JSONArray reservationInfo = new JSONArray(); while (line < lines.size()) { Element element = lines.get(line); if (element.select("hr").size() == 0 && !element.text().trim().equals("")) { Element titleElem = element.firstElementSibling(); String detail = element.text().trim(); String title = titleElem.text().replace("\u00a0", " ").trim(); if (detail.length() == 0 && title.length() == 0) { line++; continue; } if (title.contains("Standort") || title.contains("Vorhanden in") || title.contains("Location")) { location += detail; } else if (title.contains("Sonderstandort")) { location += " - " + detail; } else if (title.contains("Systemstelle") || title.contains("Subject")) { copy.setDepartment(detail); } else if (title.contains("Fachnummer") || title.contains("locationnumber")) { copy.setLocation(detail); } else if (title.contains("Signatur") || title.contains("Shelf mark")) { copy.setShelfmark(detail); } else if (title.contains("Anmerkung")) { location += " (" + detail + ")"; } else if (title.contains("Link")) { result.addDetail(new Detail(title.replace(":", "").trim(), detail)); } else if (title.contains("Status") || title.contains("Ausleihinfo") || title.contains("Ausleihstatus") || title.contains("Request info")) { // Find return date Pattern pattern = Pattern .compile("(till|bis) (\\d{2}-\\d{2}-\\d{4})"); Matcher matcher = pattern.matcher(detail); if (matcher.find()) { DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy").withLocale(Locale.GERMAN); try { copy.setStatus(detail.substring(0, matcher.start() - 1).trim()); copy.setReturnDate(fmt.parseLocalDate(matcher.group(2))); } catch (IllegalArgumentException e) { e.printStackTrace(); copy.setStatus(detail); } } else { copy.setStatus(detail); } // Get reservation info if (element.select("a:has(img[src*=inline_arrow])").size() > 0) { Element a = element.select( "a:has(img[src*=inline_arrow])").first(); boolean multipleCopies = a.text().matches( ".*(Exemplare|Volume list).*"); JSONObject reservation = new JSONObject(); try { reservation.put("multi", multipleCopies); reservation.put("link", _extract_url(a.absUrl("href"))); reservation.put("desc", location); reservationInfo.put(reservation); } catch (JSONException e1) { e1.printStackTrace(); } result.setReservable(true); } } } else { copy.setBranch(location); result.addCopy(copy); location = ""; copy = new Copy(); } line++; } if (copy.notEmpty()) { copy.setBranch(location); result.addCopy(copy); } if (reservationInfo.length() == 0) { // No reservation info found yet, because we didn't find any copies. // If there is a reservation link somewhere in the rows we interpreted // as details, we still want to use it. if (doc.select("td a:has(img[src*=inline_arrow])").size() > 0) { Element a = doc.select( "td a:has(img[src*=inline_arrow])").first(); boolean multipleCopies = a.text().matches( ".*(Exemplare|Volume list).*"); JSONObject reservation = new JSONObject(); try { reservation.put("multi", multipleCopies); reservation.put("link", _extract_url(a.attr("href"))); reservation.put("desc", location); reservationInfo.put(reservation); } catch (JSONException e1) { e1.printStackTrace(); } result.setReservable(true); } } result.setReservation_info(reservationInfo.toString()); // Volumes if (doc.select("a[href^=FAM?PPN=]").size() > 0) { String href = doc.select("a[href^=FAM?PPN=]").attr("href"); String ppn = getQueryParamsFirst(href).get("PPN"); Map<String, String> data = new HashMap<>(); data.put("ppn", ppn); result.setVolumesearch(data); } return result; } private String _extract_url(String javascriptUrl) { if (javascriptUrl.startsWith("javascript:")) { javascriptUrl = javascriptUrl.replaceAll("^javascript:PU\\('(.*)',(.*)\\)(.*)", "$1"); } try { return new URL(new URL(opac_url), javascriptUrl).toString(); } catch (MalformedURLException e) { e.printStackTrace(); return null; } } @Override public List<SearchField> parseSearchFields() throws IOException, JSONException { if (!initialised) { start(); } String html = httpGet(opac_url + "/LNG=" + getLang() + "/DB=" + db + "/ADVANCED_SEARCHFILTER", getDefaultEncoding()); Document doc = Jsoup.parse(html); List<SearchField> fields = new ArrayList<>(); Elements options = doc.select("select[name=IKT0] option"); for (Element option : options) { TextSearchField field = new TextSearchField(); field.setDisplayName(option.text()); field.setId(option.attr("value")); field.setHint(""); field.setData(new JSONObject("{\"ADI\": false}")); Pattern pattern = Pattern .compile("\\[X?[A-Za-z]{2,3}:?\\]|\\(X?[A-Za-z]{2,3}:?\\)"); Matcher matcher = pattern.matcher(field.getDisplayName()); if (matcher.find()) { field.getData().put("meaning", matcher.group().replace(":", "").toUpperCase()); field.setDisplayName(matcher.replaceFirst("").trim()); } fields.add(field); } Elements sort = doc.select("select[name=SRT]"); if (sort.size() > 0) { DropdownSearchField field = new DropdownSearchField(); field.setDisplayName(sort.first().parent().parent() .select(".longval").first().text()); field.setId("SRT"); for (Element option : sort.select("option")) { field.addDropdownValue(option.attr("value"), option.text()); } fields.add(field); } for (Element input : doc.select("input[type=text][name^=ADI]")) { TextSearchField field = new TextSearchField(); field.setDisplayName(input.parent().parent().select(".longkey") .text()); field.setId(input.attr("name")); field.setHint(input.parent().select("span").text()); field.setData(new JSONObject("{\"ADI\": true}")); fields.add(field); } for (Element dropdown : doc.select("select[name^=ADI]")) { DropdownSearchField field = new DropdownSearchField(); field.setDisplayName(dropdown.parent().parent().select(".longkey") .text()); field.setId(dropdown.attr("name")); for (Element option : dropdown.select("option")) { field.addDropdownValue(option.attr("value"), option.text()); } fields.add(field); } Elements fuzzy = doc.select("input[name=FUZZY]"); if (fuzzy.size() > 0) { CheckboxSearchField field = new CheckboxSearchField(); field.setDisplayName(fuzzy.first().parent().parent() .select(".longkey").first().text()); field.setId("FUZZY"); fields.add(field); } Elements mediatypes = doc.select("input[name=ADI_MAT]"); if (mediatypes.size() > 0) { DropdownSearchField field = new DropdownSearchField(); field.setDisplayName("Materialart"); field.setId("ADI_MAT"); field.addDropdownValue("", "Alle"); for (Element mt : mediatypes) { field.addDropdownValue(mt.attr("value"), mt.parent().nextElementSibling().text().replace("\u00a0", "")); } fields.add(field); } return fields; } @Override public String getShareUrl(String id, String title) { if (id.startsWith("http")) { return id; } else { return opac_url + "/LNG=" + getLang() + "/DB=" + db + "/PPNSET?PPN=" + id; } } @Override public int getSupportFlags() { return SUPPORT_FLAG_ENDLESS_SCROLLING | SUPPORT_FLAG_CHANGE_ACCOUNT; } public void updateSearchSetValue(Document doc) { String url = doc.select("base").first().attr("href"); Integer setPosition = url.indexOf("SET=") + 4; String searchSetString = url.substring(setPosition, url.indexOf("/", setPosition)); searchSet = Integer.parseInt(searchSetString); } public MediaType getMediaTypeInSingleResult(String html) { Document doc = Jsoup.parse(html); MediaType mediatype = MediaType.UNKNOWN; if (doc.select("table[summary=presentation switch] img").size() > 0) { String[] fparts = doc .select("table[summary=presentation switch] img").get(0) .attr("src").split("/"); String fname = fparts[fparts.length - 1]; if (data.has("mediatypes")) { try { mediatype = MediaType.valueOf(data.getJSONObject( "mediatypes").getString(fname)); } catch (JSONException e) { mediatype = defaulttypes.get(fname .toLowerCase(Locale.GERMAN).replace(".jpg", "") .replace(".gif", "").replace(".png", "")); } catch (IllegalArgumentException e) { mediatype = defaulttypes.get(fname .toLowerCase(Locale.GERMAN).replace(".jpg", "") .replace(".gif", "").replace(".png", "")); } } else { mediatype = defaulttypes.get(fname.toLowerCase(Locale.GERMAN) .replace(".jpg", "").replace(".gif", "") .replace(".png", "")); } } return mediatype; } @Override protected String getDefaultEncoding() { try { if (data.has("charset")) { return data.getString("charset"); } } catch (JSONException e) { e.printStackTrace(); } return "UTF-8"; } @Override public void setLanguage(String language) { this.languageCode = language; } protected String getLang() { if (!initialised) { return null; } if (supportedLanguages.contains(languageCode)) { return languageCodes.get(languageCode); } else if (supportedLanguages.contains("en")) // Fall back to English if language not available { return languageCodes.get("en"); } else if (supportedLanguages.contains("de")) // Fall back to German if English not available { return languageCodes.get("de"); } else { return null; } } @Override public Set<String> getSupportedLanguages() throws IOException { Set<String> langs = new HashSet<>(); for (String lang : languageCodes.keySet()) { String html = httpGet(opac_url + "/DB=" + db + "/LNG=" + languageCodes.get(lang) + "/START_WELCOME", getDefaultEncoding()); if (!html.contains("MODE_START") && !html.contains("LABEL_")) { langs.add(lang); } } return langs; } }