Example usage for org.jsoup.nodes Element childNodes

List of usage examples for org.jsoup.nodes Element childNodes

Introduction

In this page you can find the example usage for org.jsoup.nodes Element childNodes.

Prototype

List childNodes

To view the source code for org.jsoup.nodes Element childNodes.

Click Source Link

Usage

From source file:mml.handler.post.MMLPostHTMLHandler.java

/**
 * Parse a paragraph. These may be "p" or "hN" elements, often with classes
 * @param p the paragraph/heading element from the document fragment
 * @param defaultName the default name for the property
 *//*from  w  ww  .j a va  2  s .  com*/
private void parsePara(Element p, String defaultName) throws JSONException {
    List<Node> children = p.childNodes();
    String name = p.attr("class");
    if (name == null || name.length() == 0)
        name = defaultName;
    if (isLineFormat(name) || prevWasMilestone)
        ensure(1, false);
    else
        ensure(2, true);
    int offset = sb.length();
    Range r = new Range(name, offset, 0);
    stil.add(r);
    for (Node child : children) {
        if (child instanceof Element) {
            String nName = child.nodeName().toLowerCase();
            if (nName.equals("span"))
                parseSpan((Element) child);
            else
                parseOtherElement((Element) child);
        } else if (child instanceof TextNode) {
            TextNode tn = (TextNode) child;
            sb.append(tn.getWholeText());
        }
    }
    if (isLineFormat(name))
        ensure(1, true);
    else
        ensure(2, true);
    this.stil.updateLen(r, sb.length() - offset);
    prevWasMilestone = false;
}

From source file:de.tudarmstadt.ukp.experiments.dip.wp1.documents.helpers.boilerplateremoval.impl.JusTextBoilerplateRemoval.java

/**
 * remove unwanted parts from a jsoup doc
 *
 * @param jsoupDoc//w  w w .  j  a  va 2 s .c  o m
 * @return
 */
public Document cleanDom(Document jsoupDoc) {
    String[] tagsToRemove = { "head", "script", ".hidden", "embedded" };

    for (String tag : tagsToRemove) {
        Elements selectedTags = jsoupDoc.select(tag);
        for (Element element : selectedTags) {
            element.remove();
        }
    }
    //remove comments (might be slow)
    for (Element element : jsoupDoc.getAllElements()) {
        for (Node n : element.childNodes()) {
            NodeHelper.removeComments(n);
        }
    }
    return jsoupDoc;

}

From source file:com.laudandjolynn.mytv.crawler.tvmao.TvMaoCrawler.java

/**
 * ?/*  w ww .  jav a  2  s . c o  m*/
 * 
 * @param html
 * @return
 */
private List<ProgramTable> parseProgramTable(String html) {
    Document doc = Jsoup.parse(html);
    Elements dateElements = doc.select("div.pgmain div[class=\"mt10 clear\"] b:first-child");
    String dateAndWeek = dateElements.get(0).text().trim();
    String[] dateAndWeekArray = dateAndWeek.split("\\s+");
    String date = Calendar.getInstance().get(Calendar.YEAR) + "-" + dateAndWeekArray[0];
    String weekString = dateAndWeekArray[1];
    int week = weekStringToInt(weekString);
    Elements stationElements = doc
            .select("aside[class=\"related-aside rt\"] section[class=\"aside-section clear\"] div.bar");
    String stationName = stationElements.get(0).text().trim();
    Elements programElements = doc.select("ul#pgrow li");

    List<ProgramTable> resultList = new ArrayList<ProgramTable>();
    for (Element element : programElements) {
        List<Node> children = element.childNodes();
        int size = children.size();
        if (size < 2) {
            continue;
        }

        int i = 0;
        // 
        boolean foundAirTime = false;
        for (; i < size; i++) {
            Node child = children.get(i);
            if (child instanceof Element && "SPAN".equalsIgnoreCase(((Element) child).tagName())) {
                foundAirTime = true;
                break;
            }
        }
        if (!foundAirTime) {
            logger.info("the program table of " + stationName + " at " + date + " does not exists.");
            return resultList;
        }
        String airTime = ((Element) children.get(i++)).text().trim();
        StringBuffer program = new StringBuffer();
        // ??
        for (; i < size; i++) {
            Node child = children.get(i);
            if (child instanceof TextNode) {
                program.append(((TextNode) child).text().trim());
            } else if (child instanceof Element && "A".equalsIgnoreCase(((Element) child).tagName())) {
                program.append(((Element) child).text().trim());
                i++;
                break;
            }
        }

        if (i < size - 1) {
            // textnode
            Node child = children.get(i);
            if (child instanceof TextNode) {
                program.append(((TextNode) child).text().trim());
            }
        }
        ProgramTable pt = new ProgramTable();
        pt.setAirDate(date);
        pt.setAirTime(date + " " + airTime);
        pt.setProgram(program.toString().trim());
        pt.setStationName(stationName);
        pt.setWeek(week);
        for (CrawlEventListener listener : listeners) {
            listener.itemFound(new ProgramTableFoundEvent(this, pt));
        }
        resultList.add(pt);
    }
    return resultList;
}

From source file:me.vertretungsplan.parser.UntisCommonParser.java

static String findLastChange(Element doc, SubstitutionScheduleData scheduleData) {
    String lastChange = null;//from  ww w .  j a  v a2 s  . com

    boolean lastChangeLeft = false;
    if (scheduleData != null) {
        if (scheduleData.getData().has("stand_links")) {
            // backwards compatibility
            lastChangeLeft = scheduleData.getData().optBoolean("stand_links", false);
        } else {
            lastChangeLeft = scheduleData.getData().optBoolean(PARAM_LAST_CHANGE_LEFT, false);
        }
    }

    if (doc.select("table.mon_head").size() > 0) {
        Element monHead = doc.select("table.mon_head").first();
        lastChange = findLastChangeFromMonHeadTable(monHead);
    } else if (lastChangeLeft) {
        final String bodyHtml = doc.select("body").size() > 0 ? doc.select("body").html() : doc.html();
        lastChange = bodyHtml.substring(0, bodyHtml.indexOf("<p>") - 1);
    } else {
        List<Node> childNodes;
        if (doc instanceof Document) {
            childNodes = ((Document) doc).body().childNodes();
        } else {
            childNodes = doc.childNodes();
        }
        for (Node node : childNodes) {
            if (node instanceof Comment) {
                Comment comment = (Comment) node;
                if (comment.getData().contains("<table class=\"mon_head\">")) {
                    Document commentedDoc = Jsoup.parse(comment.getData());
                    Element monHead = commentedDoc.select("table.mon_head").first();
                    lastChange = findLastChangeFromMonHeadTable(monHead);
                    break;
                }
            }
        }
    }
    return lastChange;
}

From source file:cn.edu.hfut.dmic.contentextractor.ContentExtractor.java

/**
 * @param node /*from w  w w. ja  v  a2  s.  c  o  m*/
 *             1. styleclass
 *             2. ????density???
 *             3. p???
 * @return
 */
protected CountInfo computeInfo(Node node) {
    if (node instanceof Element) {
        node.removeAttr("style").removeAttr("class");
        Element tag = (Element) node;

        if (tag.text().matches(".{1,20}>.{1,10}>.{1,20}")) {
            CountInfo countInfo = new CountInfo();
            countInfo.density = -200;
            return countInfo;
        }
        CountInfo countInfo = new CountInfo();
        for (Node childNode : tag.childNodes()) {
            CountInfo childCountInfo = computeInfo(childNode);
            countInfo.textCount += childCountInfo.textCount;
            countInfo.linkTextCount += childCountInfo.linkTextCount;
            countInfo.tagCount += childCountInfo.tagCount;
            countInfo.linkTagCount += childCountInfo.linkTagCount;
            countInfo.leafList.addAll(childCountInfo.leafList);
            countInfo.densitySum += childCountInfo.density;
            countInfo.pCount += childCountInfo.pCount;
        }

        countInfo.tagCount++;
        String tagName = tag.tagName();
        if (tagName.equals("a") || tagName.equals("img")) {
            countInfo.linkTextCount = countInfo.textCount;
            countInfo.linkTagCount++;
        } else if (tagName.equals("p")) {
            countInfo.pCount++;
        }

        int pureLen = countInfo.textCount - countInfo.linkTextCount;
        int len = countInfo.tagCount - countInfo.linkTagCount;
        if (pureLen == 0 || len == 0) {
            countInfo.density = 0;
        } else {
            countInfo.density = (pureLen + 0.0) / len;
        }

        infoMap.put(tag, countInfo);

        return countInfo;
    } else if (node instanceof TextNode) {
        TextNode tn = (TextNode) node;
        CountInfo countInfo = new CountInfo();
        String text = tn.text();
        int len = text.length();
        countInfo.textCount = len;
        countInfo.leafList.add(len);
        return countInfo;
    } else {
        return new CountInfo();
    }
}

From source file:mml.handler.post.MMLPostHTMLHandler.java

/**
 * Parse a codeblock/*from w w  w  . ja va2 s . c om*/
 * @param elem the element to parse
 * @throws a JSON exception
 */
private void parsePre(Element elem) throws JSONException {
    if (elem.hasText()) {
        int offset = sb.length();
        String name = elem.attr("class");
        if (name == null || name.length() == 0)
            name = "pre";
        Range r = new Range(name, offset, 0);
        stil.add(r);
        if (elem.hasAttr("class")) {
            List<Node> children = elem.childNodes();
            for (Node child : children) {
                if (child instanceof Element) {
                    if (child.nodeName().equals("span"))
                        parseSpan((Element) child);
                    else
                        parseOtherElement((Element) child);
                } else if (child instanceof TextNode)
                    sb.append(((TextNode) child).getWholeText());
            }
        } else
            sb.append(elem.text());
        this.stil.updateLen(r, sb.length() - offset);
    }
    prevWasMilestone = false;
    ensure(1, false);
}

From source file:de.geeksfactory.opacclient.apis.Pica.java

protected SearchRequestResult parse_search(String html, int page) throws OpacErrorException {
    Document doc = Jsoup.parse(html);

    updateSearchSetValue(doc);/*  w w  w.j ava  2s  .c om*/

    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
        DetailledItem 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);
}

From source file:de.geeksfactory.opacclient.apis.Zones22.java

private DetailledItem parse_result(String id, String html) throws IOException {
    Document doc = Jsoup.parse(html);

    DetailledItem result = new DetailledItem();
    result.setTitle("");
    boolean title_is_set = false;

    result.setId(id);/*www .  j ava2s  .com*/

    Elements detaildiv = doc.select("div.record-item-new");

    Elements detailtrs1 = doc.select(".DetailDataCell table table:not(.inRecordHeader) tr");
    for (int i = 0; i < detailtrs1.size(); i++) {
        Element tr = detailtrs1.get(i);
        int s = tr.children().size();
        if (tr.child(0).text().trim().equals("Titel") && !title_is_set) {
            result.setTitle(tr.child(s - 1).text().trim());
            title_is_set = true;
        } else if (s > 1) {
            Element valchild = tr.child(s - 1);
            if (valchild.select("table").isEmpty()) {
                String val = valchild.text().trim();
                if (val.length() > 0)
                    result.addDetail(new Detail(tr.child(0).text().trim(), val));
            }
        }
    }

    for (Element a : doc.select("a.SummaryActionLink")) {
        if (a.text().contains("Vormerken")) {
            result.setReservable(true);
            result.setReservation_info(a.attr("href"));
        }
    }

    if (!detaildiv.isEmpty()) {
        for (int i = 0; i < detaildiv.size(); i++) {
            Element dd = detaildiv.get(i);
            String text = "";
            for (Node node : dd.childNodes()) {
                if (node instanceof TextNode) {
                    String snip = ((TextNode) node).text();
                    if (snip.length() > 0)
                        text += snip;
                } else if (node instanceof Element) {
                    if (((Element) node).tagName().equals("br"))
                        text += "\n";
                    else {
                        String snip = ((Element) node).text().trim();
                        if (snip.length() > 0)
                            text += snip;
                    }
                }
            }
            result.addDetail(new Detail("", text));
        }
    }

    if (doc.select("span.z3988").size() > 0) {
        // Sometimes there is a <span class="Z3988"> item which provides
        // data in a standardized format.
        String z3988data = doc.select("span.z3988").first().attr("title").trim();
        for (String pair : z3988data.split("\\&")) {
            String[] nv = pair.split("=", 2);
            if (nv.length == 2) {
                if (!nv[1].trim().equals("")) {
                    if (nv[0].equals("rft.btitle") && result.getTitle().length() == 0) {
                        result.setTitle(nv[1]);
                    } else if (nv[0].equals("rft.atitle") && result.getTitle().length() == 0) {
                        result.setTitle(nv[1]);
                    } else if (nv[0].equals("rft.au")) {
                        result.addDetail(new Detail("Author", nv[1]));
                    }
                }
            }
        }
    }

    Elements copydivs = doc.select(".DetailDataCell div[id^=stock_]");
    String pop = "";
    for (int i = 0; i < copydivs.size(); i++) {
        Element div = copydivs.get(i);

        if (div.attr("id").startsWith("stock_head")) {
            pop = div.text().trim();
            continue;
        }

        Map<String, String> copy = new HashMap<String, String>();

        // This is getting very ugly - check if it is valid for libraries
        // which are not
        // Hamburg.
        int j = 0;
        for (Node node : div.childNodes()) {
            try {
                if (node instanceof Element) {
                    if (((Element) node).tag().getName().equals("br")) {
                        copy.put(DetailledItem.KEY_COPY_BRANCH, pop);
                        result.addCopy(copy);
                        j = -1;
                    } else if (((Element) node).tag().getName().equals("b") && j == 1) {
                        copy.put(DetailledItem.KEY_COPY_LOCATION, ((Element) node).text());
                    } else if (((Element) node).tag().getName().equals("b") && j > 1) {
                        copy.put(DetailledItem.KEY_COPY_STATUS, ((Element) node).text());
                    }
                    j++;
                } else if (node instanceof TextNode) {
                    if (j == 0)
                        copy.put(DetailledItem.KEY_COPY_DEPARTMENT, ((TextNode) node).text());
                    if (j == 2)
                        copy.put(DetailledItem.KEY_COPY_BARCODE,
                                ((TextNode) node).getWholeText().trim().split("\n")[0].trim());
                    if (j == 6) {
                        String text = ((TextNode) node).text().trim();
                        copy.put(DetailledItem.KEY_COPY_RETURN, text.substring(text.length() - 10));
                    }
                    j++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    return result;
}

From source file:mml.handler.post.MMLPostHTMLHandler.java

/**
 * Parse the body of the HTML fragment//from   ww  w.j  a  va 2s.co  m
 * @param body should be contents of the target div in the editor
 * @throws JSONException 
 */
protected void parseBody(Element body) throws MMLSaveException {
    try {
        this.speller = new AeseSpeller(this.langCode);
        this.sb = new StringBuilder();
        String style = ScratchVersionSet.getDefaultStyleName(this.docid);
        stil = new STILDocument(style);
        pages = new STILDocument(style);
        if (body.nodeName().toLowerCase().equals("div"))
            parseDiv(body);
        else {
            List<Node> children = body.childNodes();
            for (Node child : children) {
                if (child instanceof Element) {
                    String nName = child.nodeName().toLowerCase();
                    if (nName.equals("div"))
                        parseDiv((Element) child);
                    else if (nName.equals("p"))
                        parsePara((Element) child, "p");
                    else if (nName.equals("span"))
                        parseSpan((Element) child);
                    else if (nName.matches("(h|H)\\d"))
                        parsePara((Element) child, nName);
                    else if (nName.equals("pre"))
                        parsePre((Element) child);
                    else
                        parseOtherElement((Element) child);

                }
                // else it is insignificant white space
            }
        }
        this.speller.cleanup();
    } catch (Exception e) {
        if (this.speller != null)
            this.speller.cleanup();
        throw new MMLSaveException(e);
    }
}

From source file:de.geeksfactory.opacclient.apis.Pica.java

protected DetailledItem parse_result(String html) {
    Document doc = Jsoup.parse(html);
    doc.setBaseUri(opac_url);//from  w w  w . ja va 2  s  . co m

    DetailledItem result = new DetailledItem();
    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)
        // 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 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;
}