public void checkEntryTypes(ParserResult _pr) { for (BibtexEntry be : _db.getEntries()) { if (be.getType() instanceof UnknownEntryType) { // Look up the unknown type name in our map of parsed types: Object o = entryTypes.get(be.getType().getName().toLowerCase()); if (o != null) { BibtexEntryType type = (BibtexEntryType) o; be.setType(type); } else { // System.out.println("Unknown entry type: // "+be.getType().getName()); _pr.addWarning( Globals.lang("unknown entry type") + ": " + be.getType().getName() + ". " + Globals.lang("Type set to 'other'") + "."); be.setType(BibtexEntryType.OTHER); } } } }
/** * Tries to restore the key * * @return rest of key on success, otherwise empty string * @throws IOException on Reader-Error */ private String fixKey() throws IOException { StringBuilder key = new StringBuilder(); int lookahead_used = 0; char currentChar; // Find a char which ends key (','&&'\n') or entryfield ('='): do { currentChar = (char) read(); key.append(currentChar); lookahead_used++; } while ((currentChar != ',' && currentChar != '\n' && currentChar != '=') && (lookahead_used < LOOKAHEAD)); // Consumed a char too much, back into reader and remove from key: unread(currentChar); key.deleteCharAt(key.length() - 1); // Restore if possible: switch (currentChar) { case '=': // Get entryfieldname, push it back and take rest as key key = key.reverse(); boolean matchedAlpha = false; for (int i = 0; i < key.length(); i++) { currentChar = key.charAt(i); /// Skip spaces: if (!matchedAlpha && currentChar == ' ') { continue; } matchedAlpha = true; // Begin of entryfieldname (e.g. author) -> push back: unread(currentChar); if (currentChar == ' ' || currentChar == '\n') { /* * found whitespaces, entryfieldname completed -> key in * keybuffer, skip whitespaces */ StringBuilder newKey = new StringBuilder(); for (int j = i; j < key.length(); j++) { currentChar = key.charAt(j); if (!Character.isWhitespace(currentChar)) { newKey.append(currentChar); } } // Finished, now reverse newKey and remove whitespaces: _pr.addWarning( Globals.lang("Line %0: Found corrupted BibTeX-key.", String.valueOf(line))); key = newKey.reverse(); } } break; case ',': _pr.addWarning( Globals.lang( "Line %0: Found corrupted BibTeX-key (contains whitespaces).", String.valueOf(line))); case '\n': _pr.addWarning( Globals.lang( "Line %0: Found corrupted BibTeX-key (comma missing).", String.valueOf(line))); break; default: // No more lookahead, give up: unreadBuffer(key); return ""; } return removeWhitespaces(key).toString(); }
/** * Will parse the BibTex-Data found when reading from reader. * * <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 (_pr != null) return _pr; _db = new BibtexDatabase(); // Bibtex related contents. _meta = new HashMap<String, String>(); // Metadata in comments for Bibkeeper. entryTypes = new HashMap<String, BibtexEntryType>(); // To store custem entry types parsed. _pr = new ParserResult(_db, _meta, entryTypes); // First see if we can find the version number of the JabRef version that // wrote the file: String versionNum = readJabRefVersionNumber(); if (versionNum != null) { _pr.setJabrefVersion(versionNum); setMajorMinorVersions(); } else { // No version number found. However, we have only } skipWhitespace(); try { while (!_eof) { boolean found = consumeUncritically('@'); if (!found) break; skipWhitespace(); String entryType = parseTextToken(); BibtexEntryType tp = BibtexEntryType.getType(entryType); boolean isEntry = (tp != null); // Util.pr(tp.getName()); if (!isEntry) { // The entry type name was not recognized. This can mean // that it is a string, preamble, or comment. If so, // parse and set accordingly. If not, assume it is an entry // with an unknown type. if (entryType.toLowerCase().equals("preamble")) { _db.setPreamble(parsePreamble()); } else if (entryType.toLowerCase().equals("string")) { BibtexString bs = parseString(); try { _db.addString(bs); } catch (KeyCollisionException ex) { _pr.addWarning(Globals.lang("Duplicate string name") + ": " + bs.getName()); // ex.printStackTrace(); } } else if (entryType.toLowerCase().equals("comment")) { StringBuffer commentBuf = parseBracketedTextExactly(); /** * Metadata are used to store Bibkeeper-specific information in .bib files. * * <p>Metadata are stored in bibtex files in the format * * @comment{jabref-meta: type:data0;data1;data2;...} * <p>Each comment that starts with the META_FLAG is stored in the meta HashMap, * with type as key. Unluckily, the old META_FLAG bibkeeper-meta: was used in JabRef * 1.0 and 1.1, so we need to support it as well. At least for a while. We'll always * save with the new one. */ String comment = commentBuf.toString().replaceAll("[\\x0d\\x0a]", ""); if (comment .substring(0, Math.min(comment.length(), GUIGlobals.META_FLAG.length())) .equals(GUIGlobals.META_FLAG) || comment .substring(0, Math.min(comment.length(), GUIGlobals.META_FLAG_OLD.length())) .equals(GUIGlobals.META_FLAG_OLD)) { String rest; if (comment.substring(0, GUIGlobals.META_FLAG.length()).equals(GUIGlobals.META_FLAG)) rest = comment.substring(GUIGlobals.META_FLAG.length()); else rest = comment.substring(GUIGlobals.META_FLAG_OLD.length()); int pos = rest.indexOf(':'); if (pos > 0) _meta.put(rest.substring(0, pos), rest.substring(pos + 1)); // We remove all line breaks in the metadata - these // will have been inserted // to prevent too long lines when the file was // saved, and are not part of the data. } /** * A custom entry type can also be stored in a * * @comment: */ if (comment .substring(0, Math.min(comment.length(), GUIGlobals.ENTRYTYPE_FLAG.length())) .equals(GUIGlobals.ENTRYTYPE_FLAG)) { CustomEntryType typ = CustomEntryType.parseEntryType(comment); entryTypes.put(typ.getName().toLowerCase(), typ); } } else { // The entry type was not recognized. This may mean that // it is a custom entry type whose definition will // appear // at the bottom of the file. So we use an // UnknownEntryType // to remember the type name by. tp = new UnknownEntryType(entryType.toLowerCase()); // System.out.println("unknown type: "+entryType); isEntry = true; } } if (isEntry) // True if not comment, preamble or string. { /** * Morten Alver 13 Aug 2006: Trying to make the parser more robust. If an exception is * thrown when parsing an entry, drop the entry and try to resume parsing. Add a warning * for the user. * * <p>An alternative solution is to try rescuing the entry for which parsing failed, by * returning the entry with the exception and adding it before parsing is continued. */ try { BibtexEntry be = parseEntry(tp); boolean duplicateKey = _db.insertEntry(be); if (duplicateKey) // JZTODO lyrics _pr.addWarning( Globals.lang("duplicate BibTeX key") + ": " + be.getCiteKey() + " (" + Globals.lang("grouping may not work for this entry") + ")"); else if (be.getCiteKey() == null || be.getCiteKey().equals("")) { _pr.addWarning( Globals.lang("empty BibTeX key") + ": " + be.getAuthorTitleYear(40) + " (" + Globals.lang("grouping may not work for this entry") + ")"); } } catch (IOException ex) { ex.printStackTrace(); _pr.addWarning( Globals.lang("Error occured when parsing entry") + ": '" + ex.getMessage() + "'. " + Globals.lang("Skipped entry.")); } } skipWhitespace(); } // Before returning the database, update entries with unknown type // based on parsed type definitions, if possible. checkEntryTypes(_pr); return _pr; } catch (KeyCollisionException kce) { // kce.printStackTrace(); throw new IOException("Duplicate ID in bibtex file: " + kce.toString()); } }