Esempio n. 1
0
  /**
   * 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());
 }