@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()); }
/** * Checks if the two entries represent the same publication. * * @param one BibEntry * @param two BibEntry * @return boolean */ public static boolean isDuplicate(BibEntry one, BibEntry two, BibDatabaseMode bibDatabaseMode) { // First check if they are of the same type - a necessary condition: if (!one.getType().equals(two.getType())) { return false; } EntryType type = EntryTypes.getTypeOrDefault(one.getType(), bibDatabaseMode); // The check if they have the same required fields: java.util.List<String> var = type.getRequiredFieldsFlat(); String[] fields = var.toArray(new String[var.size()]); double[] req; if (fields == null) { req = new double[] {0., 0.}; } else { req = DuplicateCheck.compareFieldSet(fields, one, two); } if (Math.abs(req[0] - DuplicateCheck.duplicateThreshold) > DuplicateCheck.DOUBT_RANGE) { // Far from the threshold value, so we base our decision on the req. fields only return req[0] >= DuplicateCheck.duplicateThreshold; } // Close to the threshold value, so we take a look at the optional fields, if any: java.util.List<String> optionalFields = type.getOptionalFields(); fields = optionalFields.toArray(new String[optionalFields.size()]); if (fields != null) { double[] opt = DuplicateCheck.compareFieldSet(fields, one, two); double totValue = ((DuplicateCheck.REQUIRED_WEIGHT * req[0] * req[1]) + (opt[0] * opt[1])) / ((req[1] * DuplicateCheck.REQUIRED_WEIGHT) + opt[1]); return totValue >= DuplicateCheck.duplicateThreshold; } return req[0] >= DuplicateCheck.duplicateThreshold; }
@Test public void writeEntryWithCustomizedTypeAlsoWritesTypeDeclaration() throws Exception { try { EntryTypes.addOrModifyCustomEntryType( new CustomEntryType("customizedType", "required", "optional"), BibDatabaseMode.BIBTEX); BibEntry entry = new BibEntry(); entry.setType("customizedType"); database.insertEntry(entry); StringSaveSession session = databaseWriter.savePartOfDatabase( bibtexContext, Collections.singletonList(entry), new SavePreferences()); assertEquals( OS.NEWLINE + "@Customizedtype{," + OS.NEWLINE + "}" + OS.NEWLINE + OS.NEWLINE + "@Comment{jabref-meta: databaseType:bibtex;}" + OS.NEWLINE + OS.NEWLINE + "@Comment{jabref-entrytype: Customizedtype: req[required] opt[optional]}" + OS.NEWLINE, session.getStringValue()); } finally { EntryTypes.removeAllCustomEntryTypes(); } }
/** * 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 public void monthFieldSpecialSyntax() throws IOException { // @formatter:off String bibtexEntry = "@Article{test," + OS.NEWLINE + " Author = {Foo Bar}," + OS.NEWLINE + " Month = mar," + 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(); // modify month field Set<String> fields = entry.getFieldNames(); assertTrue(fields.contains("month")); assertEquals("#mar#", entry.getFieldOptional("month").get()); // write out bibtex string StringWriter stringWriter = new StringWriter(); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); assertEquals(bibtexEntry, actual); }
@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 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 addDuplicateGroupDoesNotChangeGroupsField() throws Exception { entry.setField(FieldName.GROUPS, "myExplicitGroup"); group.add(entry); assertEquals(Optional.of("myExplicitGroup"), entry.getField(FieldName.GROUPS)); }
private boolean isAnyFieldSetForSelectedEntry(List<String> fieldnames) { if (panel.getMainTable().getSelectedRowCount() == 1) { BibEntry entry = panel.getMainTable().getSelected().get(0); return !Collections.disjoint(fieldnames, entry.getFieldNames()); } return false; }
private void index() { /* Old version, from when set was a TreeSet. // The boolean "changing" is true in the situation that an entry is about to change, // and has temporarily been removed from the entry set in this sorter. So, if we index // now, we will cause exceptions other places because one entry has been left out of // the indexed array. Simply waiting foth this to change can lead to deadlocks, // so we have no other choice than to return without indexing. if (changing) return; */ synchronized (set) { // Resort if necessary: if (changed) { Collections.sort(set, comp); changed = false; } // Create an array of IDs for quick access, since getIdAt() is called by // getValueAt() in EntryTableModel, which *has* to be efficient. int count = set.size(); idArray = new String[count]; entryArray = new BibEntry[count]; int piv = 0; for (BibEntry entry : set) { idArray[piv] = entry.getId(); entryArray[piv] = entry; piv++; } } }
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); } } }
/** * Generates a Citation based on the given entry, style, and output format WARNING: the citation * is generated with JavaScript which may take some time, better call it in outside the main * Thread */ protected static String generateCitation( BibEntry entry, String style, CitationStyleOutputFormat outputFormat) { try { String citeKey = entry.getCiteKeyOptional().orElse(""); BibTeXEntry bibTeXEntry = new BibTeXEntry(new Key(entry.getType()), new Key(citeKey)); for (Map.Entry<String, String> field : entry.getFieldMap().entrySet()) { String value = UNICODE_TO_LATEX_FORMATTER.format(field.getValue()); bibTeXEntry.addField(new Key(field.getKey()), new DigitStringValue(value)); } CSLItemData cslItemData = BIBTEX_CONVERTER.toItemData(bibTeXEntry); Bibliography bibliography = CSL.makeAdhocBibliography(style, outputFormat.getFormat(), cslItemData); return bibliography.getEntries()[0]; } catch (IOException | ArrayIndexOutOfBoundsException e) { LOGGER.error("Could not generate BibEntry citation", e); return Localization.lang("Cannot generate preview based on selected citation style."); } catch (TokenMgrException e) { LOGGER.error("Bad character inside BibEntry", e); // sadly one cannot easily retrieve the bad char from the TokenMgrError return new StringBuilder() .append(Localization.lang("Cannot generate preview based on selected citation style.")) .append(outputFormat == CitationStyleOutputFormat.HTML ? "<br>" : "\n") .append(Localization.lang("Bad character inside entry")) .append(outputFormat == CitationStyleOutputFormat.HTML ? "<br>" : "\n") .append(e.getLocalizedMessage()) .toString(); } }
@Test public void testGrammarSearch() { BibEntry entry = new BibEntry(); entry.addKeyword("one two", ','); SearchQuery searchQuery = new SearchQuery("keywords=\"one two\"", false, false); assertTrue(searchQuery.isMatch(entry)); }
@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 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 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)); }
@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)); }
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 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)); }
private Set<String> getFieldContentAsWords(BibEntry entry) { if (onlySplitWordsAtSeparator) { return entry .getField(searchField) .map(content -> KeywordList.parse(content, keywordSeparator).toStringList()) .orElse(Collections.emptySet()); } else { return entry.getFieldAsWords(searchField); } }
@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)); }
/** * Check the database to find out whether any of a set of fields are used for any of the entries. * * @param database The bib database. * @param fields The set of fields to look for. * @return true if at least one of the given fields is set in at least one entry, false otherwise. */ private boolean linksFound(BibDatabase database, String[] fields) { for (BibEntry entry : database.getEntries()) { for (String field : fields) { if (entry.getField(field) != null) { return true; } } } return false; }
private static int compareSingleField(String field, BibEntry one, BibEntry two) { String s1 = one.getField(field); String s2 = two.getField(field); if (s1 == null) { if (s2 == null) { return EMPTY_IN_BOTH; } return EMPTY_IN_ONE; } else if (s2 == null) { return EMPTY_IN_TWO; } if ("author".equals(field) || "editor".equals(field)) { // Specific for name fields. // Harmonise case: String auth1 = AuthorList.fixAuthor_lastNameOnlyCommas(s1, false).replace(" and ", " ").toLowerCase(); String auth2 = AuthorList.fixAuthor_lastNameOnlyCommas(s2, false).replace(" and ", " ").toLowerCase(); double similarity = DuplicateCheck.correlateByWords(auth1, auth2); if (similarity > 0.8) { return EQUAL; } return NOT_EQUAL; } else if ("pages".equals(field)) { // Pages can be given with a variety of delimiters, "-", "--", " - ", " -- ". // We do a replace to harmonize these to a simple "-": // After this, a simple test for equality should be enough: s1 = s1.replaceAll("[- ]+", "-"); s2 = s2.replaceAll("[- ]+", "-"); if (s1.equals(s2)) { return EQUAL; } return NOT_EQUAL; } else if ("journal".equals(field)) { // We do not attempt to harmonize abbreviation state of the journal names, // but we remove periods from the names in case they are abbreviated with // and without dots: s1 = s1.replace(".", "").toLowerCase(); s2 = s2.replace(".", "").toLowerCase(); double similarity = DuplicateCheck.correlateByWords(s1, s2); if (similarity > 0.8) { return EQUAL; } return NOT_EQUAL; } else { s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); double similarity = DuplicateCheck.correlateByWords(s1, s2); if (similarity > 0.8) { return EQUAL; } return NOT_EQUAL; } }
@Test public void testEntryTypeChange() throws IOException { // @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 // read in bibtex string ParserResult result = BibtexParser.parse(new StringReader(expected)); Collection<BibEntry> entries = result.getDatabase().getEntries(); BibEntry entry = entries.iterator().next(); // modify entry entry.setType("inproceedings"); // write out bibtex string StringWriter stringWriter = new StringWriter(); writer.write(entry, stringWriter, BibDatabaseMode.BIBTEX); String actual = stringWriter.toString(); // @formatter:off String expectedNewEntry = OS.NEWLINE + "@InProceedings{test," + OS.NEWLINE + " author = {BlaBla}," + OS.NEWLINE + " number = {1}," + OS.NEWLINE + " note = {some note}," + OS.NEWLINE + " howpublished = {asdf}," + OS.NEWLINE + " journal = {International Journal of Something}," + OS.NEWLINE + "}" + OS.NEWLINE; // @formatter:on assertEquals(expectedNewEntry, actual); }
@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); }
/** * Will check if the current library uses any entry types from another mode. For example it will * warn the user if he uses entry types defined for Biblatex inside a BibTeX library. */ @Override public List<IntegrityMessage> check(BibEntry entry) { if (EntryTypes.isExclusiveBibLatex(entry.getType())) { return Collections.singletonList( new IntegrityMessage( Localization.lang( "Entry type %0 is only defined for Biblatex but not for BibTeX", entry.getType()), entry, "bibtexkey")); } return Collections.emptyList(); }
@Override public Optional<URL> findFullText(BibEntry entry) throws IOException { Objects.requireNonNull(entry); String stampString = ""; // Try URL first -- will primarily work for entries from the old IEEE search Optional<String> urlString = entry.getField(FieldName.URL); if (urlString.isPresent()) { // Is the URL a direct link to IEEE? Matcher matcher = STAMP_PATTERN.matcher(urlString.get()); if (matcher.find()) { // Found it stampString = matcher.group(1); } } // If not, try DOI if (stampString.isEmpty()) { Optional<DOI> doi = entry.getField(FieldName.DOI).flatMap(DOI::build); if (doi.isPresent() && doi.get().getDOI().startsWith(IEEE_DOI) && doi.get().getURI().isPresent()) { // Download the HTML page from IEEE String resolvedDOIPage = new URLDownload(doi.get().getURI().get().toURL()) .downloadToString(StandardCharsets.UTF_8); // Try to find the link Matcher matcher = STAMP_PATTERN.matcher(resolvedDOIPage); if (matcher.find()) { // Found it stampString = matcher.group(1); } } } // Any success? if (stampString.isEmpty()) { return Optional.empty(); } // Download the HTML page containing a frame with the PDF String framePage = new URLDownload(BASE_URL + stampString).downloadToString(StandardCharsets.UTF_8); // Try to find the direct PDF link Matcher matcher = PDF_PATTERN.matcher(framePage); if (matcher.find()) { // The PDF was found LOGGER.debug("Full text document found on IEEE Xplore"); return Optional.of(new URL(matcher.group(1))); } return Optional.empty(); }
@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); }