// Copyright 2013 Michel Kraemer
//
// Licensed 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 de.undercouch.citeproc.mendeley;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import de.undercouch.citeproc.bibtex.NameParser;
import de.undercouch.citeproc.csl.CSLDate;
import de.undercouch.citeproc.csl.CSLDateBuilder;
import de.undercouch.citeproc.csl.CSLItemData;
import de.undercouch.citeproc.csl.CSLItemDataBuilder;
import de.undercouch.citeproc.csl.CSLName;
import de.undercouch.citeproc.csl.CSLNameBuilder;
import de.undercouch.citeproc.csl.CSLType;
/**
* <p>Converts Mendeley documents to CSL citation items.</p>
* <p>This implementation is based on the
* <a href="https://github.com/citation-style-editor/csl-editor/wiki/CSL-Editor-Type-and-Field-Mappings-for-Mendeley-Desktop">mapping</a>
* used in the <a href="http://editor.citationstyles.org/">CSL editor</a>.
* @author Michel Kraemer
*/
public class MendeleyConverter {
private static final String FIELD_ABSTRACT = "abstract";
private static final String FIELD_ACCESSED = "accessed";
private static final String FIELD_AUTHORS = "authors";
private static final String FIELD_CHAPTER = "chapter";
private static final String FIELD_CITATION_KEY = "citation_key";
private static final String FIELD_CITY = "city";
private static final String FIELD_DAY = "day";
private static final String FIELD_EDITION = "edition";
private static final String FIELD_EDITORS = "editors";
private static final String FIELD_FIRSTNAME = "first_name";
private static final String FIELD_GENRE = "genre";
private static final String FIELD_IDENTIFIERS = "identifiers";
private static final String FIELD_ISSUE = "issue";
private static final String FIELD_KEYWORDS = "keywords";
private static final String FIELD_LANGUAGE = "language";
private static final String FIELD_MEDIUM = "medium";
private static final String FIELD_MONTH = "month";
private static final String FIELD_PAGES = "pages";
private static final String FIELD_PUBLISHER = "publisher";
private static final String FIELD_REVISION = "revision";
private static final String FIELD_SERIES = "series";
private static final String FIELD_SERIES_EDITOR = "series_editor";
private static final String FIELD_SERIES_NUMBER = "series_number";
private static final String FIELD_SHORT_TITLE = "short_title";
private static final String FIELD_SOURCE = "source";
private static final String FIELD_LASTNAME = "last_name";
private static final String FIELD_TITLE = "title";
private static final String FIELD_TRANSLATORS = "translators";
private static final String FIELD_TYPE = "type";
private static final String FIELD_VOLUME = "volume";
private static final String FIELD_WEBSITES = "websites";
private static final String FIELD_YEAR = "year";
private static final String IDENTIFIER_DOI = "doi";
private static final String IDENTIFIER_ISBN = "isbn";
private static final String IDENTIFIER_ISSN = "issn";
private static final String IDENTIFIER_PMID = "pmid";
private static final String TYPE_BILL = "bill";
private static final String TYPE_BOOK = "book";
private static final String TYPE_BOOK_SECTION = "book_section";
private static final String TYPE_CASE = "case";
private static final String TYPE_COMPUTER_PROGRAM = "computer_program";
private static final String TYPE_CONFERENCE_PROCEEDINGS = "conference_proceedings";
private static final String TYPE_ENCYCLOPEDIA_ARTICLE = "encyclopedia_article";
private static final String TYPE_FILM = "film";
private static final String TYPE_GENERIC = "generic";
private static final String TYPE_HEARING = "hearing";
private static final String TYPE_JOURNAL = "journal";
private static final String TYPE_MAGAZINE_ARTICLE = "magazine_article";
private static final String TYPE_NEWSPAPER_ARTICLE = "newspaper_article";
private static final String TYPE_PATENT = "patent";
private static final String TYPE_REPORT = "report";
private static final String TYPE_STATUTE = "statute";
private static final String TYPE_TELEVISION_BROADCAST = "television_broadcast";
private static final String TYPE_THESIS = "thesis";
private static final String TYPE_WEB_PAGE = "web_page";
private static final String TYPE_WORKING_PAPER = "working_paper";
/**
* Converts the given Mendeley document to CSL item data
* @param documentId the Mendeley document's ID. Will be used as citation
* key if the document does not contain its own
* @param document the Mendeley document
* @return the CSL item data
*/
public static CSLItemData convert(String documentId, Map<String, Object> document) {
//convert citation id
String id = (String)document.get(FIELD_CITATION_KEY);
if (id == null) {
id = documentId;
}
//convert type
String mtype = strOrNull(document.get(FIELD_TYPE));
CSLType type = toType(mtype);
CSLItemDataBuilder builder = new CSLItemDataBuilder().id(id).type(type);
//convert authors
if (document.containsKey(FIELD_AUTHORS)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> al = (List<Map<String, Object>>)document.get(FIELD_AUTHORS);
builder.author(toAuthors(al));
}
//convert editors
if (document.containsKey(FIELD_EDITORS)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> el = (List<Map<String, Object>>)document.get(FIELD_EDITORS);
CSLName[] editors = toAuthors(el);
builder.editor(editors);
builder.collectionEditor(editors);
builder.containerAuthor(editors);
}
if (document.containsKey(FIELD_SERIES_EDITOR)) {
String seriesEditor = strOrNull(document.get(FIELD_SERIES_EDITOR));
if (seriesEditor != null) {
CSLName[] sen = NameParser.parse(seriesEditor);
builder.collectionEditor(sen);
builder.containerAuthor(sen);
}
}
//convert translators
if (document.containsKey(FIELD_TRANSLATORS)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> tl = (List<Map<String, Object>>)document.get(FIELD_TRANSLATORS);
builder.translator(toAuthors(tl));
}
//convert issue date
if (document.containsKey(FIELD_YEAR)) {
CSLDateBuilder db = new CSLDateBuilder();
int year = Integer.parseInt(document.get(FIELD_YEAR).toString());
if (document.containsKey(FIELD_MONTH)) {
int month = Integer.parseInt(document.get(FIELD_MONTH).toString());
if (document.containsKey(FIELD_DAY)) {
int day = Integer.parseInt(document.get(FIELD_DAY).toString());
db.dateParts(year, month, day);
} else {
db.dateParts(year, month);
}
} else {
db.dateParts(year);
}
CSLDate d = db.build();
builder.issued(d);
builder.eventDate(d);
}
//convert number
if (document.containsKey(FIELD_REVISION)) {
builder.number(strOrNull(document.get(FIELD_REVISION)));
} else {
builder.number(strOrNull(document.get(FIELD_SERIES_NUMBER)));
}
//convert container title
String containerTitle;
if (document.containsKey(FIELD_SERIES)) {
containerTitle = strOrNull(document.get(FIELD_SERIES));
} else {
containerTitle = strOrNull(document.get(FIELD_SOURCE));
}
builder.containerTitle(containerTitle);
builder.collectionTitle(containerTitle);
//convert publisher
if (mtype.equalsIgnoreCase(TYPE_PATENT) && document.containsKey(FIELD_SOURCE)) {
builder.publisher(strOrNull(document.get(FIELD_SOURCE)));
} else {
builder.publisher(strOrNull(document.get(FIELD_PUBLISHER)));
}
//convert access date
if (document.containsKey(FIELD_ACCESSED)) {
builder.accessed(new CSLDateBuilder().raw(
strOrNull(document.get(FIELD_ACCESSED))).build());
}
//convert identifiers
if (document.containsKey(FIELD_IDENTIFIERS)) {
@SuppressWarnings("unchecked")
Map<String, String> identifiers = (Map<String, String>)document.get(FIELD_IDENTIFIERS);
builder.DOI(identifiers.get(IDENTIFIER_DOI));
builder.ISBN(identifiers.get(IDENTIFIER_ISBN));
builder.ISSN(identifiers.get(IDENTIFIER_ISSN));
builder.PMID(identifiers.get(IDENTIFIER_PMID));
}
//convert keywords
if (document.containsKey(FIELD_KEYWORDS)) {
@SuppressWarnings("unchecked")
List<String> keywords = (List<String>)document.get(FIELD_KEYWORDS);
builder.keyword(StringUtils.join(keywords, ','));
}
//convert URL
if (document.containsKey(FIELD_WEBSITES)) {
@SuppressWarnings("unchecked")
List<String> websites = (List<String>)document.get(FIELD_WEBSITES);
if (websites.size() > 0) {
//use the first website only
builder.URL(websites.get(0));
}
}
//convert other fields
builder.abstrct(strOrNull(document.get(FIELD_ABSTRACT)));
builder.chapterNumber(strOrNull(document.get(FIELD_CHAPTER)));
builder.eventPlace(strOrNull(document.get(FIELD_CITY)));
builder.publisherPlace(strOrNull(document.get(FIELD_CITY)));
builder.edition(strOrNull(document.get(FIELD_EDITION)));
builder.genre(strOrNull(document.get(FIELD_GENRE)));
builder.issue(strOrNull(document.get(FIELD_ISSUE)));
builder.language(strOrNull(document.get(FIELD_LANGUAGE)));
builder.medium(strOrNull(document.get(FIELD_MEDIUM)));
builder.page(strOrNull(document.get(FIELD_PAGES)));
builder.shortTitle(strOrNull(document.get(FIELD_SHORT_TITLE)));
builder.title(strOrNull(document.get(FIELD_TITLE)));
builder.volume(strOrNull(document.get(FIELD_VOLUME)));
return builder.build();
}
/**
* Converts an object to a string
* @param o the object
* @return the string or <code>null</code> if <code>o</code> was
* also <code>null</code>
*/
private static String strOrNull(Object o) {
if (o == null) {
return null;
}
return o.toString();
}
/**
* Converts a list of authors
* @param authors the authors as returned by Mendeley
* @return the authors in CSL format
*/
private static CSLName[] toAuthors(List<Map<String, Object>> authors) {
CSLName[] result = new CSLName[authors.size()];
int i = 0;
for (Map<String, Object> a : authors) {
CSLNameBuilder builder = new CSLNameBuilder();
if (a.containsKey(FIELD_FIRSTNAME)) {
builder.given(strOrNull(a.get(FIELD_FIRSTNAME)));
}
if (a.containsKey(FIELD_LASTNAME)) {
builder.family(strOrNull(a.get(FIELD_LASTNAME)));
}
builder.parseNames(true);
result[i] = builder.build();
++i;
}
return result;
}
/**
* Converts a Mendeley type to a CSL type
* @param type the Mendeley type
* @return the CSL type
*/
private static CSLType toType(String type) {
if (type.equalsIgnoreCase(TYPE_BILL)) {
return CSLType.BILL;
} else if (type.equalsIgnoreCase(TYPE_BOOK)) {
return CSLType.BOOK;
} else if (type.equalsIgnoreCase(TYPE_BOOK_SECTION)) {
return CSLType.CHAPTER;
} else if (type.equalsIgnoreCase(TYPE_CASE)) {
return CSLType.ARTICLE;
} else if (type.equalsIgnoreCase(TYPE_COMPUTER_PROGRAM)) {
return CSLType.ARTICLE;
} else if (type.equalsIgnoreCase(TYPE_CONFERENCE_PROCEEDINGS)) {
return CSLType.PAPER_CONFERENCE;
} else if (type.equalsIgnoreCase(TYPE_ENCYCLOPEDIA_ARTICLE)) {
return CSLType.ENTRY_ENCYCLOPEDIA;
} else if (type.equalsIgnoreCase(TYPE_FILM)) {
return CSLType.MOTION_PICTURE;
} else if (type.equalsIgnoreCase(TYPE_GENERIC)) {
return CSLType.ARTICLE;
} else if (type.equalsIgnoreCase(TYPE_HEARING)) {
return CSLType.SPEECH;
} else if (type.equalsIgnoreCase(TYPE_JOURNAL)) {
return CSLType.ARTICLE_JOURNAL;
} else if (type.equalsIgnoreCase(TYPE_MAGAZINE_ARTICLE)) {
return CSLType.ARTICLE_MAGAZINE;
} else if (type.equalsIgnoreCase(TYPE_NEWSPAPER_ARTICLE)) {
return CSLType.ARTICLE_NEWSPAPER;
} else if (type.equalsIgnoreCase(TYPE_PATENT)) {
return CSLType.PATENT;
} else if (type.equalsIgnoreCase(TYPE_REPORT)) {
return CSLType.REPORT;
} else if (type.equalsIgnoreCase(TYPE_STATUTE)) {
return CSLType.LEGISLATION;
} else if (type.equalsIgnoreCase(TYPE_TELEVISION_BROADCAST)) {
return CSLType.BROADCAST;
} else if (type.equalsIgnoreCase(TYPE_THESIS)) {
return CSLType.THESIS;
} else if (type.equalsIgnoreCase(TYPE_WEB_PAGE)) {
return CSLType.WEBPAGE;
} else if (type.equalsIgnoreCase(TYPE_WORKING_PAPER)) {
return CSLType.ARTICLE;
}
return CSLType.ARTICLE;
}
}