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); } } }
private ParserResult parseFileContent() throws IOException { Map<String, String> meta = new HashMap<>(); while (!eof) { boolean found = consumeUncritically('@'); if (!found) { break; } skipWhitespace(); // Try to read the entry type String entryType = parseTextToken().toLowerCase().trim(); if ("preamble".equals(entryType)) { database.setPreamble(parsePreamble()); // Consume new line which signals end of preamble skipOneNewline(); // the preamble is saved verbatim anyways, so the text read so far can be dropped dumpTextReadSoFarToString(); } else if ("string".equals(entryType)) { parseBibtexString(); } else if ("comment".equals(entryType)) { parseJabRefComment(meta); } else { // Not a comment, preamble, or string. Thus, it is an entry parseAndAddEntry(entryType); } skipWhitespace(); } // Instantiate meta data: try { parserResult.setMetaData(MetaData.parse(meta)); } catch (ParseException exception) { parserResult.addWarning(exception.getLocalizedMessage()); } parseRemainingContent(); return parserResult; }
private BibtexString parseString() throws IOException { skipWhitespace(); consume('{', '('); skipWhitespace(); LOGGER.debug("Parsing string name"); String name = parseTextToken(); LOGGER.debug("Parsed string name"); skipWhitespace(); LOGGER.debug("Now the contents"); consume('='); String content = parseFieldContent(name); LOGGER.debug("Now I'm going to consume a }"); consume('}', ')'); // Consume new line which signals end of entry skipOneNewline(); LOGGER.debug("Finished string parsing."); String id = IdGenerator.next(); return new BibtexString(id, name, content); }
private String parseFieldContent(String key) throws IOException { skipWhitespace(); StringBuilder value = new StringBuilder(); int character; while (((character = peek()) != ',') && (character != '}') && (character != ')')) { if (eof) { throw new IOException("Error in line " + line + ": EOF in mid-string"); } if (character == '"') { StringBuilder text = parseQuotedFieldExactly(); value.append(fieldContentParser.format(text, key)); } else if (character == '{') { // Value is a string enclosed in brackets. There can be pairs // of brackets inside of a field, so we need to count the // brackets to know when the string is finished. StringBuilder text = parseBracketedTextExactly(); value.append(fieldContentParser.format(text, key)); } else if (Character.isDigit((char) character)) { // value is a number String number = parseTextToken(); value.append(number); } else if (character == '#') { consume('#'); } else { String textToken = parseTextToken(); if (textToken.isEmpty()) { throw new IOException( "Error in line " + line + " or above: " + "Empty text token.\nThis could be caused " + "by a missing comma between two fields."); } value.append('#').append(textToken).append('#'); } skipWhitespace(); } return value.toString(); }
private BibEntry parseEntry(String entryType) throws IOException { String id = IdGenerator.next(); BibEntry result = new BibEntry(id, entryType); skipWhitespace(); consume('{', '('); int character = peek(); if ((character != '\n') && (character != '\r')) { skipWhitespace(); } String key = parseKey(); result.setCiteKey(key); skipWhitespace(); while (true) { character = peek(); if ((character == '}') || (character == ')')) { break; } if (character == ',') { consume(','); } skipWhitespace(); character = peek(); if ((character == '}') || (character == ')')) { break; } parseField(result); } consume('}', ')'); // Consume new line which signals end of entry skipOneNewline(); return result; }
/** * Will parse the BibTex-Data found when reading from reader. Ignores any encoding supplied in the * file by "Encoding: myEncoding". * * <p>The reader will be consumed. * * <p>Multiple calls to parse() return the same results * * @return ParserResult * @throws IOException */ public ParserResult parse() throws IOException { // If we already parsed this, just return it. if (parserResult != null) { return parserResult; } // Bibtex related contents. initializeParserResult(); skipWhitespace(); try { return parseFileContent(); } catch (KeyCollisionException kce) { throw new IOException("Duplicate ID in bibtex file: " + kce); } }
private String parsePreamble() throws IOException { skipWhitespace(); return parseBracketedText().toString(); }