From 93ac8d5aa27f782a5ee0cf478a77a775d249300b Mon Sep 17 00:00:00 2001 From: Simon Harrer Date: Tue, 28 Jul 2015 09:05:19 +0200 Subject: [PATCH 1/4] BREAKING: Search groups now use same search logic as UI --> avoids confusion when converting a UI search into a search group. Behaviour before: contains/regex check of whole search string; behaviour after: contains/regex check of every word of the search string. The new search is more powerful and con simulate the old behaviour: enclose everything in double quotes, e.g., process language --> "process language". --- CHANGELOG | 1 + .../java/net/sf/jabref/SearchManager2.java | 6 +- .../net/sf/jabref/SearchManagerNoGUI.java | 3 - .../net/sf/jabref/groups/GroupDialog.java | 73 ++++----- .../jabref/groups/structure/SearchGroup.java | 111 ++++++-------- .../net/sf/jabref/search/SearchRules.java | 12 +- .../describer/BasicSearchDescriber.java | 34 ----- ...tainsAndRegexBasedSearchRuleDescriber.java | 52 +++++++ ...a => GrammarBasedSearchRuleDescriber.java} | 14 +- .../search/describer/SearchDescribers.java | 37 +++++ .../jabref/search/rules/BasicSearchRule.java | 141 ------------------ .../search/rules/ContainBasedSearchRule.java | 88 +++++++++++ ...ssion.java => GrammarBasedSearchRule.java} | 9 +- .../jabref/search/rules/InvertSearchRule.java | 2 +- .../jabref/search/rules/RegExpSearchRule.java | 101 ------------- ...rchRule.java => RegexBasedSearchRule.java} | 26 ++-- .../jabref/search/rules/SimpleSearchRule.java | 90 ----------- .../search/rules/util/SentenceAnalyzer.java | 57 +++++++ ...AndRegexBasedSearchRuleDescriberTest.java} | 16 +- ... GrammarBasedSearchRuleDescriberTest.java} | 12 +- ...t.java => ContainBasedSearchRuleTest.java} | 14 +- .../search/rules/SentenceAnalyzerTest.java | 9 +- 22 files changed, 375 insertions(+), 533 deletions(-) delete mode 100644 src/main/java/net/sf/jabref/search/describer/BasicSearchDescriber.java create mode 100644 src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java rename src/main/java/net/sf/jabref/search/describer/{SearchExpressionDescriber.java => GrammarBasedSearchRuleDescriber.java} (85%) create mode 100644 src/main/java/net/sf/jabref/search/describer/SearchDescribers.java delete mode 100644 src/main/java/net/sf/jabref/search/rules/BasicSearchRule.java create mode 100644 src/main/java/net/sf/jabref/search/rules/ContainBasedSearchRule.java rename src/main/java/net/sf/jabref/search/rules/{SearchExpression.java => GrammarBasedSearchRule.java} (95%) delete mode 100644 src/main/java/net/sf/jabref/search/rules/RegExpSearchRule.java rename src/main/java/net/sf/jabref/search/rules/{BasicRegexSearchRule.java => RegexBasedSearchRule.java} (77%) delete mode 100644 src/main/java/net/sf/jabref/search/rules/SimpleSearchRule.java create mode 100644 src/main/java/net/sf/jabref/search/rules/util/SentenceAnalyzer.java rename src/test/java/net/sf/jabref/search/{BasicSearchDescriberTest.java => ContainsAndRegexBasedSearchRuleDescriberTest.java} (67%) rename src/test/java/net/sf/jabref/search/{SearchExpressionDescriberTest.java => GrammarBasedSearchRuleDescriberTest.java} (84%) rename src/test/java/net/sf/jabref/search/rules/{BasicSearchRuleTest.java => ContainBasedSearchRuleTest.java} (78%) diff --git a/CHANGELOG b/CHANGELOG index 26728d49831..2ddfb8a3502 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ - Switch vom antlr2 grammar to antlr4 for capturing the search expressions. Should be backwards compatible. - Changed html description of search expressions, expressing the conditional logic more explicitly with brackets. - Fixed INSPIRE searches compatibility problem (by Stefano Gariazzo). + - BREAKING: Search groups now use same search logic as UI --> avoids confusion when converting a UI search into a search group. Behaviour before: contains/regex check of whole search string; behaviour after: contains/regex check of every word of the search string. The new search is more powerful and con simulate the old behaviour: enclose everything in double quotes, e.g., process language --> "process language". 2.11 beta 3 - New MacOSX integration - Two releases for MacOSX: OSX-Java6 for Apple Java 1.6 and OSX for Oracle Java 1.7+ diff --git a/src/main/java/net/sf/jabref/SearchManager2.java b/src/main/java/net/sf/jabref/SearchManager2.java index 4a5228dbed3..ce0c563c0ce 100644 --- a/src/main/java/net/sf/jabref/SearchManager2.java +++ b/src/main/java/net/sf/jabref/SearchManager2.java @@ -21,9 +21,7 @@ import net.sf.jabref.search.SearchRule; import net.sf.jabref.search.SearchRules; import net.sf.jabref.search.matchers.SearchMatcher; -import net.sf.jabref.search.rules.BasicRegexSearchRule; -import net.sf.jabref.search.rules.BasicSearchRule; -import net.sf.jabref.search.rules.SearchExpression; +import net.sf.jabref.search.rules.GrammarBasedSearchRule; import javax.swing.*; import javax.swing.event.CaretEvent; @@ -802,7 +800,7 @@ private void updateSearchButtonText() { } private boolean isSpecificSearch() { - return !increment.isSelected() && SearchExpression.isValid(caseSensitive.isSelected(), regExpSearch.isSelected(), searchField.getText()); + return !increment.isSelected() && GrammarBasedSearchRule.isValid(caseSensitive.isSelected(), regExpSearch.isSelected(), searchField.getText()); } @Override diff --git a/src/main/java/net/sf/jabref/SearchManagerNoGUI.java b/src/main/java/net/sf/jabref/SearchManagerNoGUI.java index e9854d43114..d2a1a76e5db 100644 --- a/src/main/java/net/sf/jabref/SearchManagerNoGUI.java +++ b/src/main/java/net/sf/jabref/SearchManagerNoGUI.java @@ -22,9 +22,6 @@ import net.sf.jabref.imports.*; import net.sf.jabref.search.SearchRules; -import net.sf.jabref.search.rules.BasicRegexSearchRule; -import net.sf.jabref.search.rules.BasicSearchRule; -import net.sf.jabref.search.rules.SearchExpression; import net.sf.jabref.search.SearchRule; /** diff --git a/src/main/java/net/sf/jabref/groups/GroupDialog.java b/src/main/java/net/sf/jabref/groups/GroupDialog.java index b203612c8f5..9e2dd1f836c 100644 --- a/src/main/java/net/sf/jabref/groups/GroupDialog.java +++ b/src/main/java/net/sf/jabref/groups/GroupDialog.java @@ -15,11 +15,22 @@ */ package net.sf.jabref.groups; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; +import com.jgoodies.forms.builder.ButtonBarBuilder; +import com.jgoodies.forms.builder.DefaultFormBuilder; +import com.jgoodies.forms.factories.Borders; +import com.jgoodies.forms.layout.FormLayout; +import net.sf.jabref.*; +import net.sf.jabref.groups.structure.*; +import net.sf.jabref.search.SearchRules; +import net.sf.jabref.search.describer.SearchDescribers; +import net.sf.jabref.util.StringUtil; +import net.sf.jabref.util.Util; + +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.undo.AbstractUndoableEdit; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -28,24 +39,6 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import javax.swing.*; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.undo.AbstractUndoableEdit; - -import com.jgoodies.forms.factories.Borders; -import net.sf.jabref.*; -import net.sf.jabref.groups.structure.*; -import net.sf.jabref.search.describer.BasicSearchDescriber; -import net.sf.jabref.search.describer.SearchExpressionDescriber; -import net.sf.jabref.search.rules.SearchExpression; -import net.sf.jabref.util.StringUtil; -import net.sf.jabref.util.Util; - -import com.jgoodies.forms.builder.ButtonBarBuilder; -import com.jgoodies.forms.builder.DefaultFormBuilder; -import com.jgoodies.forms.layout.FormLayout; - /** * Dialog for creating or modifying groups. Operates directly on the Vector * containing group information. @@ -65,11 +58,11 @@ class GroupDialog extends JDialog { private final JRadioButton m_searchRadioButton = new JRadioButton( Globals.lang("Dynamically group entries by a free-form search expression")); private final JRadioButton m_independentButton = new JRadioButton( // JZTODO lyrics - Globals.lang("Independent group: When selected, view only this group's entries")); + Globals.lang("Independent group: When selected, view only this group's entries")); private final JRadioButton m_intersectionButton = new JRadioButton( // JZTODO lyrics - Globals.lang("Refine supergroup: When selected, view entries contained in both this group and its supergroup")); + Globals.lang("Refine supergroup: When selected, view entries contained in both this group and its supergroup")); private final JRadioButton m_unionButton = new JRadioButton( // JZTODO lyrics - Globals.lang("Include subgroups: When selected, view entries contained in this group or its subgroups")); + Globals.lang("Include subgroups: When selected, view entries contained in this group or its subgroups")); // for KeywordGroup private final JTextField m_kgSearchField = new JTextField(GroupDialog.TEXTFIELD_LENGTH); private final FieldTextField m_kgSearchTerm = new FieldTextField("keywords", "", @@ -111,17 +104,13 @@ public Dimension getPreferredSize() { private final CardLayout m_optionsLayout = new CardLayout(); - /** * Shows a group add/edit dialog. * - * @param jabrefFrame - * The parent frame. - * @param basePanel - * The default grouping field. - * @param editedGroup - * The group being edited, or null if a new group is to be - * created. + * @param jabrefFrame The parent frame. + * @param basePanel The default grouping field. + * @param editedGroup The group being edited, or null if a new group is to be + * created. */ public GroupDialog(JabRefFrame jabrefFrame, BasePanel basePanel, AbstractGroup editedGroup) { @@ -308,9 +297,9 @@ public void actionPerformed(ActionEvent e) { // therefore I don't catch anything here m_resultingGroup = new KeywordGroup( m_name.getText().trim(), m_kgSearchField.getText() - .trim(), m_kgSearchTerm.getText().trim(), + .trim(), m_kgSearchTerm.getText().trim(), m_kgCaseSensitive.isSelected(), m_kgRegExp - .isSelected(), getContext()); + .isSelected(), getContext()); if (((m_editedGroup instanceof ExplicitGroup) || (m_editedGroup instanceof SearchGroup)) && m_resultingGroup.supportsAdd()) { addPreviousEntries(); @@ -448,12 +437,8 @@ private void updateComponents() { + "To search the field Author for Smith and the field Title for electrical, enter%c

" + "author%esmith and title%eelectrical")); } else { - SearchExpression expression = new SearchExpression(isCaseSensitive(), isRegex()); - if(expression.validateSearchStrings(s1)) { - setDescription(new SearchExpressionDescriber(isCaseSensitive(), isRegex(), expression.getTree()).getDescription()); - } else { - setDescription(new BasicSearchDescriber(isCaseSensitive(), isRegex(), s1).getDescription()); - } + setDescription(SearchDescribers.getSearchDescriberFor(SearchRules.getSearchRuleByQuery(s1, isCaseSensitive(), isRegex()), s1).getDescription()); + if (isRegex()) { try { Pattern.compile(s1); @@ -553,7 +538,9 @@ public AbstractUndoableEdit getUndoForAddPreviousEntries() { return m_undoAddPreviousEntires; } - /** Sets the font of the name entry field. */ + /** + * Sets the font of the name entry field. + */ private void setNameFontItalic(boolean italic) { Font f = m_name.getFont(); if (f.isItalic() != italic) { diff --git a/src/main/java/net/sf/jabref/groups/structure/SearchGroup.java b/src/main/java/net/sf/jabref/groups/structure/SearchGroup.java index ab3dfe3b3e8..b8ef5bca9aa 100644 --- a/src/main/java/net/sf/jabref/groups/structure/SearchGroup.java +++ b/src/main/java/net/sf/jabref/groups/structure/SearchGroup.java @@ -15,18 +15,18 @@ */ package net.sf.jabref.groups.structure; -import javax.swing.undo.AbstractUndoableEdit; - -import net.sf.jabref.*; -import net.sf.jabref.search.describer.BasicSearchDescriber; -import net.sf.jabref.search.describer.SearchExpressionDescriber; -import net.sf.jabref.search.rules.RegExpSearchRule; +import net.sf.jabref.BibtexDatabase; +import net.sf.jabref.BibtexEntry; +import net.sf.jabref.Globals; +import net.sf.jabref.JabRefPreferences; import net.sf.jabref.search.SearchRule; -import net.sf.jabref.search.rules.SearchExpression; -import net.sf.jabref.search.rules.SimpleSearchRule; +import net.sf.jabref.search.SearchRules; +import net.sf.jabref.search.describer.SearchDescribers; import net.sf.jabref.util.QuotedStringTokenizer; import net.sf.jabref.util.StringUtil; +import javax.swing.undo.AbstractUndoableEdit; + /** * Internally, it consists of a search pattern. * @@ -40,14 +40,7 @@ public class SearchGroup extends AbstractGroup { private final boolean caseSensitive; private final boolean regExp; - /** - * If searchExpression is in valid syntax for advanced search, this - * will do the search; otherwise, either RegExpSearchRule or - * SimpleSearchRule will be used. - */ private final SearchRule searchRule; - private final SearchExpression expressionSearchRule; - /** * Creates a SearchGroup with the specified properties. @@ -58,15 +51,7 @@ public SearchGroup(String name, String searchExpression, boolean caseSensitive, this.caseSensitive = caseSensitive; this.regExp = regExp; - // TODO why use other search rules instead of "normal" search in JabRef. This WILL cause confusion! - expressionSearchRule = new SearchExpression(caseSensitive, regExp); - if (expressionSearchRule.validateSearchStrings(this.searchExpression)) { - searchRule = expressionSearchRule; // do advanced search - } else if (this.regExp) { - searchRule = new RegExpSearchRule(this.caseSensitive); - } else { - searchRule = new SimpleSearchRule(this.caseSensitive); - } + this.searchRule = SearchRules.getSearchRuleByQuery(searchExpression, caseSensitive, regExp); } /** @@ -76,7 +61,7 @@ public SearchGroup(String name, String searchExpression, boolean caseSensitive, * SearchGroup.toString(), or null if incompatible */ public static AbstractGroup fromString(String s, BibtexDatabase db, - int version) throws Exception { + int version) throws Exception { if (!s.startsWith(SearchGroup.ID)) { throw new Exception( "Internal error: SearchGroup cannot be created from \"" + s @@ -86,33 +71,33 @@ public static AbstractGroup fromString(String s, BibtexDatabase db, QuotedStringTokenizer tok = new QuotedStringTokenizer(s.substring(SearchGroup.ID .length()), AbstractGroup.SEPARATOR, AbstractGroup.QUOTE_CHAR); switch (version) { - case 0: - case 1: - case 2: { - String name = tok.nextToken(); - String expression = tok.nextToken(); - boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1; - boolean regExp = Integer.parseInt(tok.nextToken()) == 1; - // version 0 contained 4 additional booleans to specify search - // fields; these are ignored now, all fields are always searched - return new SearchGroup(StringUtil.unquote(name, AbstractGroup.QUOTE_CHAR), StringUtil - .unquote(expression, AbstractGroup.QUOTE_CHAR), caseSensitive, regExp, - GroupHierarchyType.INDEPENDENT); - } - case 3: { - String name = tok.nextToken(); - int context = Integer.parseInt(tok.nextToken()); - String expression = tok.nextToken(); - boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1; - boolean regExp = Integer.parseInt(tok.nextToken()) == 1; - // version 0 contained 4 additional booleans to specify search - // fields; these are ignored now, all fields are always searched - return new SearchGroup(StringUtil.unquote(name, AbstractGroup.QUOTE_CHAR), StringUtil - .unquote(expression, AbstractGroup.QUOTE_CHAR), caseSensitive, regExp, - GroupHierarchyType.getByNumber(context)); - } - default: - throw new UnsupportedVersionException("SearchGroup", version); + case 0: + case 1: + case 2: { + String name = tok.nextToken(); + String expression = tok.nextToken(); + boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1; + boolean regExp = Integer.parseInt(tok.nextToken()) == 1; + // version 0 contained 4 additional booleans to specify search + // fields; these are ignored now, all fields are always searched + return new SearchGroup(StringUtil.unquote(name, AbstractGroup.QUOTE_CHAR), StringUtil + .unquote(expression, AbstractGroup.QUOTE_CHAR), caseSensitive, regExp, + GroupHierarchyType.INDEPENDENT); + } + case 3: { + String name = tok.nextToken(); + int context = Integer.parseInt(tok.nextToken()); + String expression = tok.nextToken(); + boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1; + boolean regExp = Integer.parseInt(tok.nextToken()) == 1; + // version 0 contained 4 additional booleans to specify search + // fields; these are ignored now, all fields are always searched + return new SearchGroup(StringUtil.unquote(name, AbstractGroup.QUOTE_CHAR), StringUtil + .unquote(expression, AbstractGroup.QUOTE_CHAR), caseSensitive, regExp, + GroupHierarchyType.getByNumber(context)); + } + default: + throw new UnsupportedVersionException("SearchGroup", version); } } @@ -227,11 +212,7 @@ public boolean isDynamic() { @Override public String getDescription() { - if(expressionSearchRule.getTree() != null) { - return new SearchExpressionDescriber(caseSensitive, regExp, expressionSearchRule.getTree()).getDescription(); - } else { - return new BasicSearchDescriber(caseSensitive, regExp, searchExpression).getDescription(); - } + return SearchDescribers.getSearchDescriberFor(searchRule, searchExpression).getDescription(); } @Override @@ -250,14 +231,14 @@ public String getShortDescription() { sb.append(" "). append(StringUtil.quoteForHTML(searchExpression)).append(")"); switch (getHierarchicalContext()) { - case INCLUDING: - sb.append(", ").append(Globals.lang("includes subgroups")); - break; - case REFINING: - sb.append(", ").append(Globals.lang("refines supergroup")); - break; - default: - break; + case INCLUDING: + sb.append(", ").append(Globals.lang("includes subgroups")); + break; + case REFINING: + sb.append(", ").append(Globals.lang("refines supergroup")); + break; + default: + break; } return sb.toString(); } diff --git a/src/main/java/net/sf/jabref/search/SearchRules.java b/src/main/java/net/sf/jabref/search/SearchRules.java index 065852c521d..f3dc933e017 100644 --- a/src/main/java/net/sf/jabref/search/SearchRules.java +++ b/src/main/java/net/sf/jabref/search/SearchRules.java @@ -1,8 +1,8 @@ package net.sf.jabref.search; -import net.sf.jabref.search.rules.BasicRegexSearchRule; -import net.sf.jabref.search.rules.BasicSearchRule; -import net.sf.jabref.search.rules.SearchExpression; +import net.sf.jabref.search.rules.RegexBasedSearchRule; +import net.sf.jabref.search.rules.ContainBasedSearchRule; +import net.sf.jabref.search.rules.GrammarBasedSearchRule; public class SearchRules { @@ -17,7 +17,7 @@ public class SearchRules { public static SearchRule getSearchRuleByQuery(String query, boolean caseSensitive, boolean regex) { // this searches specified fields if specified, // and all fields otherwise - SearchRule searchExpression = new SearchExpression(caseSensitive, regex); + SearchRule searchExpression = new GrammarBasedSearchRule(caseSensitive, regex); if (searchExpression.validateSearchStrings(query)) { return searchExpression; } else { @@ -27,9 +27,9 @@ public static SearchRule getSearchRuleByQuery(String query, boolean caseSensitiv private static SearchRule getSearchRule(boolean caseSensitive, boolean regex) { if (regex) { - return new BasicRegexSearchRule(caseSensitive); + return new RegexBasedSearchRule(caseSensitive); } else { - return new BasicSearchRule(caseSensitive); + return new ContainBasedSearchRule(caseSensitive); } } diff --git a/src/main/java/net/sf/jabref/search/describer/BasicSearchDescriber.java b/src/main/java/net/sf/jabref/search/describer/BasicSearchDescriber.java deleted file mode 100644 index 598d5ddb0e3..00000000000 --- a/src/main/java/net/sf/jabref/search/describer/BasicSearchDescriber.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.sf.jabref.search.describer; - -import net.sf.jabref.Globals; -import net.sf.jabref.util.StringUtil; - -public class BasicSearchDescriber implements SearchDescriber { - - private final boolean regExp; - private final boolean caseSensitive; - private final String query; - - public BasicSearchDescriber(boolean caseSensitive, boolean regExp, String query) { - this.caseSensitive = caseSensitive; - this.regExp = regExp; - this.query = query; - } - - @Override - public String getDescription() { - StringBuilder sb = new StringBuilder(); - sb.append(regExp ? Globals.lang( - "This group contains entries in which any field contains the regular expression %0", - StringUtil.quoteForHTML(query)) - : Globals.lang("This group contains entries in which any field contains the term %0", - StringUtil.quoteForHTML(query))); - sb.append(" (").append(caseSensitive ? Globals.lang("case sensitive") - : Globals.lang("case insensitive")).append("). "); - sb.append(Globals.lang( - "Entries cannot be manually assigned to or removed from this group.")); - sb.append("


").append(Globals.lang( - "Hint%c To search specific fields only, enter for example%c

author%esmith and title%eelectrical")); - return sb.toString(); - } -} diff --git a/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java b/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java new file mode 100644 index 00000000000..5e9123a0c09 --- /dev/null +++ b/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java @@ -0,0 +1,52 @@ +package net.sf.jabref.search.describer; + +import net.sf.jabref.Globals; +import net.sf.jabref.search.rules.util.SentenceAnalyzer; +import net.sf.jabref.util.StringUtil; + +import java.util.LinkedList; +import java.util.List; + +public class ContainsAndRegexBasedSearchRuleDescriber implements SearchDescriber { + + private final boolean regExp; + private final boolean caseSensitive; + private final String query; + + public ContainsAndRegexBasedSearchRuleDescriber(boolean caseSensitive, boolean regExp, String query) { + this.caseSensitive = caseSensitive; + this.regExp = regExp; + this.query = query; + } + + @Override + public String getDescription() { + List words = new SentenceAnalyzer(query).getWords(); + String firstWord = words.size() > 0 ? words.get(0) : ""; + + System.out.println("words = " + words); + + String searchDescription = regExp ? Globals.lang( + "This group contains entries in which any field contains the regular expression %0", + StringUtil.quoteForHTML(firstWord)) + : Globals.lang("This group contains entries in which any field contains the term %0", + StringUtil.quoteForHTML(firstWord)); + + if(words.size() > 1) { + List unprocessedWords = words.subList(1, words.size()); + List unprocessedWordsInHtmlFormat = new LinkedList(); + for(String word : unprocessedWords) { + unprocessedWordsInHtmlFormat.add(String.format("%s", StringUtil.quoteForHTML(word))); + } + String andSeparator = String.join(" %s ", Globals.lang("and")); + String[] unprocessedWordsInHtmlFormatArray = unprocessedWordsInHtmlFormat.toArray(new String[unprocessedWordsInHtmlFormat.size()]); + searchDescription += StringUtil.join(unprocessedWordsInHtmlFormatArray, andSeparator); + } + + String caseSensitiveDescription = caseSensitive ? Globals.lang("case sensitive") : Globals.lang("case insensitive"); + String genericDescription = Globals.lang( + "Entries cannot be manually assigned to or removed from this group.") + "


" + Globals.lang( + "Hint%c To search specific fields only, enter for example%c

author%esmith and title%eelectrical"); + return String.format("%s (%s). %s", searchDescription, caseSensitiveDescription, genericDescription); + } +} diff --git a/src/main/java/net/sf/jabref/search/describer/SearchExpressionDescriber.java b/src/main/java/net/sf/jabref/search/describer/GrammarBasedSearchRuleDescriber.java similarity index 85% rename from src/main/java/net/sf/jabref/search/describer/SearchExpressionDescriber.java rename to src/main/java/net/sf/jabref/search/describer/GrammarBasedSearchRuleDescriber.java index 00355c71988..542a0dc1071 100644 --- a/src/main/java/net/sf/jabref/search/describer/SearchExpressionDescriber.java +++ b/src/main/java/net/sf/jabref/search/describer/GrammarBasedSearchRuleDescriber.java @@ -4,19 +4,19 @@ import net.sf.jabref.Globals; import net.sf.jabref.search.SearchBaseVisitor; import net.sf.jabref.search.SearchParser; -import net.sf.jabref.search.rules.SearchExpression; +import net.sf.jabref.search.rules.GrammarBasedSearchRule; import net.sf.jabref.util.StringUtil; import org.antlr.v4.runtime.tree.ParseTree; import java.util.regex.Pattern; -public class SearchExpressionDescriber implements SearchDescriber { +public class GrammarBasedSearchRuleDescriber implements SearchDescriber { private final boolean caseSensitive; private final boolean regExp; private final ParseTree parseTree; - public SearchExpressionDescriber(boolean caseSensitive, boolean regExp, ParseTree parseTree) { + public GrammarBasedSearchRuleDescriber(boolean caseSensitive, boolean regExp, ParseTree parseTree) { this.caseSensitive = caseSensitive; this.regExp = regExp; this.parseTree = Preconditions.checkNotNull(parseTree); @@ -58,7 +58,7 @@ public String visitComparison(SearchParser.ComparisonContext ctx) { final String field = StringUtil.unquote(ctx.left.getText(), '"'); final String value = StringUtil.unquote(ctx.right.getText(), '"'); - final SearchExpression.ComparisonOperator operator = SearchExpression.ComparisonOperator.build(ctx.operator.getText()); + final GrammarBasedSearchRule.ComparisonOperator operator = GrammarBasedSearchRule.ComparisonOperator.build(ctx.operator.getText()); final boolean regExpFieldSpec = !Pattern.matches("\\w+", field); final String termQuoted = StringUtil.quoteForHTML(value); @@ -67,13 +67,13 @@ public String visitComparison(SearchParser.ComparisonContext ctx) { StringUtil.quoteForHTML(field)) : Globals.lang("the field %0", StringUtil.quoteForHTML(field)); - if (operator == SearchExpression.ComparisonOperator.CONTAINS) { + if (operator == GrammarBasedSearchRule.ComparisonOperator.CONTAINS) { if (regExp) { return Globals.lang( "%0 contains the Regular Expression %1", fieldSpecQuoted, termQuoted); } return Globals.lang("%0 contains the term %1", fieldSpecQuoted, termQuoted); - } else if (operator == SearchExpression.ComparisonOperator.EXACT) { + } else if (operator == GrammarBasedSearchRule.ComparisonOperator.EXACT) { if (regExp) { return Globals.lang("%0 matches the Regular Expression %1", fieldSpecQuoted, termQuoted); @@ -81,7 +81,7 @@ public String visitComparison(SearchParser.ComparisonContext ctx) { return Globals.lang("%0 matches the term %1", fieldSpecQuoted, termQuoted); - } else if (operator == SearchExpression.ComparisonOperator.DOES_NOT_CONTAIN) { + } else if (operator == GrammarBasedSearchRule.ComparisonOperator.DOES_NOT_CONTAIN) { if (regExp) { return Globals.lang("%0 doesn't contain the Regular Expression %1", fieldSpecQuoted, termQuoted); diff --git a/src/main/java/net/sf/jabref/search/describer/SearchDescribers.java b/src/main/java/net/sf/jabref/search/describer/SearchDescribers.java new file mode 100644 index 00000000000..9d88640b45b --- /dev/null +++ b/src/main/java/net/sf/jabref/search/describer/SearchDescribers.java @@ -0,0 +1,37 @@ +package net.sf.jabref.search.describer; + +import net.sf.jabref.search.SearchRule; +import net.sf.jabref.search.rules.ContainBasedSearchRule; +import net.sf.jabref.search.rules.GrammarBasedSearchRule; +import net.sf.jabref.search.rules.RegexBasedSearchRule; + +public class SearchDescribers { + + /** + * Get the search describer for a given search rule and a given search query. + * + * @param searchRule the rule that encodes the search logic + * @param query the search query + * @return the search describer to turn the search into something human understandable + */ + public static SearchDescriber getSearchDescriberFor(SearchRule searchRule, String query) { + if (searchRule instanceof GrammarBasedSearchRule) { + GrammarBasedSearchRule grammarBasedSearchRule = (GrammarBasedSearchRule) searchRule; + + return new GrammarBasedSearchRuleDescriber(grammarBasedSearchRule.isCaseSensitiveSearch(), + grammarBasedSearchRule.isRegExpSearch(), grammarBasedSearchRule.getTree()); + } else if (searchRule instanceof ContainBasedSearchRule) { + ContainBasedSearchRule containBasedSearchRule = (ContainBasedSearchRule) searchRule; + + return new ContainsAndRegexBasedSearchRuleDescriber(containBasedSearchRule.isCaseSensitive(), false, query); + } else if (searchRule instanceof RegexBasedSearchRule) { + RegexBasedSearchRule regexBasedSearchRule = (RegexBasedSearchRule) searchRule; + + return new ContainsAndRegexBasedSearchRuleDescriber(regexBasedSearchRule.isCaseSensitive(), true, query); + } else { + throw new IllegalStateException("Cannot find a describer for searchRule " + + searchRule + " and query " + query); + } + } + +} diff --git a/src/main/java/net/sf/jabref/search/rules/BasicSearchRule.java b/src/main/java/net/sf/jabref/search/rules/BasicSearchRule.java deleted file mode 100644 index 5a60488cd80..00000000000 --- a/src/main/java/net/sf/jabref/search/rules/BasicSearchRule.java +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (C) 2003-2011 JabRef contributors. - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ -package net.sf.jabref.search.rules; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.jabref.BibtexEntry; -import net.sf.jabref.export.layout.format.RemoveLatexCommands; -import net.sf.jabref.search.SearchRule; - -/** - * Search rule for simple search. - */ -public class BasicSearchRule implements SearchRule { - - private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); - - protected final boolean caseSensitive; - - public BasicSearchRule(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - @Override - public boolean validateSearchStrings(String query) { - return true; - } - - @Override - public boolean applyRule(String query, BibtexEntry bibtexEntry) { - - String searchString = query; - if (!caseSensitive) { - searchString = searchString.toLowerCase(); - } - - List words = parseQuery(searchString); - - //print(words); - // We need match for all words: - boolean[] matchFound = new boolean[words.size()]; - - //TODO build upon already existing SimpleSearchRule - for (String field : bibtexEntry.getAllFields()) { - Object fieldContentAsObject = bibtexEntry.getField(field); - if (fieldContentAsObject != null) { - String fieldContent = BasicSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContentAsObject.toString()); - if (!caseSensitive) { - fieldContent = fieldContent.toLowerCase(); - } - - int index = 0; - // Check if we have a match for each of the query words, ignoring - // those words for which we already have a match: - for (String s : words) { - matchFound[index] = matchFound[index] || fieldContent.contains(s); - - index++; - } - } - - } - for (boolean aMatchFound : matchFound) { - if (!aMatchFound) { - return false; // Didn't match all words. - } - } - return true; // Matched all words. - } - - public static List parseQuery(String query) { - return new SentenceAnalyzer(query).getWords(); - } - - public static class SentenceAnalyzer { - - public static final char ESCAPE_CHAR = '\\'; - public static final char QUOTE_CHAR = '"'; - - private final String query; - - public SentenceAnalyzer(String query) { - this.query = query; - } - - public List getWords() { - List result = new ArrayList(); - - StringBuffer sb = new StringBuffer(); - boolean escaped = false; - boolean quoted = false; - for(char c : query.toCharArray()) { - // Check if we are entering an escape sequence: - if (!escaped && (c == ESCAPE_CHAR)) { - escaped = true; - } else { - // See if we have reached the end of a word: - if (!escaped && !quoted && Character.isWhitespace(c)) { - if (sb.length() > 0) { - result.add(sb.toString()); - sb = new StringBuffer(); - } - } else if (c == QUOTE_CHAR) { - // Whether it is a start or end quote, store the current - // word if any: - if (sb.length() > 0) { - result.add(sb.toString()); - sb = new StringBuffer(); - } - quoted = !quoted; - } else { - // All other possibilities exhausted, we add the char to - // the current word: - sb.append(c); - } - escaped = false; - } - } - // Finished with the loop. If we have a current word, add it: - if (sb.length() > 0) { - result.add(sb.toString()); - } - - return result; - } - } -} diff --git a/src/main/java/net/sf/jabref/search/rules/ContainBasedSearchRule.java b/src/main/java/net/sf/jabref/search/rules/ContainBasedSearchRule.java new file mode 100644 index 00000000000..476573c0c60 --- /dev/null +++ b/src/main/java/net/sf/jabref/search/rules/ContainBasedSearchRule.java @@ -0,0 +1,88 @@ +/* Copyright (C) 2003-2011 JabRef contributors. + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.search.rules; + +import java.util.List; + +import net.sf.jabref.BibtexEntry; +import net.sf.jabref.export.layout.format.RemoveLatexCommands; +import net.sf.jabref.search.SearchRule; +import net.sf.jabref.search.rules.util.SentenceAnalyzer; + +/** + * Search rule for contain-based search. + */ +public class ContainBasedSearchRule implements SearchRule { + + private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); + + private final boolean caseSensitive; + + public ContainBasedSearchRule(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public boolean isCaseSensitive() { + return caseSensitive; + } + + @Override + public boolean validateSearchStrings(String query) { + return true; + } + + @Override + public boolean applyRule(String query, BibtexEntry bibtexEntry) { + + String searchString = query; + if (!caseSensitive) { + searchString = searchString.toLowerCase(); + } + + List words = new SentenceAnalyzer(searchString).getWords(); + + //print(words); + // We need match for all words: + boolean[] matchFound = new boolean[words.size()]; + + for (String field : bibtexEntry.getAllFields()) { + Object fieldContentAsObject = bibtexEntry.getField(field); + if (fieldContentAsObject != null) { + String fieldContent = ContainBasedSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContentAsObject.toString()); + if (!caseSensitive) { + fieldContent = fieldContent.toLowerCase(); + } + + int index = 0; + // Check if we have a match for each of the query words, ignoring + // those words for which we already have a match: + for (String s : words) { + matchFound[index] = matchFound[index] || fieldContent.contains(s); + + index++; + } + } + + } + for (boolean aMatchFound : matchFound) { + if (!aMatchFound) { + return false; // Didn't match all words. + } + } + return true; // Matched all words. + } + +} diff --git a/src/main/java/net/sf/jabref/search/rules/SearchExpression.java b/src/main/java/net/sf/jabref/search/rules/GrammarBasedSearchRule.java similarity index 95% rename from src/main/java/net/sf/jabref/search/rules/SearchExpression.java rename to src/main/java/net/sf/jabref/search/rules/GrammarBasedSearchRule.java index acfc1daea5a..bb6d5013474 100644 --- a/src/main/java/net/sf/jabref/search/rules/SearchExpression.java +++ b/src/main/java/net/sf/jabref/search/rules/GrammarBasedSearchRule.java @@ -27,7 +27,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SearchExpression implements SearchRule { +/** + * The search query must be specified in an expression that is acceptable by the Search.g4 grammar. + */ +public class GrammarBasedSearchRule implements SearchRule { static public class ThrowingErrorListener extends BaseErrorListener { @@ -47,13 +50,13 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, private ParseTree tree; private String query; - public SearchExpression(boolean caseSensitiveSearch, boolean regExpSearch) throws RecognitionException { + public GrammarBasedSearchRule(boolean caseSensitiveSearch, boolean regExpSearch) throws RecognitionException { this.caseSensitiveSearch = caseSensitiveSearch; this.regExpSearch = regExpSearch; } public static boolean isValid(boolean caseSensitive, boolean regExp, String query) { - return new SearchExpression(caseSensitive, regExp).validateSearchStrings(query); + return new GrammarBasedSearchRule(caseSensitive, regExp).validateSearchStrings(query); } public boolean isCaseSensitiveSearch() { diff --git a/src/main/java/net/sf/jabref/search/rules/InvertSearchRule.java b/src/main/java/net/sf/jabref/search/rules/InvertSearchRule.java index 1509e7029f1..849f06de7b4 100644 --- a/src/main/java/net/sf/jabref/search/rules/InvertSearchRule.java +++ b/src/main/java/net/sf/jabref/search/rules/InvertSearchRule.java @@ -5,7 +5,7 @@ import net.sf.jabref.search.SearchRule; /** - * Inverts result score. + * Inverts the search result. * * Example: * false --> true diff --git a/src/main/java/net/sf/jabref/search/rules/RegExpSearchRule.java b/src/main/java/net/sf/jabref/search/rules/RegExpSearchRule.java deleted file mode 100644 index 9305e9fd88b..00000000000 --- a/src/main/java/net/sf/jabref/search/rules/RegExpSearchRule.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - Copyright (C) 2003 Nathan Dunn, Morten O. Alver - - 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.search.rules; - -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import net.sf.jabref.BibtexEntry; -import net.sf.jabref.export.layout.format.RemoveLatexCommands; -import net.sf.jabref.search.SearchRule; - -public class RegExpSearchRule implements SearchRule { - - private static final RemoveLatexCommands removeBrackets = new RemoveLatexCommands(); - - private final boolean caseSensitive; - - public RegExpSearchRule(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - @Override - public boolean validateSearchStrings(String query) { - try { - compileSearchString(query); - } catch (PatternSyntaxException e) { - return false; - } - return true; - } - - private Pattern compileSearchString(String searchString) { - int flags = 0; - if (!caseSensitive) { - flags = Pattern.CASE_INSENSITIVE; // testing - } - - return Pattern.compile(searchString, flags); - } - - @Override - public boolean applyRule(String query, BibtexEntry bibtexEntry) throws PatternSyntaxException { - - int score = 0; - - Pattern pattern = compileSearchString(query); - - score += searchFields(bibtexEntry.getAllFields(), bibtexEntry, pattern); - - return score > 0; - } - - private int searchFields(Set fields, BibtexEntry bibtexEntry, Pattern pattern) { - if (fields == null) { - return 0; - } - - int score = 0; - for (String field : fields) { - try { - Object value = bibtexEntry.getField(field); - if (value != null) { - Matcher m = pattern.matcher(RegExpSearchRule.removeBrackets.format((String) value)); - if (m.find()) { - score++; - } - } - } catch (Throwable t) { - System.err.println("Searching error: " + t); - } - } - return score; - } - -} diff --git a/src/main/java/net/sf/jabref/search/rules/BasicRegexSearchRule.java b/src/main/java/net/sf/jabref/search/rules/RegexBasedSearchRule.java similarity index 77% rename from src/main/java/net/sf/jabref/search/rules/BasicRegexSearchRule.java rename to src/main/java/net/sf/jabref/search/rules/RegexBasedSearchRule.java index 242c0f7f1c1..26f275ac414 100644 --- a/src/main/java/net/sf/jabref/search/rules/BasicRegexSearchRule.java +++ b/src/main/java/net/sf/jabref/search/rules/RegexBasedSearchRule.java @@ -17,6 +17,8 @@ import net.sf.jabref.BibtexEntry; import net.sf.jabref.export.layout.format.RemoveLatexCommands; +import net.sf.jabref.search.SearchRule; +import net.sf.jabref.search.rules.util.SentenceAnalyzer; import java.util.ArrayList; import java.util.List; @@ -25,14 +27,20 @@ import java.util.regex.PatternSyntaxException; /** - * Search rule for simple search. + * Search rule for regex-based search. */ -public class BasicRegexSearchRule extends BasicSearchRule { +public class RegexBasedSearchRule implements SearchRule { - private static final RemoveLatexCommands removeBrackets = new RemoveLatexCommands(); + private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); - public BasicRegexSearchRule(boolean caseSensitive) { - super(caseSensitive); + private final boolean caseSensitive; + + public RegexBasedSearchRule(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public boolean isCaseSensitive() { + return caseSensitive; } @Override @@ -41,7 +49,7 @@ public boolean validateSearchStrings(String query) { if (!caseSensitive) { searchString = searchString.toLowerCase(); } - List words = parseQuery(searchString); + List words = new SentenceAnalyzer(searchString).getWords(); try { for (String word : words) { Pattern.compile(word, caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); @@ -60,7 +68,7 @@ public boolean applyRule(String query, BibtexEntry bibtexEntry) { searchString = searchString.toLowerCase(); } - List words = parseQuery(searchString); + List words = new SentenceAnalyzer(searchString).getWords(); List patterns = new ArrayList(); try { @@ -78,7 +86,7 @@ public boolean applyRule(String query, BibtexEntry bibtexEntry) { for (String field : bibtexEntry.getAllFields()) { Object fieldContentAsObject = bibtexEntry.getField(field); if (fieldContentAsObject != null) { - String fieldContent = BasicRegexSearchRule.removeBrackets.format(fieldContentAsObject.toString()); + String fieldContent = RegexBasedSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContentAsObject.toString()); if (!caseSensitive) { fieldContent = fieldContent.toLowerCase(); } @@ -87,7 +95,7 @@ public boolean applyRule(String query, BibtexEntry bibtexEntry) { // Check if we have a match for each of the query words, ignoring // those words for which we already have a match: for (Pattern pattern : patterns) { - String fieldContentNoBrackets = BasicRegexSearchRule.removeBrackets.format(fieldContent); + String fieldContentNoBrackets = RegexBasedSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContent); Matcher m = pattern.matcher(fieldContentNoBrackets); matchFound[index] = matchFound[index] || m.find(); diff --git a/src/main/java/net/sf/jabref/search/rules/SimpleSearchRule.java b/src/main/java/net/sf/jabref/search/rules/SimpleSearchRule.java deleted file mode 100644 index 537da28ee7e..00000000000 --- a/src/main/java/net/sf/jabref/search/rules/SimpleSearchRule.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright (C) 2003 Nathan Dunn, Morten O. Alver - - 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.search.rules; - -import net.sf.jabref.BibtexEntry; -import net.sf.jabref.export.layout.format.RemoveLatexCommands; -import net.sf.jabref.search.SearchRule; - -//TODO why have simple and basic search rule???? -public class SimpleSearchRule implements SearchRule { - - private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); - - private final boolean caseSensitive; - - public SimpleSearchRule(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - @Override - public boolean validateSearchStrings(String query) { - return true; - } - - @Override - public boolean applyRule(String query, BibtexEntry bibtexEntry) { - int score = 0; - for (String field : bibtexEntry.getAllFields()) { - Object fieldContentAsObject = bibtexEntry.getField(field); - if (fieldContentAsObject == null) { - continue; - } - - try { - String fieldContent = sanatizeFieldContent(fieldContentAsObject); - score += getNumberOfOccurrences(fieldContent, sanatizeString(query)); - } catch (Throwable t) { - System.err.println("sorting error: " + t); - } - } - return score > 0; - } - - private String sanatizeString(String query) { - if (!caseSensitive) { - return query.toLowerCase(); - } else { - return query; - } - } - - private String sanatizeFieldContent(Object fieldContentAsObject) { - return sanatizeString(SimpleSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContentAsObject.toString())); - } - - private static int getNumberOfOccurrences(String haystack, String needle) { - int score = 0; - int counter = haystack.indexOf(needle); - while (counter >= 0) { - score++; - counter = haystack.indexOf(needle, counter + 1); - } - return score; - } - -} diff --git a/src/main/java/net/sf/jabref/search/rules/util/SentenceAnalyzer.java b/src/main/java/net/sf/jabref/search/rules/util/SentenceAnalyzer.java new file mode 100644 index 00000000000..9c3d35bd6e4 --- /dev/null +++ b/src/main/java/net/sf/jabref/search/rules/util/SentenceAnalyzer.java @@ -0,0 +1,57 @@ +package net.sf.jabref.search.rules.util; + +import java.util.ArrayList; +import java.util.List; + +public class SentenceAnalyzer { + + public static final char ESCAPE_CHAR = '\\'; + public static final char QUOTE_CHAR = '"'; + + private final String query; + + public SentenceAnalyzer(String query) { + this.query = query; + } + + public List getWords() { + List result = new ArrayList(); + + StringBuffer sb = new StringBuffer(); + boolean escaped = false; + boolean quoted = false; + for(char c : query.toCharArray()) { + // Check if we are entering an escape sequence: + if (!escaped && (c == ESCAPE_CHAR)) { + escaped = true; + } else { + // See if we have reached the end of a word: + if (!escaped && !quoted && Character.isWhitespace(c)) { + if (sb.length() > 0) { + result.add(sb.toString()); + sb = new StringBuffer(); + } + } else if (c == QUOTE_CHAR) { + // Whether it is a start or end quote, store the current + // word if any: + if (sb.length() > 0) { + result.add(sb.toString()); + sb = new StringBuffer(); + } + quoted = !quoted; + } else { + // All other possibilities exhausted, we add the char to + // the current word: + sb.append(c); + } + escaped = false; + } + } + // Finished with the loop. If we have a current word, add it: + if (sb.length() > 0) { + result.add(sb.toString()); + } + + return result; + } +} diff --git a/src/test/java/net/sf/jabref/search/BasicSearchDescriberTest.java b/src/test/java/net/sf/jabref/search/ContainsAndRegexBasedSearchRuleDescriberTest.java similarity index 67% rename from src/test/java/net/sf/jabref/search/BasicSearchDescriberTest.java rename to src/test/java/net/sf/jabref/search/ContainsAndRegexBasedSearchRuleDescriberTest.java index 9f3eb384b8f..23edeb8f528 100644 --- a/src/test/java/net/sf/jabref/search/BasicSearchDescriberTest.java +++ b/src/test/java/net/sf/jabref/search/ContainsAndRegexBasedSearchRuleDescriberTest.java @@ -1,35 +1,35 @@ package net.sf.jabref.search; -import net.sf.jabref.search.describer.BasicSearchDescriber; +import net.sf.jabref.search.describer.ContainsAndRegexBasedSearchRuleDescriber; import org.junit.Test; import static org.junit.Assert.assertEquals; -public class BasicSearchDescriberTest { +public class ContainsAndRegexBasedSearchRuleDescriberTest { @Test public void testNoAst() throws Exception { - String query = "a=b"; + String query = "a b"; evaluateNoAst(query, true, true, "This group contains entries in which any field contains the regular expression " + - "a=b (case sensitive). Entries cannot be manually assigned to or removed " + + "ab (case sensitive). Entries cannot be manually assigned to or removed " + "from this group.


Hint: To search specific fields only, " + "enter for example:

author=smith and title=electrical"); evaluateNoAst(query, true, false, "This group contains entries in which any field contains the term " + - "a=b (case sensitive). Entries cannot be manually assigned to or removed from " + + "ab (case sensitive). Entries cannot be manually assigned to or removed from " + "this group.


Hint: To search specific fields only, enter for " + "example:

author=smith and title=electrical"); evaluateNoAst(query, false, false, "This group contains entries in which any field contains the term " + - "a=b (case insensitive). Entries cannot be manually assigned to or removed " + + "ab (case insensitive). Entries cannot be manually assigned to or removed " + "from this group.


Hint: To search specific fields only, enter for " + "example:

author=smith and title=electrical"); evaluateNoAst(query, false, true, "This group contains entries in which any field contains the regular " + - "expression a=b (case insensitive). Entries cannot be manually assigned " + + "expression ab (case insensitive). Entries cannot be manually assigned " + "to or removed from this group.


Hint: To search specific fields only, enter for " + "example:

author=smith and title=electrical"); } private void evaluateNoAst(String query, boolean caseSensitive, boolean regex, String expected) { - assertEquals(expected, new BasicSearchDescriber(caseSensitive, regex, query).getDescription()); + assertEquals(expected, new ContainsAndRegexBasedSearchRuleDescriber(caseSensitive, regex, query).getDescription()); } } diff --git a/src/test/java/net/sf/jabref/search/SearchExpressionDescriberTest.java b/src/test/java/net/sf/jabref/search/GrammarBasedSearchRuleDescriberTest.java similarity index 84% rename from src/test/java/net/sf/jabref/search/SearchExpressionDescriberTest.java rename to src/test/java/net/sf/jabref/search/GrammarBasedSearchRuleDescriberTest.java index f02b8aabaac..580ec68bb8e 100644 --- a/src/test/java/net/sf/jabref/search/SearchExpressionDescriberTest.java +++ b/src/test/java/net/sf/jabref/search/GrammarBasedSearchRuleDescriberTest.java @@ -1,12 +1,12 @@ package net.sf.jabref.search; -import net.sf.jabref.search.describer.SearchExpressionDescriber; -import net.sf.jabref.search.rules.SearchExpression; +import net.sf.jabref.search.describer.GrammarBasedSearchRuleDescriber; +import net.sf.jabref.search.rules.GrammarBasedSearchRule; import org.junit.Test; import static org.junit.Assert.*; -public class SearchExpressionDescriberTest { +public class GrammarBasedSearchRuleDescriberTest { @Test public void testSimpleQuery() throws Exception { @@ -47,9 +47,9 @@ public void testComplexQuery() throws Exception { private void evaluate(String query, boolean caseSensitive, boolean regex, String expected) { - SearchExpression searchExpression = new SearchExpression(caseSensitive, regex); - assertTrue(searchExpression.validateSearchStrings(query)); - SearchExpressionDescriber describer = new SearchExpressionDescriber(caseSensitive, regex, searchExpression.getTree()); + GrammarBasedSearchRule grammarBasedSearchRule = new GrammarBasedSearchRule(caseSensitive, regex); + assertTrue(grammarBasedSearchRule.validateSearchStrings(query)); + GrammarBasedSearchRuleDescriber describer = new GrammarBasedSearchRuleDescriber(caseSensitive, regex, grammarBasedSearchRule.getTree()); assertEquals(expected, describer.getDescription()); } } \ No newline at end of file diff --git a/src/test/java/net/sf/jabref/search/rules/BasicSearchRuleTest.java b/src/test/java/net/sf/jabref/search/rules/ContainBasedSearchRuleTest.java similarity index 78% rename from src/test/java/net/sf/jabref/search/rules/BasicSearchRuleTest.java rename to src/test/java/net/sf/jabref/search/rules/ContainBasedSearchRuleTest.java index feee21060ff..613587e2c16 100644 --- a/src/test/java/net/sf/jabref/search/rules/BasicSearchRuleTest.java +++ b/src/test/java/net/sf/jabref/search/rules/ContainBasedSearchRuleTest.java @@ -2,27 +2,25 @@ import net.sf.jabref.*; -import net.sf.jabref.search.rules.BasicRegexSearchRule; -import net.sf.jabref.search.rules.BasicSearchRule; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; /** - * Test case for BasicSearchRule. + * Test case for ContainBasedSearchRule. */ -public class BasicSearchRuleTest { +public class ContainBasedSearchRuleTest { @Test public void testBasicSearchParsing() { Globals.prefs = JabRefPreferences.getInstance(); BibtexEntry be = makeBibtexEntry(); - BasicSearchRule bsCaseSensitive = new BasicSearchRule(true); - BasicSearchRule bsCaseInsensitive = new BasicSearchRule(false); - BasicSearchRule bsCaseSensitiveRegexp = new BasicRegexSearchRule(true); - BasicSearchRule bsCaseInsensitiveRegexp = new BasicRegexSearchRule(false); + ContainBasedSearchRule bsCaseSensitive = new ContainBasedSearchRule(true); + ContainBasedSearchRule bsCaseInsensitive = new ContainBasedSearchRule(false); + RegexBasedSearchRule bsCaseSensitiveRegexp = new RegexBasedSearchRule(true); + RegexBasedSearchRule bsCaseInsensitiveRegexp = new RegexBasedSearchRule(false); String query = "marine 2001 shields"; diff --git a/src/test/java/net/sf/jabref/search/rules/SentenceAnalyzerTest.java b/src/test/java/net/sf/jabref/search/rules/SentenceAnalyzerTest.java index d99939623c2..03d90929b22 100644 --- a/src/test/java/net/sf/jabref/search/rules/SentenceAnalyzerTest.java +++ b/src/test/java/net/sf/jabref/search/rules/SentenceAnalyzerTest.java @@ -1,5 +1,6 @@ package net.sf.jabref.search.rules; +import net.sf.jabref.search.rules.util.SentenceAnalyzer; import org.junit.Test; import java.util.Arrays; @@ -11,10 +12,10 @@ public class SentenceAnalyzerTest { @Test public void testGetWords() throws Exception { - assertEquals(Arrays.asList("a","b"), new BasicSearchRule.SentenceAnalyzer("a b").getWords()); - assertEquals(Arrays.asList("a","b"), new BasicSearchRule.SentenceAnalyzer(" a b ").getWords()); - assertEquals(Collections.singletonList("b "), new BasicSearchRule.SentenceAnalyzer("\"b \" ").getWords()); - assertEquals(Collections.singletonList(" a"), new BasicSearchRule.SentenceAnalyzer(" \\ a").getWords()); + assertEquals(Arrays.asList("a","b"), new SentenceAnalyzer("a b").getWords()); + assertEquals(Arrays.asList("a","b"), new SentenceAnalyzer(" a b ").getWords()); + assertEquals(Collections.singletonList("b "), new SentenceAnalyzer("\"b \" ").getWords()); + assertEquals(Collections.singletonList(" a"), new SentenceAnalyzer(" \\ a").getWords()); } } \ No newline at end of file From 6ba896555032127e608d64013fbc0f9beb8f7273 Mon Sep 17 00:00:00 2001 From: Simon Harrer Date: Tue, 28 Jul 2015 09:09:19 +0200 Subject: [PATCH 2/4] Quickfix: used join instead of format. --- .../describer/ContainsAndRegexBasedSearchRuleDescriber.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java b/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java index 5e9123a0c09..ff42ca001fb 100644 --- a/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java +++ b/src/main/java/net/sf/jabref/search/describer/ContainsAndRegexBasedSearchRuleDescriber.java @@ -38,7 +38,7 @@ public String getDescription() { for(String word : unprocessedWords) { unprocessedWordsInHtmlFormat.add(String.format("%s", StringUtil.quoteForHTML(word))); } - String andSeparator = String.join(" %s ", Globals.lang("and")); + String andSeparator = String.format(" %s ", Globals.lang("and")); String[] unprocessedWordsInHtmlFormatArray = unprocessedWordsInHtmlFormat.toArray(new String[unprocessedWordsInHtmlFormat.size()]); searchDescription += StringUtil.join(unprocessedWordsInHtmlFormatArray, andSeparator); } From 0171dba0e39389139957e0f3ec523077d9941d2f Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 28 Jul 2015 11:19:27 +0200 Subject: [PATCH 3/4] "ISBN to BibTeX" fetcher now uses eBook.de's API (fixes bug #1241) --- CHANGELOG | 1 + .../imports/fetcher/ISBNtoBibTeXFetcher.java | 35 +++++++++---------- src/main/resources/help/ISBNtoBibTeXHelp.html | 5 ++- .../resources/help/fr/ISBNtoBibTeXHelp.html | 4 +-- .../resources/help/ja/ISBNtoBibTeXHelp.html | 6 ++-- .../plugins/net.sf.jabref.core/plugin.xml | 2 -- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2ddfb8a3502..fb7415a14b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ [master] + - "ISBN to BibTeX" fetcher now uses eBook.de's API (fixes bug #1241) - Update dependencies: jersey, commons.logging, pdfbox, jgoodies, lazedlists, JDBC connectors - Refactored preferences - Remove local jgoodies dependency: replace SimpleInteralFrame with swingx JXTitledPanel and UIFSplitPane by JSplitPane diff --git a/src/main/java/net/sf/jabref/imports/fetcher/ISBNtoBibTeXFetcher.java b/src/main/java/net/sf/jabref/imports/fetcher/ISBNtoBibTeXFetcher.java index a0e25ef7afc..96f21badcc2 100644 --- a/src/main/java/net/sf/jabref/imports/fetcher/ISBNtoBibTeXFetcher.java +++ b/src/main/java/net/sf/jabref/imports/fetcher/ISBNtoBibTeXFetcher.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2012 JabRef contributors. +/* Copyright (C) 2012, 2015 JabRef contributors. 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 3 of the License, or @@ -14,12 +14,11 @@ */ package net.sf.jabref.imports.fetcher; -import java.io.IOException; +import java.io.FileNotFoundException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLConnection; import java.net.URLEncoder; import java.util.Scanner; @@ -36,14 +35,12 @@ import net.sf.jabref.imports.UnitFormatter; /** - * This class uses Manas Tungare's ISBN to BibTeX Converter to convert an ISBN to a BibTeX entry
- * The online version of the converter is available at http://manas.tungare.name/software/isbn-to-bibtex/ - * This was not approved by him, see discussion https://sourceforge.net/p/jabref/bugs/1241/. - * We are currently working on sorting things out + * This class uses ebook.de's ISBN to BibTeX Converter to convert an ISBN to a BibTeX entry
+ * There is no separate web-based converter available, just that API */ public class ISBNtoBibTeXFetcher implements EntryFetcher { - private static final String URL_PATTERN = "http://manas.tungare.name/software/isbn-to-bibtex/isbn-service?isbn=%s"; + private static final String URL_PATTERN = "http://www.ebook.de/de/tools/isbn2bibtex?isbn=%s"; private final CaseKeeper caseKeeper = new CaseKeeper(); private final UnitFormatter unitFormatter = new UnitFormatter(); @@ -69,7 +66,6 @@ public boolean processQuery(String query, ImportInspector inspector, OutputPrint // Send the request URL url; - URLConnection conn; try { url = new URL(urlString); } catch (MalformedURLException e) { @@ -80,20 +76,21 @@ public boolean processQuery(String query, ImportInspector inspector, OutputPrint InputStream source; try { source = url.openStream(); - } catch (IOException e) { - e.printStackTrace(); + } catch (FileNotFoundException e) { + // invalid ISBN --> 404--> FileNotFoundException + status.showMessage(Globals.lang("Invalid ISBN")); + return false; + } catch (java.net.UnknownHostException e) { + // It is very unlikely that ebook.de is an unknown host + // It is more likely that we don't have an internet connection + status.showMessage(Globals.lang("No_Internet_Connection.")); + return false; + } catch (Exception e) { + status.showMessage(e.toString()); return false; } String bibtexString = new Scanner(source).useDelimiter("\\A").next(); - if (bibtexString.startsWith("@comment")) { - // an error occured - // the error is nested in @comment{...} - String errorMsg = bibtexString.substring("@comment{".length()); - errorMsg = errorMsg.substring(0, errorMsg.length() - 1); - status.showMessage(errorMsg); // showMessage does not work -> NPE - return false; - } BibtexEntry entry = BibtexParser.singleFromString(bibtexString); if (entry != null) { diff --git a/src/main/resources/help/ISBNtoBibTeXHelp.html b/src/main/resources/help/ISBNtoBibTeXHelp.html index 01daaf354fd..aa2b36c60c4 100644 --- a/src/main/resources/help/ISBNtoBibTeXHelp.html +++ b/src/main/resources/help/ISBNtoBibTeXHelp.html @@ -1,6 +1,6 @@ - + @@ -9,8 +9,7 @@

Fetching entries using the ISBN number

To use this feature, choose Search -> Web search, and the search interface will appear in the side pane. Select ISBN to BibTeX in the dropdown menu.

-

This fetcher uses Manas Tungare's ISBN to BibTeX Converter - to convert an ISBN to a BibTeX entry.

+

This fetcher uses eBook.de's API to convert an ISBN to a BibTeX entry.

Enter the ISBN number in the search field and press Enter or the Fetch button. The entry will be fetched and added to your currently active diff --git a/src/main/resources/help/fr/ISBNtoBibTeXHelp.html b/src/main/resources/help/fr/ISBNtoBibTeXHelp.html index 129f9f5bafb..0886dd41fdc 100644 --- a/src/main/resources/help/fr/ISBNtoBibTeXHelp.html +++ b/src/main/resources/help/fr/ISBNtoBibTeXHelp.html @@ -1,6 +1,6 @@ - + @@ -11,7 +11,7 @@

Récupération d'entrées à partir du numéro Sélectionnez ISBN vers BibTeX dans le menu déroulant.

Ce moteur de recherche utilise - Manas Tungare's ISBN to BibTeX Converter + eBook.de's API pour convertir un ISBN en une entrée BibTeX.

Entrer le numéro ISBN dans le champ de recherche et presser sur Entrer diff --git a/src/main/resources/help/ja/ISBNtoBibTeXHelp.html b/src/main/resources/help/ja/ISBNtoBibTeXHelp.html index d775ee33be5..5c6350893af 100644 --- a/src/main/resources/help/ja/ISBNtoBibTeXHelp.html +++ b/src/main/resources/help/ja/ISBNtoBibTeXHelp.html @@ -1,7 +1,7 @@ - - + + @@ -9,7 +9,7 @@

ISBN番号を使用して項目を取得する

この機能を使うには、検索→ウェブ検索を選択すれば、操作盤が側面に表示されますので、そのドロップダウンメニューからISBN to BibTeXを選択してください。

-

この取得子は、ISBNからBibTeX項目に変換するのに、Manas TungareのISBN to BibTeX Converterを使用しています。

+

この取得子は、ISBNからBibTeX項目に変換するのに、eBook.de's APIを使用しています。

検索フィールドにISBN番号を入力し、Enter鍵を押すか取得ボタンを押してください。項目が取得されて、現在アクティブになっているデータベースに追加されます。エラーが発生した場合には、その旨ポップアップで表示されます。

diff --git a/src/main/resources/plugins/net.sf.jabref.core/plugin.xml b/src/main/resources/plugins/net.sf.jabref.core/plugin.xml index 970c0a5b9b1..875013ba12a 100644 --- a/src/main/resources/plugins/net.sf.jabref.core/plugin.xml +++ b/src/main/resources/plugins/net.sf.jabref.core/plugin.xml @@ -169,7 +169,6 @@ value="Fetch Entries from DBLP." /> - From a2afe397a95a8909b4a3f8a5d3290a65f8d3bdb5 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 28 Jul 2015 15:42:40 +0200 Subject: [PATCH 4/4] Add missing strings to translate --- src/main/resources/resource/JabRef_da.properties | 2 ++ src/main/resources/resource/JabRef_de.properties | 2 ++ src/main/resources/resource/JabRef_en.properties | 2 ++ src/main/resources/resource/JabRef_es.properties | 2 ++ src/main/resources/resource/JabRef_fr.properties | 2 ++ src/main/resources/resource/JabRef_in.properties | 2 ++ src/main/resources/resource/JabRef_it.properties | 2 ++ src/main/resources/resource/JabRef_ja.properties | 2 ++ src/main/resources/resource/JabRef_nl.properties | 2 ++ src/main/resources/resource/JabRef_no.properties | 2 ++ src/main/resources/resource/JabRef_pt_BR.properties | 2 ++ src/main/resources/resource/JabRef_ru.properties | 2 ++ src/main/resources/resource/JabRef_tr.properties | 2 ++ src/main/resources/resource/JabRef_vi.properties | 2 ++ src/main/resources/resource/JabRef_zh.properties | 2 ++ 15 files changed, 30 insertions(+) diff --git a/src/main/resources/resource/JabRef_da.properties b/src/main/resources/resource/JabRef_da.properties index 461266934b5..ebc1b14b74f 100644 --- a/src/main/resources/resource/JabRef_da.properties +++ b/src/main/resources/resource/JabRef_da.properties @@ -1720,3 +1720,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_de.properties b/src/main/resources/resource/JabRef_de.properties index ba359736421..e88d6bc1d59 100644 --- a/src/main/resources/resource/JabRef_de.properties +++ b/src/main/resources/resource/JabRef_de.properties @@ -2476,3 +2476,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_en.properties b/src/main/resources/resource/JabRef_en.properties index 3b98d2ed7d9..9f50dc3eae7 100644 --- a/src/main/resources/resource/JabRef_en.properties +++ b/src/main/resources/resource/JabRef_en.properties @@ -2465,3 +2465,5 @@ Please_open_http\://github.com/JabRef/jabref_manually.=Please_open_http://github Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1=Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD:_%0\nNEW:_%1 Found_%0_broken_links=Found_%0_broken_links This_group_contains_all_entries._It_cannot_be_edited_or_removed.=This_group_contains_all_entries._It_cannot_be_edited_or_removed. + +Invalid_ISBN=Invalid_ISBN diff --git a/src/main/resources/resource/JabRef_es.properties b/src/main/resources/resource/JabRef_es.properties index 9bfaffdfe00..2a99e405ef4 100644 --- a/src/main/resources/resource/JabRef_es.properties +++ b/src/main/resources/resource/JabRef_es.properties @@ -1611,3 +1611,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_fr.properties b/src/main/resources/resource/JabRef_fr.properties index c204dab535a..151af8e16ec 100644 --- a/src/main/resources/resource/JabRef_fr.properties +++ b/src/main/resources/resource/JabRef_fr.properties @@ -1658,3 +1658,5 @@ Rebind_C-f,_too=R\u00e9-associer_aussi_C-f Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1=Duplication_d'abréviations_de_journaux_-_l'ancienne_sera_remplacée_par_la_nouvelle\nANCIENNE\:_%0\nNOUVELLE\:_%1 Found_%0_broken_links=%0_liens_cassés trouvés This_group_contains_all_entries._It_cannot_be_edited_or_removed.=Ce_groupe_contient_toutes_les_entrées._Il_ne_peut_pas_être_modifié_ou_supprimé. + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_in.properties b/src/main/resources/resource/JabRef_in.properties index 26aab64ec39..4736fbfeac3 100644 --- a/src/main/resources/resource/JabRef_in.properties +++ b/src/main/resources/resource/JabRef_in.properties @@ -1631,3 +1631,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_it.properties b/src/main/resources/resource/JabRef_it.properties index 35631f592de..1577fadae4d 100644 --- a/src/main/resources/resource/JabRef_it.properties +++ b/src/main/resources/resource/JabRef_it.properties @@ -1742,3 +1742,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_ja.properties b/src/main/resources/resource/JabRef_ja.properties index 4eb47d28862..a14034d946c 100644 --- a/src/main/resources/resource/JabRef_ja.properties +++ b/src/main/resources/resource/JabRef_ja.properties @@ -2461,3 +2461,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_nl.properties b/src/main/resources/resource/JabRef_nl.properties index a5dce4f4b01..6dbe1df6bd2 100644 --- a/src/main/resources/resource/JabRef_nl.properties +++ b/src/main/resources/resource/JabRef_nl.properties @@ -2462,3 +2462,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_no.properties b/src/main/resources/resource/JabRef_no.properties index 73c73fa79b4..fa5881e7ad2 100644 --- a/src/main/resources/resource/JabRef_no.properties +++ b/src/main/resources/resource/JabRef_no.properties @@ -2927,3 +2927,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_pt_BR.properties b/src/main/resources/resource/JabRef_pt_BR.properties index b330d641f3c..c6339e143d7 100644 --- a/src/main/resources/resource/JabRef_pt_BR.properties +++ b/src/main/resources/resource/JabRef_pt_BR.properties @@ -1627,3 +1627,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_ru.properties b/src/main/resources/resource/JabRef_ru.properties index d0d2f21ea77..f5c9fe3c54f 100644 --- a/src/main/resources/resource/JabRef_ru.properties +++ b/src/main/resources/resource/JabRef_ru.properties @@ -2464,3 +2464,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_tr.properties b/src/main/resources/resource/JabRef_tr.properties index 2f9713d1b98..689721a7de5 100644 --- a/src/main/resources/resource/JabRef_tr.properties +++ b/src/main/resources/resource/JabRef_tr.properties @@ -1649,3 +1649,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_vi.properties b/src/main/resources/resource/JabRef_vi.properties index 7a7010f1334..d2e6ec3378c 100644 --- a/src/main/resources/resource/JabRef_vi.properties +++ b/src/main/resources/resource/JabRef_vi.properties @@ -2458,3 +2458,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN= diff --git a/src/main/resources/resource/JabRef_zh.properties b/src/main/resources/resource/JabRef_zh.properties index 4cd81dda462..68cb5d179e2 100644 --- a/src/main/resources/resource/JabRef_zh.properties +++ b/src/main/resources/resource/JabRef_zh.properties @@ -2451,3 +2451,5 @@ Rebind_C-f,_too= Duplicate_Journal_Abbreviation_-_old_one_will_be_overwritten_by_new_one\nOLD\:_%0\nNEW\:_%1= Found_%0_broken_links= This_group_contains_all_entries._It_cannot_be_edited_or_removed.= + +Invalid_ISBN=