/** * 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()); } }
private void applyChanges() { valueChanged(new ListSelectionEvent(new JList(), 0, 0, false)); // Iterate over our map of required fields, and list those types if necessary: List<String> types = typeComp.getFields(); for (Map.Entry<String, List<String>> stringListEntry : reqLists.entrySet()) { if (!types.contains(stringListEntry.getKey())) { continue; } List<String> reqFields = stringListEntry.getValue(); List<String> optFields = optLists.get(stringListEntry.getKey()); List<String> opt2Fields = opt2Lists.get(stringListEntry.getKey()); String[] reqStr = new String[reqFields.size()]; reqStr = reqFields.toArray(reqStr); String[] optStr = new String[optFields.size()]; optStr = optFields.toArray(optStr); String[] opt2Str; if (opt2Fields != null) { opt2Str = opt2Fields.toArray(new String[opt2Fields.size()]); } else { opt2Str = new String[0]; } // If this type is already existing, check if any changes have // been made boolean changesMade = true; if (defaulted.contains(stringListEntry.getKey())) { // This type should be reverted to its default setup. // System.out.println("Defaulting: "+typeName); String nm = StringUtil.nCase(stringListEntry.getKey()); BibtexEntryType.removeType(nm); updateTypesForEntries(nm); continue; } BibtexEntryType oldType = BibtexEntryType.getType(stringListEntry.getKey()); if (oldType != null) { String[] oldReq = oldType.getRequiredFields(), oldOpt = oldType.getOptionalFields(); if (biblatexMode) { String[] priOpt = oldType.getPrimaryOptionalFields(); String[] secOpt = Util.getRemainder(oldOpt, priOpt); if (equalArrays(oldReq, reqStr) && equalArrays(oldOpt, optStr) && equalArrays(secOpt, opt2Str)) { changesMade = false; } } else if (equalArrays(oldReq, reqStr) && equalArrays(oldOpt, optStr)) { changesMade = false; } } if (changesMade) { // System.out.println("Updating: "+typeName); CustomEntryType typ = biblatexMode ? new CustomEntryType( StringUtil.nCase(stringListEntry.getKey()), reqStr, optStr, opt2Str) : new CustomEntryType(StringUtil.nCase(stringListEntry.getKey()), reqStr, optStr); BibtexEntryType.ALL_TYPES.put(stringListEntry.getKey().toLowerCase(), typ); updateTypesForEntries(typ.getName()); } } Set<Object> toRemove = new HashSet<Object>(); for (String o : BibtexEntryType.ALL_TYPES.keySet()) { if (!types.contains(o)) { toRemove.add(o); } } // Remove those that should be removed: if (!toRemove.isEmpty()) { for (Object aToRemove : toRemove) { typeDeletion((String) aToRemove); } } updateTables(); }
/** Stores all information about the entry type in preferences, with the tag given by number. */ public void storeCustomEntryType(CustomEntryType tp, int number) { String nr = "" + number; put(CUSTOM_TYPE_NAME + nr, tp.getName()); putStringArray(CUSTOM_TYPE_REQ + nr, tp.getRequiredFields()); putStringArray(CUSTOM_TYPE_OPT + nr, tp.getOptionalFields()); }