<commit_before>/* LanguageTool, a natural language style checker
 * Copyright (C) 2012 <NAME> (http://www.danielnaber.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package org.languagetool.rules.patterns;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ObjectUtils;
import org.languagetool.Languages;
import org.languagetool.rules.*;
import org.languagetool.tagging.disambiguation.rules.DisambiguationPatternRule;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

public class PatternRuleHandler extends XMLRuleHandler {

  public static final String TYPE = "type";

  static final String MARKER_TAG = "<marker>";
  static final String RAW_TAG = "raw_pos";
  static final String PLEASE_SPELL_ME = "<pleasespellme/>";

  private static final String EXTERNAL = "external";

  protected final String sourceFile;

  protected Category category;
  protected String categoryIssueType;
  protected String ruleGroupIssueType;
  protected String ruleIssueType;
  protected String name;
  protected String filterClassName;
  protected String filterArgs;

  private final List<DisambiguationPatternRule> rulegroupAntiPatterns = new ArrayList<>();
  private final List<DisambiguationPatternRule> ruleAntiPatterns = new ArrayList<>();

  private int subId;
  private boolean interpretPosTagsPreDisambiguation;

  private boolean defaultOff;
  private boolean defaultTempOff;
  private boolean ruleGroupDefaultOff;
  private boolean ruleGroupDefaultTempOff;

  private String ruleGroupDescription;
  private int startPos = -1;
  private int endPos = -1;
  private int tokenCountForMarker;

  private int antiPatternCounter;

  private boolean inRule;

  private boolean relaxedMode = false;
  private boolean inAntiPattern;

  private String idPrefix;

  public PatternRuleHandler() {
    this.sourceFile = null;
  }

  public PatternRuleHandler(String sourceFile) {
    this.sourceFile = sourceFile;
  }

  /**
   * If set to true, don't throw an exception if id or name is not set.
   * Used for online rule editor.
   * @since 2.1
   */
  void setRelaxedMode(boolean relaxedMode) {
    this.relaxedMode = relaxedMode;
  }

  // ===========================================================
  // SAX DocumentHandler methods
  // ===========================================================

  @Override
  public void startElement(String namespaceURI, String lName,
                           String qName, Attributes attrs) throws SAXException {
    switch (qName) {
      case "category":
        String catName = attrs.getValue(NAME);
        String catId = attrs.getValue(ID);
        Category.Location location = YES.equals(attrs.getValue(EXTERNAL)) ?
                Category.Location.EXTERNAL : Category.Location.INTERNAL;
        boolean onByDefault = !OFF.equals(attrs.getValue(DEFAULT));
        String tabName = attrs.getValue(TABNAME);
        category = new Category(catId != null ? new CategoryId(catId) : null, catName, location, onByDefault, tabName);
        if (attrs.getValue(TYPE) != null) {
          categoryIssueType = attrs.getValue(TYPE);
        }
        break;
      case "rules":
        String languageStr = attrs.getValue("lang");
        idPrefix = attrs.getValue("idprefix");
        language = Languages.getLanguageForShortCode(languageStr);
        break;
      case "regexp":
        inRegex = true;
        regexMode = "exact".equals(attrs.getValue("type")) ? RegexpMode.EXACT : RegexpMode.SMART;
        regexCaseSensitive = attrs.getValue(CASE_SENSITIVE) != null && YES.equals(attrs.getValue(CASE_SENSITIVE));
        regexpMark = attrs.getValue(MARK) != null ? Integer.parseInt(attrs.getValue(MARK)) : 0;
        break;
      case RULE:
        regex = new StringBuilder();
        inRule = true;
        shortMessage = new StringBuilder();
        message = new StringBuilder();
        suggestionsOutMsg = new StringBuilder();
        url = new StringBuilder();
        id = attrs.getValue(ID);
        name = attrs.getValue(NAME);
        if (inRuleGroup) {
          subId++;
          if (id == null) {
            id = ruleGroupId;
          }
          if (name == null) {
            name = ruleGroupDescription;
          }
        }
        if (id == null) {
          throw new RuntimeException("id is null for rule with name '" + name + "'");
        }
        id = idPrefix != null ? idPrefix + id : id;

        if (inRuleGroup && ruleGroupDefaultOff && attrs.getValue(DEFAULT) != null) {
          throw new RuntimeException("Rule group " + ruleGroupId + " is off by default, thus rule " + id + " cannot specify 'default=...'");
        }
        if (inRuleGroup && ruleGroupDefaultTempOff && attrs.getValue(DEFAULT) != null) {
          throw new RuntimeException("Rule group " + ruleGroupId + " is off by default, thus rule " + id + " cannot specify 'default=...'");
        }
        if (inRuleGroup && ruleGroupDefaultOff) {
          defaultOff = true;
        } else if (inRuleGroup && ruleGroupDefaultTempOff) {
          defaultTempOff = true;
        } else {
          defaultOff = OFF.equals(attrs.getValue(DEFAULT));
          defaultTempOff = TEMP_OFF.equals(attrs.getValue(DEFAULT));
        }

        correctExamples = new ArrayList<>();
        incorrectExamples = new ArrayList<>();
        errorTriggeringExamples = new ArrayList<>();
        suggestionMatches.clear();
        suggestionMatchesOutMsg.clear();
        if (attrs.getValue(TYPE) != null) {<commit_msg>don't require 'id' in relaxedMode (used by online rule editor)<commit_after>/* LanguageTool, a natural language style checker
 * Copyright (C) 2012 <NAME> (http://www.danielnaber.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package org.languagetool.rules.patterns;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ObjectUtils;
import org.languagetool.Languages;
import org.languagetool.rules.*;
import org.languagetool.tagging.disambiguation.rules.DisambiguationPatternRule;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

public class PatternRuleHandler extends XMLRuleHandler {

  public static final String TYPE = "type";

  static final String MARKER_TAG = "<marker>";
  static final String RAW_TAG = "raw_pos";
  static final String PLEASE_SPELL_ME = "<pleasespellme/>";

  private static final String EXTERNAL = "external";

  protected final String sourceFile;

  protected Category category;
  protected String categoryIssueType;
  protected String ruleGroupIssueType;
  protected String ruleIssueType;
  protected String name;
  protected String filterClassName;
  protected String filterArgs;

  private final List<DisambiguationPatternRule> rulegroupAntiPatterns = new ArrayList<>();
  private final List<DisambiguationPatternRule> ruleAntiPatterns = new ArrayList<>();

  private int subId;
  private boolean interpretPosTagsPreDisambiguation;

  private boolean defaultOff;
  private boolean defaultTempOff;
  private boolean ruleGroupDefaultOff;
  private boolean ruleGroupDefaultTempOff;

  private String ruleGroupDescription;
  private int startPos = -1;
  private int endPos = -1;
  private int tokenCountForMarker;

  private int antiPatternCounter;

  private boolean inRule;

  private boolean relaxedMode = false;
  private boolean inAntiPattern;

  private String idPrefix;

  public PatternRuleHandler() {
    this.sourceFile = null;
  }

  public PatternRuleHandler(String sourceFile) {
    this.sourceFile = sourceFile;
  }

  /**
   * If set to true, don't throw an exception if id or name is not set.
   * Used for online rule editor.
   * @since 2.1
   */
  void setRelaxedMode(boolean relaxedMode) {
    this.relaxedMode = relaxedMode;
  }

  // ===========================================================
  // SAX DocumentHandler methods
  // ===========================================================

  @Override
  public void startElement(String namespaceURI, String lName,
                           String qName, Attributes attrs) throws SAXException {
    switch (qName) {
      case "category":
        String catName = attrs.getValue(NAME);
        String catId = attrs.getValue(ID);
        Category.Location location = YES.equals(attrs.getValue(EXTERNAL)) ?
                Category.Location.EXTERNAL : Category.Location.INTERNAL;
        boolean onByDefault = !OFF.equals(attrs.getValue(DEFAULT));
        String tabName = attrs.getValue(TABNAME);
        category = new Category(catId != null ? new CategoryId(catId) : null, catName, location, onByDefault, tabName);
        if (attrs.getValue(TYPE) != null) {
          categoryIssueType = attrs.getValue(TYPE);
        }
        break;
      case "rules":
        String languageStr = attrs.getValue("lang");
        idPrefix = attrs.getValue("idprefix");
        language = Languages.getLanguageForShortCode(languageStr);
        break;
      case "regexp":
        inRegex = true;
        regexMode = "exact".equals(attrs.getValue("type")) ? RegexpMode.EXACT : RegexpMode.SMART;
        regexCaseSensitive = attrs.getValue(CASE_SENSITIVE) != null && YES.equals(attrs.getValue(CASE_SENSITIVE));
        regexpMark = attrs.getValue(MARK) != null ? Integer.parseInt(attrs.getValue(MARK)) : 0;
        break;
      case RULE:
        regex = new StringBuilder();
        inRule = true;
        shortMessage = new StringBuilder();
        message = new StringBuilder();
        suggestionsOutMsg = new StringBuilder();
        url = new StringBuilder();
        id = attrs.getValue(ID);
        name = attrs.getValue(NAME);
        if (inRuleGroup) {
          subId++;
          if (id == null) {
            id = ruleGroupId;
          }
          if (name == null) {
            name = ruleGroupDescription;
          }
        }
        if (id == null && !relaxedMode) {
          throw new RuntimeException("id is null for rule with name '" + name + "'");
        }
        id = idPrefix != null ? idPrefix + id : id;

        if (inRuleGroup && ruleGroupDefaultOff && attrs.getValue(DEFAULT) != null) {
          throw new RuntimeException("Rule group " + ruleGroupId + " is off by default, thus rule " + id + " cannot specify 'default=...'");
        }
        if (inRuleGroup && ruleGroupDefaultTempOff && attrs.getValue(DEFAULT) != null) {
          throw new RuntimeException("Rule group " + ruleGroupId + " is off by default, thus rule " + id + " cannot specify 'default=...'");
        }
        if (inRuleGroup && ruleGroupDefaultOff) {
          defaultOff = true;
        } else if (inRuleGroup && ruleGroupDefaultTempOff) {
          defaultTempOff = true;
        } else {
          defaultOff = OFF.equals(attrs.getValue(DEFAULT));
          defaultTempOff = TEMP_OFF.equals(attrs.getValue(DEFAULT));
        }

        correctExamples = new ArrayList<>();
        incorrectExamples = new ArrayList<>();
        errorTriggeringExamples = new ArrayList<>();
        suggestionMatches.clear();
        suggestionMatchesOutMsg.clear();
        if (attrs.getValue(TYPE) != null) {
{'fraction_non_alphanumeric': 0.11202895130202187, 'fraction_numerical': 0.0036189127527338524, 'mean_word_length': 3.184331797235023, 'pattern_counts': {'":': 6, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3877153', 'n_tokens_mistral': 3435, 'n_tokens_neox': 3345, 'n_words': 1046}