Logo Search packages:      
Sourcecode: jabref version File versions  Download package

KeywordGroup.java

/*
 All programs in this directory and subdirectories are published under the 
 GNU General Public License as described below.

 This program is free software; you can redistribute it and/or modify it 
 under the terms of the GNU General Public License as published by the Free 
 Software Foundation; either version 2 of the License, or (at your option) 
 any later version.

 This program is distributed in the hope that it will be useful, but WITHOUT 
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
 more details.

 You should have received a copy of the GNU General Public License along 
 with this program; if not, write to the Free Software Foundation, Inc., 59 
 Temple Place, Suite 330, Boston, MA 02111-1307 USA

 Further information about the GNU GPL is available at:
 http://www.gnu.org/copyleft/gpl.ja.html
 */

package net.sf.jabref.groups;

import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.undo.AbstractUndoableEdit;

import net.sf.jabref.*;
import net.sf.jabref.undo.NamedCompound;
import net.sf.jabref.undo.UndoableFieldChange;
import net.sf.jabref.util.QuotedStringTokenizer;

/**
 * @author jzieren
 */
00039 public class KeywordGroup extends AbstractGroup implements SearchRule {
      public static final String ID = "KeywordGroup:";
      private final String m_searchField;
      private final String m_searchExpression;
      private final boolean m_caseSensitive;
      private final boolean m_regExp;
      private Pattern m_pattern = null;

      /**
       * Creates a KeywordGroup with the specified properties.
       */
00050       public KeywordGroup(String name, String searchField,
                  String searchExpression, boolean caseSensitive, boolean regExp,
                  int context) throws IllegalArgumentException,
                  PatternSyntaxException {
            super(name, context);
            m_searchField = searchField;
            m_searchExpression = searchExpression;
            m_caseSensitive = caseSensitive;
            m_regExp = regExp;
            if (m_regExp)
                  compilePattern();
      }

      protected void compilePattern() throws IllegalArgumentException,
                  PatternSyntaxException {
            m_pattern = m_caseSensitive ? Pattern.compile("\\b"+m_searchExpression+"\\b")
                        : Pattern.compile("\\b"+m_searchExpression+"\\b", Pattern.CASE_INSENSITIVE);
      }

      /**
       * Parses s and recreates the KeywordGroup from it.
       * 
       * @param s
       *            The String representation obtained from
       *            KeywordGroup.toString()
       */
00076       public static AbstractGroup fromString(String s, BibtexDatabase db,
                  int version) throws Exception {
            if (!s.startsWith(ID))
                  throw new Exception(
                              "Internal error: KeywordGroup cannot be created from \""
                                          + s
                                          + "\". "
                                          + "Please report this on www.sf.net/projects/jabref");
            QuotedStringTokenizer tok = new QuotedStringTokenizer(s.substring(ID
                        .length()), SEPARATOR, QUOTE_CHAR);
            switch (version) {
            case 0: {
                  String name = tok.nextToken();
                  String field = tok.nextToken();
                  String expression = tok.nextToken();
                  // assume caseSensitive=false and regExp=true for old groups
                  return new KeywordGroup(Util.unquote(name, QUOTE_CHAR), Util
                              .unquote(field, QUOTE_CHAR), Util.unquote(expression,
                              QUOTE_CHAR), false, true, AbstractGroup.INDEPENDENT);
            }
            case 1:
            case 2: {
                  String name = tok.nextToken();
                  String field = tok.nextToken();
                  String expression = tok.nextToken();
                  boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1;
                  boolean regExp = Integer.parseInt(tok.nextToken()) == 1;
                  return new KeywordGroup(Util.unquote(name, QUOTE_CHAR), Util
                              .unquote(field, QUOTE_CHAR), Util.unquote(expression,
                              QUOTE_CHAR), caseSensitive, regExp,
                              AbstractGroup.INDEPENDENT);
            }
            case 3: {
                  String name = tok.nextToken();
                  int context = Integer.parseInt(tok.nextToken());
                  String field = tok.nextToken();
                  String expression = tok.nextToken();
                  boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1;
                  boolean regExp = Integer.parseInt(tok.nextToken()) == 1;
                  return new KeywordGroup(Util.unquote(name, QUOTE_CHAR), Util
                              .unquote(field, QUOTE_CHAR), Util.unquote(expression,
                              QUOTE_CHAR), caseSensitive, regExp, context);
            }
            default:
                  throw new UnsupportedVersionException("KeywordGroup", version);
            }
      }

      /**
       * @see net.sf.jabref.groups.AbstractGroup#getSearchRule()
       */
00127       public SearchRule getSearchRule() {
            return this;
      }

      /**
       * Returns a String representation of this object that can be used to
       * reconstruct it.
       */
00135       public String toString() {
            return ID + Util.quote(m_name, SEPARATOR, QUOTE_CHAR) + SEPARATOR
                        + m_context + SEPARATOR
                        + Util.quote(m_searchField, SEPARATOR, QUOTE_CHAR) + SEPARATOR
                        + Util.quote(m_searchExpression, SEPARATOR, QUOTE_CHAR)
                        + SEPARATOR + (m_caseSensitive ? "1" : "0") + SEPARATOR
                        + (m_regExp ? "1" : "0") + SEPARATOR;
      }

00144       public boolean supportsAdd() {
            return !m_regExp;
      }

00148       public boolean supportsRemove() {
            return !m_regExp;
      }

00152       public AbstractUndoableEdit add(BibtexEntry[] entries) {
            if (!supportsAdd())
                  return null;
            if ((entries != null) && (entries.length > 0)) {
                  NamedCompound ce = new NamedCompound(Globals
                              .lang("add entries to group"));
                  boolean modified = false;
                  for (int i = 0; i < entries.length; i++) {
                        if (applyRule(null, entries[i]) == 0) {
                              String oldContent = entries[i]
                                          .getField(m_searchField), 
                                          pre = Globals.prefs.get("groupKeywordSeparator");
                              String newContent = (oldContent == null ? "" : oldContent
                                          + pre)
                                          + m_searchExpression;
                              entries[i].setField(m_searchField, newContent);

                              // Store undo information.
                              ce.addEdit(new UndoableFieldChange(entries[i],
                                          m_searchField, oldContent, newContent));
                              modified = true;
                        }
                  }
                  if (modified)
                        ce.end();

                  return modified ? ce : null;
            }

            return null;
      }

00184       public AbstractUndoableEdit remove(BibtexEntry[] entries) {
            if (!supportsRemove())
                  return null;

            if ((entries != null) && (entries.length > 0)) {
                  NamedCompound ce = new NamedCompound(Globals
                              .lang("remove from group"));
                  boolean modified = false;
                  for (int i = 0; i < entries.length; ++i) {
                        if (applyRule(null, entries[i]) > 0) {
                              String oldContent = entries[i]
                                          .getField(m_searchField);
                              removeMatches(entries[i]);
                              // Store undo information.
                              ce.addEdit(new UndoableFieldChange(entries[i],
                                          m_searchField, oldContent, entries[i]
                                                      .getField(m_searchField)));
                              modified = true;
                        }
                  }
                  if (modified)
                        ce.end();

                  return modified ? ce : null;
            }

            return null;
      }

      public boolean equals(Object o) {
            if (!(o instanceof KeywordGroup))
                  return false;
            KeywordGroup other = (KeywordGroup) o;
            return m_name.equals(other.m_name)
                        && m_searchField.equals(other.m_searchField)
                        && m_searchExpression.equals(other.m_searchExpression)
                        && m_caseSensitive == other.m_caseSensitive
                        && m_regExp == other.m_regExp
                && getHierarchicalContext() == other.getHierarchicalContext();
      }

      /*
       * (non-Javadoc)
       * 
       * @see net.sf.jabref.groups.AbstractGroup#contains(java.util.Map,
       *      net.sf.jabref.BibtexEntry)
       */
00231       public boolean contains(Map<String, String> searchOptions, BibtexEntry entry) {
            return contains(entry);
      }

00235       public boolean contains(BibtexEntry entry) {
            String content = entry.getField(m_searchField);
            if (content == null)
                  return false;
            if (m_regExp)
                  return m_pattern.matcher(content).find();
            if (m_caseSensitive)
                  return containsWord(m_searchExpression, content);
            return containsWord(m_searchExpression.toLowerCase(), content.toLowerCase());
      }

    /**
     * Look for the given non-regexp string in another string, but check whether a
     * match concerns a complete word, not part of a word.
     * @param word The word to look for.
     * @param text The string to look in.
     * @return true if the word was found, false otherwise.
     */
00253     private static boolean containsWord(String word, String text) {
        int piv = 0;
        while (piv < text.length()) {
            int ind = text.indexOf(word, piv);
            if (ind < 0)
                return false;
            // Found a match. See if it is a complete word:
            if (((ind == 0) || !Character.isLetterOrDigit(text.charAt(ind-1))) &&
                ((ind+word.length() == text.length()) || !Character.isLetterOrDigit(text.charAt(ind+word.length())))) {
                return true;
            }
            else piv = ind+1;
        }
        return false;
    }

      /**
       * Removes matches of searchString in the entry's field. This is only
       * possible if the search expression is not a regExp.
       */
00273       private void removeMatches(BibtexEntry entry) {
            String content = entry.getField(m_searchField);
            if (content == null)
                  return; // nothing to modify
            StringBuffer sbOrig = new StringBuffer(content);
            StringBuffer sbLower = new StringBuffer(content.toLowerCase());
            StringBuffer haystack = m_caseSensitive ? sbOrig : sbLower;
            String needle = m_caseSensitive ? m_searchExpression
                        : m_searchExpression.toLowerCase();
            int i, j, k;
            final String separator = Globals.prefs.get("groupKeywordSeparator");
            while ((i = haystack.indexOf(needle)) >= 0) {
                  sbOrig.replace(i, i + needle.length(), "");
                  sbLower.replace(i, i + needle.length(), "");
                  // reduce spaces at i to 1
                  j = i;
                  k = i;
                  while (j - 1 >= 0 && separator.indexOf(haystack.charAt(j - 1)) >= 0)
                        --j;
                  while (k < haystack.length() && separator.indexOf(haystack.charAt(k)) >= 0)
                        ++k;
                  sbOrig.replace(j, k, j >= 0 && k < sbOrig.length() ? separator : "");
                  sbLower.replace(j, k, j >= 0 && k < sbOrig.length() ? separator : "");
            }

            String result = sbOrig.toString().trim();
            entry.setField(m_searchField, (result.length() > 0 ? result : null));
      }

      public int applyRule(Map<String, String> searchOptions, BibtexEntry entry) {
            return contains(searchOptions, entry) ? 1 : 0;
      }

00306       public AbstractGroup deepCopy() {
            try {
                  return new KeywordGroup(m_name, m_searchField, m_searchExpression,
                              m_caseSensitive, m_regExp, m_context);
            } catch (Throwable t) {
                  // this should never happen, because the constructor obviously
                  // succeeded in creating _this_ instance!
                  System.err.println("Internal error: Exception " + t
                              + " in KeywordGroup.deepCopy(). "
                              + "Please report this on www.sf.net/projects/jabref");
                  return null;
            }
      }

      public boolean isCaseSensitive() {
            return m_caseSensitive;
      }

      public boolean isRegExp() {
            return m_regExp;
      }

      public String getSearchExpression() {
            return m_searchExpression;
      }

      public String getSearchField() {
            return m_searchField;
      }

00336       public boolean isDynamic() {
            return true;
      }
      
00340       public String getDescription() {
            return getDescriptionForPreview(m_searchField, m_searchExpression, m_caseSensitive,
                        m_regExp); 
      }
      
      public static String getDescriptionForPreview(String field, String expr,
            boolean caseSensitive, boolean regExp) {
        StringBuffer sb = new StringBuffer();
        sb.append(regExp ? Globals.lang(
                "This group contains entries whose <b>%0</b> field contains the regular expression <b>%1</b>",
                field, Util.quoteForHTML(expr))
                : Globals.lang(
                        "This group contains entries whose <b>%0</b> field contains the keyword <b>%1</b>",
                        field, Util.quoteForHTML(expr)));
        sb.append(" (").append(caseSensitive ? Globals.lang("case sensitive")
                : Globals.lang("case insensitive")).append("). ");
        sb.append(regExp ? Globals.lang(
                "Entries cannot be manually assigned to or removed from this group.")
                : Globals.lang(
                        "Additionally, entries whose <b>%0</b> field does not contain "
                        + "<b>%1</b> can be assigned manually to this group by selecting them "
                        + "then using either drag and drop or the context menu. "
                        + "This process adds the term <b>%1</b> to "
                        + "each entry's <b>%0</b> field. "
                        + "Entries can be removed manually from this group by selecting them "
                        + "then using the context menu. "
                        + "This process removes the term <b>%1</b> from "
                        + "each entry's <b>%0</b> field.",
                        field, Util.quoteForHTML(expr)));
        return sb.toString();
    }

00372       public String getShortDescription() {
            StringBuffer sb = new StringBuffer();
            sb.append("<b>");
            if (Globals.prefs.getBoolean("groupShowDynamic"))
            sb.append("<i>").append(Util.quoteForHTML(getName())).append("</i>");
            else
                  sb.append(Util.quoteForHTML(getName()));
        sb.append(Globals.lang("</b> - dynamic group (<b>")).append(m_searchField).
            append(Globals.lang("</b> contains <b>")).
            append(Util.quoteForHTML(m_searchExpression)).append("</b>)");
            switch (getHierarchicalContext()) {
            case AbstractGroup.INCLUDING:
                  sb.append(Globals.lang(", includes subgroups"));
                  break;
            case AbstractGroup.REFINING:
                  sb.append(Globals.lang(", refines supergroup"));
                  break;
            default:
                  break;
            }
            return sb.toString();
      }

    public String getTypeId() {
        return ID;
    }
}

Generated by  Doxygen 1.6.0   Back to index