diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index 5d3e526985d..4cf95de899d 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -68,6 +68,14 @@ public class PreviewViewer extends ScrollPane implements InvalidationListener { "function(){function n(e){t(this,n),this.ctx=e,this.ie=!1;var r=window.navigator.userAgent;(r.indexOf(\"MSIE\")>-1||r.indexOf(\"Trident\")>-1)&&(this.ie=!0)}return r(n,[{key:\"log\",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"debug\",r=this.opt.log;this.opt.debug&&\"object\"===e(r)&&\"function\"==typeof r[n]&&r[n](\"mark.js: \".concat(t))}},{key:\"getSeparatedKeywords\",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(\" \").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:\"isNumeric\",value:function(e){return Number(parseFloat(e))==e}},{key:\"checkRanges\",value:function(e){var t=this;if(!Array.isArray(e)||\"[object Object]\"!==Object.prototype.toString.call(e[0]))return this.log(\"markRanges() will only accept an array of objects\"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var o=t.callNoMatchOnInvalidRanges(e,r),i=o.start,a=o.end;o.valid&&(e.start=i,e.length=a-i,n.push(e),r=a)}),n}},{key:\"callNoMatchOnInvalidRanges\",value:function(e,t){var n,r,o=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?o=!0:(this.log(\"Ignoring invalid or overlapping range: \"+\"\".concat(JSON.stringify(e))),this.opt.noMatch(e))):(this.log(\"Ignoring invalid range: \".concat(JSON.stringify(e))),this.opt.noMatch(e)),{start:n,end:r,valid:o}}},{key:\"checkWhitespaceRanges\",value:function(e,t,n){var r,o=!0,i=n.length,a=t-i,s=parseInt(e.start,10)-a;return(r=(s=s>i?i:s)+parseInt(e.length,10))>i&&(r=i,this.log(\"End range automatically set to the max value of \".concat(i))),s<0||r-s<0||s>i||r>i?(o=!1,this.log(\"Invalid range: \".concat(JSON.stringify(e))),this.opt.noMatch(e)):\"\"===n.substring(s,r).replace(/\\s+/g,\"\")&&(o=!1,this.log(\"Skipping whitespace only range: \"+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:o}}},{key:\"getTextNodes\",value:function(e){var t=this,n=\"\",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:\"matchesExclude\",value:function(e){return i.matches(e,this.opt.exclude.concat([\"script\",\"style\",\"title\",\"head\",\"html\"]))}},{key:\"wrapRangeInTextNode\",value:function(e,t,n){var r=this.opt.element?this.opt.element:\"mark\",o=e.splitText(t),i=o.splitText(n-t),a=document.createElement(r);return a.setAttribute(\"data-markjs\",\"true\"),this.opt.className&&a.setAttribute(\"class\",this.opt.className),a.textContent=o.textContent,o.parentNode.replaceChild(a,o),i}},{key:\"wrapRangeInMappedTextNode\",value:function(e,t,n,r,o){var i=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=i.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,o(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:\"wrapGroups\",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:\"separateGroups\",value:function(e,t,n,r,o){for(var i=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,o))}return e}},{key:\"wrapMatches\",value:function(e,t,n,r,o){var i=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){var o;for(t=t.node;null!==(o=e.exec(t.textContent))&&\"\"!==o[a];){if(i.opt.separateGroups)t=i.separateGroups(t,o,a,n,r);else{if(!n(o[a],t))continue;var s=o.index;if(0!==a)for(var c=1;c\n" + ""; + private static final String JS_MARK_REG_EXP_CALLBACK = "" + + "{done: function(){" + + " markInstance.markRegExp(%s);}" + + "}"; + private static final String JS_UNMARK_WITH_CALLBACK = "" + + "var markInstance = new Mark(document.getElementById(\"content\"));" + + "markInstance.unmark(%s);"; + private static final Pattern UNESCAPED_FORWARD_SLASH = Pattern.compile("\"(? getSearchWords() { if (isRegularExpression()) { @@ -151,7 +168,9 @@ public Optional getJavaScriptPatternForWords() { return joinWordsToPattern(EscapeMode.JAVASCRIPT); } - /** Returns a regular expression pattern in the form (w1)|(w2)| ... wi are escaped if no regular expression search is enabled + /** + * Returns a regular expression pattern in the form (w1)|(w2)| ... wi are escaped if no regular expression search is enabled + * * @param escapeMode the mode of escaping special characters in wi */ private Optional joinWordsToPattern(EscapeMode escapeMode) { @@ -162,24 +181,12 @@ private Optional joinWordsToPattern(EscapeMode escapeMode) { } // compile the words to a regular expression in the form (w1)|(w2)|(w3) - StringJoiner joiner = new StringJoiner(")|(", "(", ")"); - for (String word : words) { - if (regularExpression) { - joiner.add(word); - } else { - switch (escapeMode) { - case JAVA: - joiner.add(Pattern.quote(word)); - break; - case JAVASCRIPT: - joiner.add(JAVASCRIPT_ESCAPED_CHARS_PATTERN.matcher(word).replaceAll("\\\\$0")); - break; - default: - throw new IllegalArgumentException("Unknown special characters escape mode: " + escapeMode); - } - } + Stream joiner = words.stream(); + if (!regularExpression) { + // Reformat string when we are looking for a literal match + joiner = joiner.map(escapeMode::format); } - String searchPattern = joiner.toString(); + String searchPattern = joiner.collect(Collectors.joining(")|(", "(", ")")); if (caseSensitive) { return Optional.of(Pattern.compile(searchPattern)); diff --git a/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java b/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java index f70a05b1576..98e12978963 100644 --- a/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java +++ b/src/main/java/org/jabref/model/search/rules/GrammarBasedSearchRule.java @@ -232,7 +232,7 @@ public Boolean visitComparison(SearchParser.ComparisonContext context) { if (fieldDescriptor.isPresent()) { return comparison(fieldDescriptor.get().getText(), ComparisonOperator.build(context.operator.getText()), right); } else { - return new ContainBasedSearchRule(caseSensitive).applyRule(right, entry); + return SearchRules.getSearchRule(caseSensitive, regex).applyRule(right, entry); } } diff --git a/src/main/java/org/jabref/model/search/rules/SearchRules.java b/src/main/java/org/jabref/model/search/rules/SearchRules.java index 88268f6d2b7..dcde2b66227 100644 --- a/src/main/java/org/jabref/model/search/rules/SearchRules.java +++ b/src/main/java/org/jabref/model/search/rules/SearchRules.java @@ -31,7 +31,7 @@ private static boolean isSimpleQuery(String query) { return SIMPLE_EXPRESSION.matcher(query).matches(); } - private static SearchRule getSearchRule(boolean caseSensitive, boolean regex) { + static SearchRule getSearchRule(boolean caseSensitive, boolean regex) { if (regex) { return new RegexBasedSearchRule(caseSensitive); } else { diff --git a/src/test/java/org/jabref/model/search/rules/ContainBasedSearchRuleTest.java b/src/test/java/org/jabref/model/search/rules/ContainBasedSearchRuleTest.java index 3d9f5c94314..ad7b0f645ad 100644 --- a/src/test/java/org/jabref/model/search/rules/ContainBasedSearchRuleTest.java +++ b/src/test/java/org/jabref/model/search/rules/ContainBasedSearchRuleTest.java @@ -6,7 +6,8 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test case for ContainBasedSearchRule. @@ -23,32 +24,31 @@ public void testBasicSearchParsing() { String query = "marine 2001 shields"; - assertEquals(false, bsCaseSensitive.applyRule(query, be)); - assertEquals(true, bsCaseInsensitive.applyRule(query, be)); - assertEquals(false, bsCaseSensitiveRegexp.applyRule(query, be)); - assertEquals(false, bsCaseInsensitiveRegexp.applyRule(query, be)); + assertFalse(bsCaseSensitive.applyRule(query, be)); + assertTrue(bsCaseInsensitive.applyRule(query, be)); + assertFalse(bsCaseSensitiveRegexp.applyRule(query, be)); + assertFalse(bsCaseInsensitiveRegexp.applyRule(query, be)); query = "\"marine larviculture\""; - assertEquals(false, bsCaseSensitive.applyRule(query, be)); - assertEquals(false, bsCaseInsensitive.applyRule(query, be)); - assertEquals(false, bsCaseSensitiveRegexp.applyRule(query, be)); - assertEquals(false, bsCaseInsensitiveRegexp.applyRule(query, be)); + assertFalse(bsCaseSensitive.applyRule(query, be)); + assertFalse(bsCaseInsensitive.applyRule(query, be)); + assertFalse(bsCaseSensitiveRegexp.applyRule(query, be)); + assertFalse(bsCaseInsensitiveRegexp.applyRule(query, be)); query = "marine [A-Za-z]* larviculture"; - assertEquals(false, bsCaseSensitive.applyRule(query, be)); - assertEquals(false, bsCaseInsensitive.applyRule(query, be)); - assertEquals(false, bsCaseSensitiveRegexp.applyRule(query, be)); - assertEquals(true, bsCaseInsensitiveRegexp.applyRule(query, be)); + assertFalse(bsCaseSensitive.applyRule(query, be)); + assertFalse(bsCaseInsensitive.applyRule(query, be)); + assertFalse(bsCaseSensitiveRegexp.applyRule(query, be)); + assertTrue(bsCaseInsensitiveRegexp.applyRule(query, be)); } public BibEntry makeBibtexEntry() { - BibEntry e = new BibEntry(StandardEntryType.InCollection); - e.setField(StandardField.TITLE, "Marine finfish larviculture in Europe"); - e.setCitationKey("shields01"); - e.setField(StandardField.YEAR, "2001"); - e.setField(StandardField.AUTHOR, "Kevin Shields"); - return e; + return new BibEntry(StandardEntryType.InCollection) + .withCitationKey("shields01") + .withField(StandardField.TITLE, "Marine finfish larviculture in Europe") + .withField(StandardField.YEAR, "2001") + .withField(StandardField.AUTHOR, "Kevin Shields"); } } diff --git a/src/test/java/org/jabref/model/search/rules/GrammarBasedSearchRuleTest.java b/src/test/java/org/jabref/model/search/rules/GrammarBasedSearchRuleTest.java new file mode 100644 index 00000000000..28f045b07fb --- /dev/null +++ b/src/test/java/org/jabref/model/search/rules/GrammarBasedSearchRuleTest.java @@ -0,0 +1,42 @@ +package org.jabref.model.search.rules; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case for GrammarBasedSearchRuleTest. + */ +public class GrammarBasedSearchRuleTest { + + @Test + void applyRuleMatchesSingleTermWithRegex() { + GrammarBasedSearchRule searchRule = new GrammarBasedSearchRule(true, true); + + String query = "M[a-z]+e"; + assertTrue(searchRule.validateSearchStrings(query)); + assertTrue(searchRule.applyRule(query, makeBibtexEntry())); + } + + @Test + void applyRuleDoesNotMatchSingleTermWithRegex() { + GrammarBasedSearchRule searchRule = new GrammarBasedSearchRule(true, true); + + String query = "M[0-9]+e"; + assertTrue(searchRule.validateSearchStrings(query)); + assertFalse(searchRule.applyRule(query, makeBibtexEntry())); + } + + public BibEntry makeBibtexEntry() { + return new BibEntry(StandardEntryType.InCollection) + .withCitationKey("shields01") + .withField(StandardField.TITLE, "Marine finfish larviculture in Europe") + .withField(StandardField.YEAR, "2001") + .withField(StandardField.AUTHOR, "Kevin Shields"); + } +}