blob: 52fecc932d580e43a6d014a1be8674a3179e6164 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tika.parser.journal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.utils.XMLReaderUtils;
public class TEIDOMParser {
public TEIDOMParser() {
}
//returns first child with this name, null otherwise
private static Node getFirstChild(NodeList childNodes, String name) {
for (int i = 0; i < childNodes.getLength(); i++) {
Node n = childNodes.item(i);
if (n.getNodeName().equals(name)) {
return n;
}
}
return null;
}
private static String getFirstAttribute(Node node, String ns, String name) {
if (node.hasAttributes()) {
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
if (attr.getLocalName().equals(name)) {
return attr.getNodeValue();
}
}
}
return null;
}
private static List<Node> getChildNodes(NodeList childNodes, String localName) {
List<Node> ret = new ArrayList<>();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getLocalName() != null && child.getLocalName().equals(localName)) {
ret.add(child);
}
}
return ret;
}
public Metadata parse(String source, ParseContext parseContext)
throws TikaException, SAXException, IOException {
Document root = XMLReaderUtils
.buildDOM(new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)),
parseContext);
Metadata metadata = new Metadata();
createGrobidMetadata(source, root.getDocumentElement(), metadata);
return metadata;
}
private void createGrobidMetadata(String source, Element root, Metadata metadata) {
if (root != null) {
Node text = getFirstChild(root.getChildNodes(), "text");
if (text != null) {
parseText(text, metadata);
}
Node teiHeader = getFirstChild(root.getChildNodes(), "teiHeader");
Node fileDesc = getFirstChild(teiHeader.getChildNodes(), "fileDesc");
if (fileDesc != null) {
parseFileDesc(fileDesc, metadata);
}
Node profileDesc = getFirstChild(teiHeader.getChildNodes(), "profileDesc");
if (profileDesc != null) {
parseProfileDesc(profileDesc, metadata);
}
}
addStaticMet(source, root, metadata);
}
private void addStaticMet(String source, Element obj, Metadata metadata) {
metadata.add("Class", Metadata.class.getName());
//no longer available after we got rid of json.org's and its .toJSONObject()
// metadata.add("TEIJSONSource", obj.toString());
metadata.add("TEIXMLSource", source);
}
private void parseText(Node text, Metadata metadata) {
String lang = getFirstAttribute(text, "xml", "lang");
if (lang != null) {
metadata.add("Language", lang);
}
}
private void parseFileDesc(Node fileDesc, Metadata metadata) {
Node titleStmt = getFirstChild(fileDesc.getChildNodes(), "titleStmt");
if (titleStmt != null) {
parseTitleStmt(titleStmt, metadata);
}
Node sourceDesc = getFirstChild(fileDesc.getChildNodes(), "sourceDesc");
if (sourceDesc != null) {
parseSourceDesc(sourceDesc, metadata);
}
}
private void parseTitleStmt(Node titleStmt, Metadata metadata) {
Node title = getFirstChild(titleStmt.getChildNodes(), "title");
if (title != null) {
String titleText = title.getTextContent();
if (titleText != null) {
metadata.add("Title", titleText);
}
}
}
private void parseSourceDesc(Node sourceDesc, Metadata metadata) {
Node biblStruct = getFirstChild(sourceDesc.getChildNodes(), "biblStruct");
if (biblStruct != null) {
parseBiblStruct(biblStruct, metadata);
}
}
private void parseBiblStruct(Node biblStruct, Metadata metadata) {
Node analytic = getFirstChild(biblStruct.getChildNodes(), "analytic");
if (analytic != null) {
List<Node> authorNodes = getChildNodes(analytic.getChildNodes(), "author");
List<Author> authorList = new ArrayList<>();
for (Node authorNode : authorNodes) {
parseAuthor(authorNode, authorList);
}
metadata.add("Address", getMetadataAddresses(authorList));
metadata.add("Affiliation", getMetadataAffiliations(authorList));
metadata.add("Authors", getMetadataAuthors(authorList));
metadata.add("FullAffiliations", getMetadataFullAffiliations(authorList));
} else {
metadata.add("Error", "Unable to parse: no analytic section in JSON");
}
}
private String getMetadataFullAffiliations(List<Author> authorList) {
List<Affiliation> unique = new ArrayList<>();
StringBuilder metAffils = new StringBuilder();
for (Author a : authorList) {
for (Affiliation af : a.getAffiliations()) {
if (!unique.contains(af)) {
unique.add(af);
}
}
}
metAffils.append("[");
for (Affiliation af : unique) {
metAffils.append(af.toString());
metAffils.append(",");
}
metAffils.append(metAffils.deleteCharAt(metAffils.length() - 1));
metAffils.append("]");
return metAffils.toString();
}
private String getMetadataAuthors(List<Author> authorList) {
// generates Chris A. Mattmann 1, 2 Daniel J. Crichton 1 Nenad Medvidovic 2
// Steve Hughes 1
List<Affiliation> unique = new ArrayList<>();
StringBuilder metAuthors = new StringBuilder();
for (Author a : authorList) {
for (Affiliation af : a.getAffiliations()) {
if (!unique.contains(af)) {
unique.add(af);
}
}
}
for (Author a : authorList) {
metAuthors.append(printOrBlank(a.getFirstName()));
metAuthors.append(printOrBlank(a.getMiddleName()));
metAuthors.append(printOrBlank(a.getSurName()));
StringBuilder affilBuilder = new StringBuilder();
for (int idx = 0; idx < unique.size(); idx++) {
Affiliation af = unique.get(idx);
if (a.getAffiliations().contains(af)) {
affilBuilder.append((idx + 1));
affilBuilder.append(",");
}
}
if (affilBuilder.length() > 0) {
affilBuilder.deleteCharAt(affilBuilder.length() - 1);
}
metAuthors.append(affilBuilder.toString());
metAuthors.append(" ");
}
return metAuthors.toString();
}
private String getMetadataAffiliations(List<Author> authorList) {
// generates 1 Jet Propulsion Laboratory California Institute of Technology
// ; 2 Computer Science Department University of Southern California
List<Affiliation> unique = new ArrayList<>();
StringBuilder metAffil = new StringBuilder();
for (Author a : authorList) {
for (Affiliation af : a.getAffiliations()) {
if (!unique.contains(af)) {
unique.add(af);
}
}
}
int count = 1;
for (Affiliation a : unique) {
metAffil.append(count);
metAffil.append(" ");
metAffil.append(a.getOrgName().toString());
metAffil.deleteCharAt(metAffil.length() - 1);
metAffil.append("; ");
count++;
}
if (count > 1) {
metAffil.deleteCharAt(metAffil.length() - 1);
metAffil.deleteCharAt(metAffil.length() - 1);
}
return metAffil.toString();
}
private String getMetadataAddresses(List<Author> authorList) {
// generates: "Pasadena, CA 91109, USA Los Angeles, CA 90089, USA",
List<Address> unique = new ArrayList<>();
StringBuilder metAddress = new StringBuilder();
for (Author a : authorList) {
for (Affiliation af : a.getAffiliations()) {
if (!unique.contains(af.getAddress())) {
unique.add(af.getAddress());
}
}
}
for (Address ad : unique) {
metAddress.append(ad.toString());
metAddress.append(" ");
}
return metAddress.toString();
}
private void parseAuthor(Node authorNode, List<Author> authorList) {
Author author = new Author();
Node persName = getFirstChild(authorNode.getChildNodes(), "persName");
if (persName != null) {
List<Node> forenames = getChildNodes(persName.getChildNodes(), "forename");
for (Node forenameNode : forenames) {
parseNamePart(forenameNode, author);
}
Node surnameNode = getFirstChild(persName.getChildNodes(), "surname");
if (surnameNode != null) {
String surnameContent = surnameNode.getTextContent();
if (surnameContent != null) {
author.setSurName(surnameContent);
}
}
}
List<Node> affiliationNodes = getChildNodes(authorNode.getChildNodes(), "affiliation");
for (Node affiliationNode : affiliationNodes) {
parseOneAffiliation(affiliationNode, author);
}
authorList.add(author);
}
private void parseNamePart(Node namePart, Author author) {
String type = getFirstAttribute(namePart, null, "type");
String content = namePart.getTextContent();
if (type != null && content != null) {
if (type.equals("first")) {
author.setFirstName(content);
}
if (type.equals("middle")) {
author.setMiddleName(content);
}
}
}
private void parseOneAffiliation(Node affiliationNode, Author author) {
Affiliation affiliation = new Affiliation();
Node address = getFirstChild(affiliationNode.getChildNodes(), "address");
if (address != null) {
parseAddress(address, affiliation);
}
List<Node> orgNameNodes = getChildNodes(affiliationNode.getChildNodes(), "orgName");
OrgName orgName = new OrgName();
for (Node orgNameNode : orgNameNodes) {
parseOrgName(orgNameNode, orgName);
}
affiliation.setOrgName(orgName);
author.getAffiliations().add(affiliation);
}
private void parseAddress(Node addressNode, Affiliation affiliation) {
Address address = new Address();
Node region = getFirstChild(addressNode.getChildNodes(), "region");
if (region != null && region.getTextContent() != null) {
address.setRegion(region.getTextContent());
}
Node postCode = getFirstChild(addressNode.getChildNodes(), "postCode");
if (postCode != null && postCode.getTextContent() != null) {
address.setPostCode(postCode.getTextContent());
}
Node settlementNode = getFirstChild(addressNode.getChildNodes(), "settlement");
if (settlementNode != null && settlementNode.getTextContent() != null) {
address.setSettlment(settlementNode.getTextContent());
}
Node countryNode = getFirstChild(addressNode.getChildNodes(), "country");
if (countryNode != null) {
Country country = new Country();
String key = getFirstAttribute(countryNode, null, "key");
if (key != null) {
country.setKey(key);
}
String content = countryNode.getTextContent();
if (content != null) {
country.setContent(content);
}
address.setCountry(country);
}
affiliation.setAddress(address);
}
private void parseOrgName(Node orgNode, OrgName orgName) {
OrgTypeName typeName = new OrgTypeName();
String orgContent = orgNode.getTextContent();
if (orgContent != null) {
typeName.setName(orgContent);
}
String orgType = getFirstAttribute(orgNode, null, "type");
if (orgType != null) {
typeName.setType(orgType);
}
orgName.getTypeNames().add(typeName);
}
private void parseProfileDesc(Node profileDesc, Metadata metadata) {
Node abstractNode = getFirstChild(profileDesc.getChildNodes(), "abstract");
if (abstractNode != null) {
Node pNode = getFirstChild(abstractNode.getChildNodes(), "p");
if (pNode != null) {
metadata.add("Abstract", pNode.getTextContent());
}
}
Node textClassNode = getFirstChild(profileDesc.getChildNodes(), "textClass");
if (textClassNode != null) {
Node keywordsNode = getFirstChild(textClassNode.getChildNodes(), "keywords");
if (keywordsNode != null) {
List<Node> terms = getChildNodes(keywordsNode.getChildNodes(), "term");
if (terms.size() == 0) {
// test AJ15.pdf
metadata.add("Keyword", keywordsNode.getTextContent());
} else {
for (Node term : terms) {
metadata.add("Keyword", term.getTextContent());
}
}
}
}
}
private String printOrBlank(String val) {
if (val != null && !val.equals("")) {
return val + " ";
} else {
return " ";
}
}
class Author {
private String surName;
private String middleName;
private String firstName;
private List<Affiliation> affiliations;
public Author() {
this.surName = null;
this.middleName = null;
this.firstName = null;
this.affiliations = new ArrayList<>();
}
/**
* @return the surName
*/
public String getSurName() {
return surName;
}
/**
* @param surName the surName to set
*/
public void setSurName(String surName) {
this.surName = surName;
}
/**
* @return the middleName
*/
public String getMiddleName() {
return middleName;
}
/**
* @param middleName the middleName to set
*/
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
/**
* @return the firstName
*/
public String getFirstName() {
return firstName;
}
/**
* @param firstName the firstName to set
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* @return the affiliations
*/
public List<Affiliation> getAffiliations() {
return affiliations;
}
/**
* @param affiliations the affiliations to set
*/
public void setAffiliations(List<Affiliation> affiliations) {
this.affiliations = affiliations;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Author [surName=" + surName + ", middleName=" + middleName != null ?
middleName :
"" + ", firstName=" + firstName + ", affiliations=" + affiliations + "]";
}
}
class Affiliation {
private OrgName orgName;
private Address address;
public Affiliation() {
this.orgName = new OrgName();
this.address = new Address();
}
/**
* @return the orgName
*/
public OrgName getOrgName() {
return orgName;
}
/**
* @param orgName the orgName to set
*/
public void setOrgName(OrgName orgName) {
this.orgName = orgName;
}
/**
* @return the address
*/
public Address getAddress() {
return address;
}
/**
* @param address the address to set
*/
public void setAddress(Address address) {
this.address = address;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
Affiliation otherA = (Affiliation) obj;
return this.getAddress().equals(otherA.getAddress()) &&
this.getOrgName().equals(otherA.getOrgName());
}
@Override
public int hashCode() {
return Objects.hash(orgName, address);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Affiliation {orgName=" + orgName + ", address=" + address + "}";
}
}
class OrgName {
private List<OrgTypeName> typeNames;
public OrgName() {
this.typeNames = new ArrayList<>();
}
/**
* @return the typeNames
*/
public List<OrgTypeName> getTypeNames() {
return typeNames;
}
/**
* @param typeNames the typeNames to set
*/
public void setTypeNames(List<OrgTypeName> typeNames) {
this.typeNames = typeNames;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (OrgTypeName on : this.typeNames) {
builder.append(on.getName());
builder.append(" ");
}
return builder.toString();
}
@Override
public int hashCode() {
return Objects.hash(typeNames);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
OrgName otherA = (OrgName) obj;
if (otherA.getTypeNames() != null) {
if (this.typeNames == null) {
return false;
} else {
return this.typeNames.size() == otherA.getTypeNames().size();
}
} else {
if (this.typeNames == null) {
return true;
} else {
return false;
}
}
}
}
static class OrgTypeName {
private String name;
private String type;
public OrgTypeName() {
this.name = null;
this.type = null;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
OrgTypeName otherOrgName = (OrgTypeName) obj;
return this.type.equals(otherOrgName.getType()) &&
this.name.equals(otherOrgName.getName());
}
@Override
public int hashCode() {
return Objects.hash(name, type);
}
}
private class Address {
private String region;
private String postCode;
private String settlment;
private Country country;
public Address() {
this.region = null;
this.postCode = null;
this.settlment = null;
this.country = new Country();
}
/**
* @return the region
*/
public String getRegion() {
return region;
}
/**
* @param region the region to set
*/
public void setRegion(String region) {
this.region = region;
}
/**
* @return the postCode
*/
public String getPostCode() {
return postCode;
}
/**
* @param postCode the postCode to set
*/
public void setPostCode(String postCode) {
this.postCode = postCode;
}
/**
* @return the settlment
*/
public String getSettlment() {
return settlment;
}
/**
* @param settlment the settlment to set
*/
public void setSettlment(String settlment) {
this.settlment = settlment;
}
/**
* @return the country
*/
public Country getCountry() {
return country;
}
/**
* @param country the country to set
*/
public void setCountry(Country country) {
this.country = country;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
Address otherA = (Address) obj;
if (this.settlment == null) {
return otherA.getSettlment() == null;
} else if (this.country == null) {
return otherA.getCountry() == null;
} else if (this.postCode == null) {
return otherA.getPostCode() == null;
} else if (this.region == null) {
return otherA.getRegion() == null;
}
return this.settlment.equals(otherA.getSettlment()) &&
this.country.equals(otherA.getCountry()) &&
this.postCode.equals(otherA.getPostCode()) &&
this.region.equals(otherA.getRegion());
}
@Override
public int hashCode() {
return Objects.hash(region, postCode, settlment, country);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(settlment);
builder.append(", ");
builder.append(region);
builder.append(" ");
builder.append(postCode);
builder.append(" ");
builder.append(country.getContent());
return builder.toString();
}
}
private static class Country {
private String key;
private String content;
public Country() {
this.key = null;
this.content = null;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key the key to set
*/
public void setKey(String key) {
this.key = key;
}
/**
* @return the content
*/
public String getContent() {
return content;
}
/**
* @param content the content to set
*/
public void setContent(String content) {
this.content = content;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
Country otherC = (Country) obj;
if (this.key == null) {
if (otherC.getKey() != null) {
return false;
} else {
if (this.content == null) {
if (otherC.getContent() != null) {
return false;
} else {
return true;
}
} else {
return content.equals(otherC.getContent());
}
}
} else {
if (this.content == null) {
if (otherC.getContent() != null) {
return false;
} else {
return this.key.equals(otherC.getKey());
}
} else {
return this.key.equals(otherC.getKey()) &&
this.content.equals(otherC.getContent());
}
}
}
@Override
public int hashCode() {
return Objects.hash(key, content);
}
}
}