diff --git a/languagetool-core/src/main/java/org/languagetool/Language.java b/languagetool-core/src/main/java/org/languagetool/Language.java index 50b1be71106a..bec7d1982705 100644 --- a/languagetool-core/src/main/java/org/languagetool/Language.java +++ b/languagetool-core/src/main/java/org/languagetool/Language.java @@ -839,9 +839,12 @@ protected int getPriorityForId(String id) { public int getRulePriority(Rule rule) { int categoryPriority = this.getPriorityForId(rule.getCategory().getId().toString()); int rulePriority = this.getPriorityForId(rule.getId()); + int rulePriorityFromRule = rule.getPriority(); // if there is a priority defined for rule it takes precedence over category priority if (rulePriority != 0) { return rulePriority; + } else if ( rulePriorityFromRule != 0) { + return rulePriorityFromRule; } else { return categoryPriority; } diff --git a/languagetool-core/src/main/java/org/languagetool/rules/Rule.java b/languagetool-core/src/main/java/org/languagetool/rules/Rule.java index 8b5261497beb..86137bc5e51f 100644 --- a/languagetool-core/src/main/java/org/languagetool/rules/Rule.java +++ b/languagetool-core/src/main/java/org/languagetool/rules/Rule.java @@ -74,6 +74,7 @@ public abstract class Rule { private boolean officeDefaultOff = false; private int minPrevMatches = 0; // minimum number of previous matches to show the rule private int distanceTokens = -1; // distance (number of tokens) between matches to consider a repetition + private int priority = 0; public Rule() { this(null); @@ -620,4 +621,12 @@ public boolean isGoalSpecific() { public void setGoalSpecific(boolean goalSpecific) { isGoalSpecific = goalSpecific; } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } } diff --git a/languagetool-core/src/main/java/org/languagetool/rules/patterns/AbstractPatternRule.java b/languagetool-core/src/main/java/org/languagetool/rules/patterns/AbstractPatternRule.java index 01726fe2a553..33e2f2ba5823 100644 --- a/languagetool-core/src/main/java/org/languagetool/rules/patterns/AbstractPatternRule.java +++ b/languagetool-core/src/main/java/org/languagetool/rules/patterns/AbstractPatternRule.java @@ -82,7 +82,7 @@ public AbstractPatternRule(String id, String description, Language language, Lis public AbstractPatternRule(String id, String description, Language language, List patternTokens, boolean getUnified) { this.id = Objects.requireNonNull(id, "id cannot be null"); - this.description = Objects.requireNonNull(description, "description ('name' in XML) cannot be null"); + this.description = Objects.requireNonNull(description, "description ('name' in XML) cannot be null ruleID: " + id); this.language = Objects.requireNonNull(language, "language cannot be null"); this.getUnified = getUnified; if (patternTokens != null) { diff --git a/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java b/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java index 6fe102e6f20a..2bd150784472 100644 --- a/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java +++ b/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.ObjectUtils; import org.languagetool.*; -import org.languagetool.broker.ResourceDataBroker; import org.languagetool.rules.*; import org.languagetool.tagging.disambiguation.rules.DisambiguationPatternRule; import org.xml.sax.Attributes; @@ -140,6 +139,10 @@ public void startElement(String namespaceURI, String lName, categoryToneTags.addAll(Arrays.asList(attrs.getValue("tone_tags").split(" "))); } isGoalSpecificCategoryAttribute = attrs.getValue(GOAL_SPECIFIC); + String prioCategoryAttributeValue = attrs.getValue(PRIO); + if (prioCategoryAttributeValue != null) { + prioCategoryAttribute = Integer.parseInt(prioCategoryAttributeValue); + } break; case "rules": String languageStr = attrs.getValue("lang"); @@ -281,6 +284,10 @@ public void startElement(String namespaceURI, String lName, } else { isGoalSpecific = false; } + String prioRuleAttributeValue = attrs.getValue(PRIO); + if (prioRuleAttributeValue != null) { + prioRuleAttribute = Integer.parseInt(prioRuleAttributeValue); + } break; case PATTERN: startPattern(attrs); @@ -425,6 +432,10 @@ public void startElement(String namespaceURI, String lName, ruleGroupDistanceTokens = Integer.parseInt(distanceTokensStr2); } antipatternForRuleGroupsExamples = new ArrayList<>(); + String prioRuleGroupAttributeValue = attrs.getValue(PRIO); + if (prioRuleGroupAttributeValue != null) { + prioRuleGroupAttribute = Integer.parseInt(prioRuleGroupAttributeValue); + } break; case MATCH: setMatchElement(attrs, inSuggestion && (isSuggestionSuppressMisspelled || isRuleSuppressMisspelled)); @@ -481,6 +492,7 @@ public void endElement(String namespaceURI, String sName, isGoalSpecific = false; premiumCategoryAttribute = null; isGoalSpecificCategoryAttribute = null; + prioCategoryAttribute = 0; break; case "regexp": inRegex = false; @@ -526,6 +538,7 @@ public void endElement(String namespaceURI, String sName, ruleToneTags.clear(); isPremiumRule = false; isGoalSpecific = false; + prioRuleAttribute = 0; break; case EXCEPTION: finalizeExceptions(); @@ -675,6 +688,7 @@ public void endElement(String namespaceURI, String sName, premiumRuleGroupAttribute = null; isGoalSpecificRuleGroupAttribute = null; antipatternForRuleGroupsExamples.clear(); + prioRuleGroupAttribute = 0; break; case MARKER: if (inCorrectExample) { @@ -910,6 +924,17 @@ protected void prepareRule(AbstractPatternRule rule) { } else if (categoryIssueType != null) { rule.setLocQualityIssueType(ITSIssueType.getIssueType(categoryIssueType)); } + int prio = 0; + if (prioCategoryAttribute != 0) { + prio = prioCategoryAttribute; + } + if (prioRuleGroupAttribute != 0) { + prio = prioRuleGroupAttribute; + } + if (prioRuleAttribute != 0) { + prio = prioRuleAttribute; + } + rule.setPriority(prio); } private final Map internedUrls = new HashMap<>(); diff --git a/languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java b/languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java index 26aa112a50a9..2dc66dbbf6c8 100644 --- a/languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java +++ b/languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java @@ -103,6 +103,7 @@ enum RegexpMode { protected static final String TABNAME = "tab"; protected static final String MINPREVMATCHES = "min_prev_matches"; protected static final String DISTANCETOKENS = "distance_tokens"; + protected static final String PRIO = "prio"; protected List rules = new ArrayList<>(); protected Language language; @@ -166,6 +167,9 @@ enum RegexpMode { protected String isGoalSpecificCategoryAttribute; protected String isGoalSpecificRuleGroupAttribute; protected boolean isGoalSpecific; + protected int prioCategoryAttribute; + protected int prioRuleGroupAttribute; + protected int prioRuleAttribute; protected boolean tokenLevelCaseSensitive; protected boolean tokenLevelCaseSet; diff --git a/languagetool-core/src/main/resources/org/languagetool/rules/rules.xsd b/languagetool-core/src/main/resources/org/languagetool/rules/rules.xsd index b1fe9904cd70..50baf47b4b82 100644 --- a/languagetool-core/src/main/resources/org/languagetool/rules/rules.xsd +++ b/languagetool-core/src/main/resources/org/languagetool/rules/rules.xsd @@ -77,6 +77,7 @@ + @@ -123,6 +124,7 @@ + @@ -250,6 +252,7 @@ + diff --git a/languagetool-core/src/test/java/org/languagetool/rules/patterns/PatternRuleLoaderTest.java b/languagetool-core/src/test/java/org/languagetool/rules/patterns/PatternRuleLoaderTest.java index 171152fcac13..962f9e7982f7 100644 --- a/languagetool-core/src/test/java/org/languagetool/rules/patterns/PatternRuleLoaderTest.java +++ b/languagetool-core/src/test/java/org/languagetool/rules/patterns/PatternRuleLoaderTest.java @@ -225,6 +225,24 @@ public void testToneTagsAttribute() throws IOException { assertTrue(isGoalSpecificFromCategoryRule.isGoalSpecific()); } + @Test + public void testPrioAttribute() throws IOException { + PatternRuleLoader prg = new PatternRuleLoader(); + String xmlFile = "/xx/grammar-withPrio.xml"; + List xmlPrioRules = prg.getRules(JLanguageTool.getDataBroker().getFromRulesDirAsStream(xmlFile), xmlFile, null); + + Rule rulePrio15 = getRuleById("CAT-PRIO-5-RG-PRIO-10-R-PRIO-15", xmlPrioRules); + assertEquals(15, rulePrio15.getPriority()); + Rule rulePrio10 = getRuleById("CAT-PRIO-5-RG-PRIO-10-R-PRIO-0", xmlPrioRules); + assertEquals(10, rulePrio10.getPriority()); + Rule rulePrio5 = getRuleById("CAT-PRIO-5-RG-PRIO-0-R-PRIO-0", xmlPrioRules); + assertEquals(5,rulePrio5.getPriority()); + Rule rulePrio0_0 = getRuleById("CAT-PRIO-0-RG-PRIO-0-R-PRIO-0", xmlPrioRules); + assertEquals(0, rulePrio0_0.getPriority()); + Rule rulePrio0_1 = getRuleById("CAT-PRIO-0-R-PRIO-0", xmlPrioRules); + assertEquals(0, rulePrio0_1.getPriority()); + } + private Set getCategoryNames(List rules) { Set categories = new HashSet<>(); for (AbstractPatternRule rule : rules) { diff --git a/languagetool-core/src/test/resources/org/languagetool/rules/xx/grammar-withPrio.xml b/languagetool-core/src/test/resources/org/languagetool/rules/xx/grammar-withPrio.xml new file mode 100644 index 000000000000..8b871f83aafd --- /dev/null +++ b/languagetool-core/src/test/resources/org/languagetool/rules/xx/grammar-withPrio.xml @@ -0,0 +1,86 @@ + + + + + + + + + + fake1 + + msg1 + + fake1 + + fake2 + + + + fake1 + + msg3 + + fake1 + + fake2 + + + + + + fake1 + + msg3 + + fake1 + + fake2 + + + + + + + + fake1 + + msg1 + + fake1 + + fake2 + + + + + fake1 + + msg1 + + fake1 + + fake2 + + + diff --git a/languagetool-language-modules/ca/src/main/java/org/languagetool/language/Catalan.java b/languagetool-language-modules/ca/src/main/java/org/languagetool/language/Catalan.java index 6de38013fc39..87a7ac6cf041 100644 --- a/languagetool-language-modules/ca/src/main/java/org/languagetool/language/Catalan.java +++ b/languagetool-language-modules/ca/src/main/java/org/languagetool/language/Catalan.java @@ -189,21 +189,7 @@ public LanguageMaintainedState getMaintainedState() { @Override public int getRulePriority(Rule rule) { - int categoryPriority = this.getPriorityForId(rule.getCategory().getId().toString()); - int rulePriority = this.getPriorityForId(rule.getId()); - // if there is a priority defined for the rule, - // it takes precedence over category priority - if (rulePriority != 0) { - return rulePriority; - } - if (categoryPriority != 0) { - return categoryPriority; - } - if (rule.getLocQualityIssueType().equals(ITSIssueType.Style)) { - // don't let style issues hide more important errors - return -50; - } - return 0; + return rule.getLocQualityIssueType().equals(ITSIssueType.Style) ? -50 : super.getRulePriority(rule); } @Override diff --git a/languagetool-language-modules/en/src/main/java/org/languagetool/language/English.java b/languagetool-language-modules/en/src/main/java/org/languagetool/language/English.java index 87560e415f15..e503c63a0565 100644 --- a/languagetool-language-modules/en/src/main/java/org/languagetool/language/English.java +++ b/languagetool-language-modules/en/src/main/java/org/languagetool/language/English.java @@ -295,21 +295,7 @@ public void close() throws Exception { @Override public int getRulePriority(Rule rule) { - int categoryPriority = this.getPriorityForId(rule.getCategory().getId().toString()); - int rulePriority = this.getPriorityForId(rule.getId()); - // if there is a priority defined for the rule, - // it takes precedence over category priority - if (rulePriority != 0) { - return rulePriority; - } - if (categoryPriority != 0) { - return categoryPriority; - } - if (rule.getLocQualityIssueType().equals(ITSIssueType.Style)) { - // don't let style issues hide more important errors - return -50; - } - return 0; + return rule.getLocQualityIssueType().equals(ITSIssueType.Style) ? -50 : super.getRulePriority(rule); } private final static Map id2prio = new HashMap<>();