private void parseField(BibEntry entry) throws IOException { String key = parseTextToken().toLowerCase(); skipWhitespace(); consume('='); String content = parseFieldContent(key); if (!content.isEmpty()) { if (entry.hasField(key)) { // The following hack enables the parser to deal with multiple // author or // editor lines, stringing them together instead of getting just // one of them. // Multiple author or editor lines are not allowed by the bibtex // format, but // at least one online database exports bibtex like that, making // it inconvenient // for users if JabRef didn't accept it. if (InternalBibtexFields.getFieldExtras(key).contains(FieldProperties.PERSON_NAMES)) { entry.setField(key, entry.getFieldOptional(key).get() + " and " + content); } else if (FieldName.KEYWORDS.equals(key)) { // multiple keywords fields should be combined to one entry.addKeyword(content, Globals.prefs.get(JabRefPreferences.KEYWORD_SEPARATOR)); } } else { entry.setField(key, content); } } }
@Test public void importConvertsToCorrectBibType() throws IOException { String bsInput = "--AU-- Baklouti, F.\n" + "--YP-- 1999\n" + "--KW-- Cells; Rna; Isoforms\n" + "--TI-- Blood\n" + "--RT-- " + biblioscapeType + "\n" + "------"; List<BibEntry> bibEntries = bsImporter .importDatabase(new BufferedReader(new StringReader(bsInput))) .getDatabase() .getEntries(); BibEntry entry = new BibEntry(); entry.setField("author", "Baklouti, F."); entry.setField("keywords", "Cells; Rna; Isoforms"); entry.setField("title", "Blood"); entry.setField("year", "1999"); entry.setType(expectedBibType); Assert.assertEquals(Collections.singletonList(entry), bibEntries); }
@Test public void testSerialization() throws IOException { StringWriter stringWriter = new StringWriter(); BibEntry entry = new BibEntry("1234", "article"); // set a required field entry.setField("author", "Foo Bar"); entry.setField("journal", "International Journal of Something"); // set an optional field entry.setField("number", "1"); entry.setField("note", "some note"); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); // @formatter:off String expected = OS.NEWLINE + "@Article{," + OS.NEWLINE + " author = {Foo Bar}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + "}" + OS.NEWLINE; // @formatter:on assertEquals(expected, actual); }
@Test public void isMatchedForNormalAndFieldBasedSearchMixed() { BibEntry entry = new BibEntry(); entry.setType(BibtexEntryTypes.ARTICLE); entry.setField("author", "asdf"); entry.setField("abstract", "text"); assertTrue(new SearchQuery("text AND author=asdf", true, true).isMatch(entry)); }
@Test public void testSearchAllFieldsNotForSpecificField() { BibEntry e = new BibEntry(BibtexEntryTypes.INPROCEEDINGS.getName()); e.setField("title", "Fruity features"); e.setField("keywords", "banana, pineapple, orange"); SearchQuery searchQuery = new SearchQuery("anyfield=fruit and keywords!=banana", false, false); assertFalse(searchQuery.isMatch(e)); }
@Override public List<FieldChange> cleanup(BibEntry entry) { ArrayList<FieldChange> changes = new ArrayList<>(); // First check if the Doi Field is empty if (entry.getField("doi") != null) { String doiFieldValue = entry.getField("doi"); Optional<DOI> doi = DOI.build(doiFieldValue); if (doi.isPresent()) { String newValue = doi.get().getDOI(); if (!doiFieldValue.equals(newValue)) { entry.setField("doi", newValue); FieldChange change = new FieldChange(entry, "doi", doiFieldValue, newValue); changes.add(change); } // Doi field seems to contain Doi -> cleanup note, url, ee field for (String field : fields) { DOI.build(entry.getField((field))) .ifPresent(unused -> removeFieldValue(entry, field, changes)); } } } else { // As the Doi field is empty we now check if note, url, or ee field contains a Doi for (String field : fields) { Optional<DOI> doi = DOI.build(entry.getField(field)); if (doi.isPresent()) { // update Doi String oldValue = entry.getField("doi"); String newValue = doi.get().getDOI(); entry.setField("doi", newValue); FieldChange change = new FieldChange(entry, "doi", oldValue, newValue); changes.add(change); removeFieldValue(entry, field, changes); } } } return changes; }
/** * Unabbreviate the journal name of the given entry. * * @param entry The entry to be treated. * @param fieldName The field name (e.g. "journal") * @param ce If the entry is changed, add an edit to this compound. * @return true if the entry was changed, false otherwise. */ public boolean unabbreviate( BibDatabase database, BibEntry entry, String fieldName, CompoundEdit ce) { if (!entry.hasField(fieldName)) { return false; } String text = entry.getFieldOptional(fieldName).get(); String origText = text; if (database != null) { text = database.resolveForStrings(text); } if (!journalAbbreviationRepository.isKnownName(text)) { return false; // cannot do anything if it is not known } if (!journalAbbreviationRepository.isAbbreviatedName(text)) { return false; // cannot unabbreviate unabbreviated name. } Abbreviation abbreviation = journalAbbreviationRepository.getAbbreviation(text).get(); // must be here String newText = abbreviation.getName(); entry.setField(fieldName, newText); ce.addEdit(new UndoableFieldChange(entry, fieldName, origText, newText)); return true; }
@Test // For https://github.com/JabRef/jabref/issues/1873 public void containsOnlyMatchesCompletePhraseWithSlash() throws Exception { entry.setField(FieldName.GROUPS, "myExplicitGroup/b"); assertFalse(group.contains(entry)); }
@Test public void roundtripWithUserCommentAndEntryChange() throws Exception { Path testBibtexFile = Paths.get("src/test/resources/testbib/bibWithUserComments.bib"); Charset encoding = StandardCharsets.UTF_8; ParserResult result = new BibtexParser(importFormatPreferences) .parse(Importer.getReader(testBibtexFile, encoding)); BibEntry entry = result.getDatabase().getEntryByKey("1137631").get(); entry.setField("author", "Mr. Author"); SavePreferences preferences = new SavePreferences().withEncoding(encoding).withSaveInOriginalOrder(true); BibDatabaseContext context = new BibDatabaseContext( result.getDatabase(), result.getMetaData(), new Defaults(BibDatabaseMode.BIBTEX)); StringSaveSession session = databaseWriter.savePartOfDatabase(context, result.getDatabase().getEntries(), preferences); try (Scanner scanner = new Scanner( Paths.get("src/test/resources/testbib/bibWithUserCommentAndEntryChange.bib"), encoding.name())) { assertEquals(scanner.useDelimiter("\\A").next(), session.getStringValue()); } }
@Test public void testGrammarSearchFullEntry() { BibEntry entry = new BibEntry(); entry.setField(FieldName.TITLE, "systematic review"); SearchQuery searchQuery = new SearchQuery("title=\"systematic review\"", false, false); assertTrue(searchQuery.isMatch(entry)); }
@Test public void addDuplicateGroupDoesNotChangeGroupsField() throws Exception { entry.setField(FieldName.GROUPS, "myExplicitGroup"); group.add(entry); assertEquals(Optional.of("myExplicitGroup"), entry.getField(FieldName.GROUPS)); }
@Test public void reformatEntryIfAskedToDoSo() throws Exception { BibEntry entry = new BibEntry(); entry.setType(BibtexEntryTypes.ARTICLE); entry.setField("author", "Mr. author"); entry.setParsedSerialization("wrong serialization"); entry.setChanged(false); database.insertEntry(entry); SavePreferences preferences = new SavePreferences().withReformatFile(true); StringSaveSession session = databaseWriter.savePartOfDatabase( bibtexContext, Collections.singletonList(entry), preferences); assertEquals( OS.NEWLINE + "@Article{," + OS.NEWLINE + " author = {Mr. author}," + OS.NEWLINE + "}" + OS.NEWLINE + OS.NEWLINE + "@Comment{jabref-meta: databaseType:bibtex;}" + OS.NEWLINE, session.getStringValue()); }
@Test public void addSingleGroupToNonemptyBibEntryAppendsToGroupsField() { entry.setField(FieldName.GROUPS, "some thing"); group.add(entry); assertEquals(Optional.of("some thing, myExplicitGroup"), entry.getField(FieldName.GROUPS)); }
@Test public void testSearchMatchesSingleKeyword() { BibEntry e = new BibEntry(BibtexEntryTypes.INPROCEEDINGS.getName()); e.setField("keywords", "banana, pineapple, orange"); SearchQuery searchQuery = new SearchQuery("anykeyword==pineapple", false, false); assertTrue(searchQuery.isMatch(e)); }
@Test // For https://github.com/JabRef/jabref/issues/2334 public void removeDoesNotChangeFieldIfContainsNameAsWord() throws Exception { entry.setField(FieldName.GROUPS, "myExplicitGroup alternative"); group.remove(entry); assertEquals(Optional.of("myExplicitGroup alternative"), entry.getField(FieldName.GROUPS)); }
@Test public void testSearchingForOpenBraketInBooktitle() { BibEntry e = new BibEntry(BibtexEntryTypes.INPROCEEDINGS.getName()); e.setField(FieldName.BOOKTITLE, "Super Conference (SC)"); SearchQuery searchQuery = new SearchQuery("booktitle=\"(\"", false, false); assertTrue(searchQuery.isMatch(e)); }
private static BibEntry downloadEntryBibTeX(String id, boolean downloadAbstract) { try { URL url = new URL( ACMPortalFetcher.START_URL + ACMPortalFetcher.BIBTEX_URL + id + ACMPortalFetcher.BIBTEX_URL_END); URLConnection connection = url.openConnection(); // set user-agent to avoid being blocked as a crawler connection.addRequestProperty( "User-Agent", "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"); Collection<BibEntry> items = null; try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { items = BibtexParser.parse(in).getDatabase().getEntries(); } catch (IOException e) { LOGGER.info("Download of BibTeX information from ACM Portal failed.", e); } if ((items == null) || items.isEmpty()) { return null; } BibEntry entry = items.iterator().next(); Thread.sleep( ACMPortalFetcher.WAIT_TIME); // wait between requests or you will be blocked by ACM // get abstract if (downloadAbstract) { url = new URL(ACMPortalFetcher.START_URL + ACMPortalFetcher.ABSTRACT_URL + id); String page = Util.getResults(url); Matcher absM = ACMPortalFetcher.ABSTRACT_PATTERN.matcher(page); if (absM.find()) { entry.setField("abstract", absM.group(1).trim()); } Thread.sleep( ACMPortalFetcher.WAIT_TIME); // wait between requests or you will be blocked by ACM } return entry; } catch (NoSuchElementException e) { LOGGER.info( "Bad Bibtex record read at: " + ACMPortalFetcher.BIBTEX_URL + id + ACMPortalFetcher.BIBTEX_URL_END, e); return null; } catch (MalformedURLException e) { LOGGER.info("Malformed URL.", e); return null; } catch (IOException e) { LOGGER.info("Cannot connect.", e); return null; } catch (InterruptedException ignored) { return null; } }
@Test public void notFoundByDOI() throws IOException { // CI server is unreliable Assume.assumeFalse(DevEnvironment.isCIServer()); entry.setField("doi", "10.1021/bk-2006-WWW.ch014"); Assert.assertEquals(Optional.empty(), finder.findFullText(entry)); }
@Test public void testIsMatch() { BibEntry entry = new BibEntry(); entry.setType(BibtexEntryTypes.ARTICLE); entry.setField("author", "asdf"); assertFalse(new SearchQuery("qwer", true, true).isMatch(entry)); assertTrue(new SearchQuery("asdf", true, true).isMatch(entry)); assertTrue(new SearchQuery("author=asdf", true, true).isMatch(entry)); }
@Test public void findByDOI() throws IOException { // CI server is unreliable Assume.assumeFalse(DevEnvironment.isCIServer()); entry.setField("doi", "10.1021/bk-2006-STYG.ch014"); Assert.assertEquals( Optional.of(new URL("http://pubs.acs.org/doi/pdf/10.1021/bk-2006-STYG.ch014")), finder.findFullText(entry)); }
@Override public void getEntries(Map<String, Boolean> selection, ImportInspector inspector) { for (Map.Entry<String, Boolean> selentry : selection.entrySet()) { if (!shouldContinue) { break; } boolean sel = selentry.getValue(); if (sel) { BibEntry entry = downloadEntryBibTeX(selentry.getKey(), fetchAbstract); if (entry != null) { // Convert from HTML and optionally add curly brackets around key words to keep the case entry .getFieldOptional("title") .ifPresent( title -> { title = title.replaceAll("\\\\&", "&").replaceAll("\\\\#", "#"); title = convertHTMLChars(title); // Unit formatting if (Globals.prefs.getBoolean(JabRefPreferences.USE_UNIT_FORMATTER_ON_SEARCH)) { title = unitFormatter.format(title); } // Case keeping if (Globals.prefs.getBoolean(JabRefPreferences.USE_CASE_KEEPER_ON_SEARCH)) { title = caseKeeper.format(title); } entry.setField("title", title); }); entry .getFieldOptional("abstract") .ifPresent( abstr -> { entry.setField("abstract", convertHTMLChars(abstr)); }); inspector.addEntry(entry); } } } }
@Test public void roundTripWithCamelCasingInTheOriginalEntryAndResultInLowerCase() throws IOException { // @formatter:off String bibtexEntry = OS.NEWLINE + "@Article{test," + OS.NEWLINE + " Author = {Foo Bar}," + OS.NEWLINE + " Journal = {International Journal of Something}," + OS.NEWLINE + " Note = {some note}," + OS.NEWLINE + " Number = {1}," + OS.NEWLINE + " HowPublished = {asdf}," + OS.NEWLINE + "}"; // @formatter:on // read in bibtex string ParserResult result = BibtexParser.parse(new StringReader(bibtexEntry)); Collection<BibEntry> entries = result.getDatabase().getEntries(); BibEntry entry = entries.iterator().next(); // modify entry entry.setField("author", "BlaBla"); // write out bibtex string StringWriter stringWriter = new StringWriter(); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); // @formatter:off String expected = OS.NEWLINE + "@Article{test," + OS.NEWLINE + " author = {BlaBla}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + " howpublished = {asdf}," + OS.NEWLINE + "}" + OS.NEWLINE; // @formatter:on assertEquals(expected, actual); }
@Test public void roundTripWithPrecedingCommentAndModificationTest() throws IOException { // @formatter:off String bibtexEntry = "% Some random comment that should stay here" + OS.NEWLINE + "@Article{test," + OS.NEWLINE + " Author = {Foo Bar}," + OS.NEWLINE + " Journal = {International Journal of Something}," + OS.NEWLINE + " Note = {some note}," + OS.NEWLINE + " Number = {1}" + OS.NEWLINE + "}"; // @formatter:on // read in bibtex string ParserResult result = BibtexParser.parse(new StringReader(bibtexEntry)); Collection<BibEntry> entries = result.getDatabase().getEntries(); BibEntry entry = entries.iterator().next(); // change the entry entry.setField("author", "John Doe"); // write out bibtex string StringWriter stringWriter = new StringWriter(); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); // @formatter:off String expected = "% Some random comment that should stay here" + OS.NEWLINE + OS.NEWLINE + "@Article{test," + OS.NEWLINE + " author = {John Doe}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + "}" + OS.NEWLINE; // @formatter:on assertEquals(expected, actual); }
@Test public void addFieldWithLongerLength() throws IOException { // @formatter:off String bibtexEntry = OS.NEWLINE + OS.NEWLINE + "@Article{test," + OS.NEWLINE + " author = {BlaBla}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + "}"; // @formatter:on // read in bibtex string ParserResult result = BibtexParser.parse(new StringReader(bibtexEntry)); Collection<BibEntry> entries = result.getDatabase().getEntries(); BibEntry entry = entries.iterator().next(); // modify entry entry.setField("howpublished", "asdf"); // write out bibtex string StringWriter stringWriter = new StringWriter(); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); // @formatter:off String expected = OS.NEWLINE + "@Article{test," + OS.NEWLINE + " author = {BlaBla}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + " howpublished = {asdf}," + OS.NEWLINE + "}" + OS.NEWLINE; // @formatter:on assertEquals(expected, actual); }
@Test public void writeEntriesInOriginalOrderWhenNoSaveOrderConfigIsSetInMetadata() throws Exception { BibEntry firstEntry = new BibEntry(); firstEntry.setType(BibtexEntryTypes.ARTICLE); firstEntry.setField("author", "A"); firstEntry.setField("year", "2010"); BibEntry secondEntry = new BibEntry(); secondEntry.setType(BibtexEntryTypes.ARTICLE); secondEntry.setField("author", "B"); secondEntry.setField("year", "2000"); BibEntry thirdEntry = new BibEntry(); thirdEntry.setType(BibtexEntryTypes.ARTICLE); thirdEntry.setField("author", "A"); thirdEntry.setField("year", "2000"); database.insertEntry(firstEntry); database.insertEntry(secondEntry); database.insertEntry(thirdEntry); SavePreferences preferences = new SavePreferences().withSaveInOriginalOrder(false); StringSaveSession session = databaseWriter.savePartOfDatabase(bibtexContext, database.getEntries(), preferences); assertEquals( OS.NEWLINE + "@Article{," + OS.NEWLINE + " author = {A}," + OS.NEWLINE + " year = {2010}," + OS.NEWLINE + "}" + OS.NEWLINE + OS.NEWLINE + "@Article{," + OS.NEWLINE + " author = {B}," + OS.NEWLINE + " year = {2000}," + OS.NEWLINE + "}" + OS.NEWLINE + OS.NEWLINE + "@Article{," + OS.NEWLINE + " author = {A}," + OS.NEWLINE + " year = {2000}," + OS.NEWLINE + "}" + OS.NEWLINE + OS.NEWLINE + "@Comment{jabref-meta: databaseType:bibtex;}" + OS.NEWLINE, session.getStringValue()); }
@Test public void doNotWriteEmptyFields() throws IOException { StringWriter stringWriter = new StringWriter(); BibEntry entry = new BibEntry("1234", "article"); entry.setField("author", " "); entry.setField("note", "some note"); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); String expected = OS.NEWLINE + "@Article{," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + "}" + OS.NEWLINE; assertEquals(expected, actual); }
@Override public List<FieldChange> remove(List<BibEntry> entriesToRemove) { Objects.requireNonNull(entriesToRemove); List<FieldChange> changes = new ArrayList<>(); for (BibEntry entry : entriesToRemove) { if (contains(entry)) { String oldContent = entry.getField(searchField).orElse(""); KeywordList wordlist = KeywordList.parse(oldContent, keywordSeparator); wordlist.remove(searchExpression); String newContent = wordlist.getAsString(keywordSeparator); entry.setField(searchField, newContent).ifPresent(changes::add); } } return changes; }
@Override public void redo() { super.redo(); // Redo the change. try { if (newValue == null) { entry.clearField(field); } else { entry.setField(field, newValue); } } catch (IllegalArgumentException ex) { LOGGER.info("Cannot perform redo", ex); } }
@Override public void undo() { super.undo(); // Revert the change. try { if (oldValue == null) { entry.clearField(field); } else { entry.setField(field, oldValue); } // this is the only exception explicitly thrown here } catch (IllegalArgumentException ex) { LOGGER.info("Cannot perform undo", ex); } }
@Override public List<FieldChange> cleanup(BibEntry entry) { ArrayList<FieldChange> changes = new ArrayList<>(); final String[] fields = {"title", "author", "abstract"}; for (String field : fields) { String oldValue = entry.getField(field); if (oldValue == null) { break; } final HTMLConverter htmlConverter = new HTMLConverter(); String newValue = htmlConverter.formatUnicode(oldValue); if (!oldValue.equals(newValue)) { entry.setField(field, newValue); changes.add(new FieldChange(entry, field, oldValue, newValue)); } } return changes; }