<commit_before>/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.xpack.ml.job.config;

import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.ml.job.messages.Messages;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public class DetectionRule extends ToXContentToBytes implements Writeable {

    public static final ParseField DETECTION_RULE_FIELD = new ParseField("detection_rule");
    public static final ParseField RULE_ACTION_FIELD = new ParseField("rule_action");
    public static final ParseField TARGET_FIELD_NAME_FIELD = new ParseField("target_field_name");
    public static final ParseField TARGET_FIELD_VALUE_FIELD = new ParseField("target_field_value");
    public static final ParseField CONDITIONS_CONNECTIVE_FIELD = new ParseField("conditions_connective");
    public static final ParseField RULE_CONDITIONS_FIELD = new ParseField("rule_conditions");

    public static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>(DETECTION_RULE_FIELD.getPreferredName(), Builder::new);

    static {
        PARSER.declareField(Builder::setRuleAction, p -> {
            if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
                return RuleAction.fromString(p.text());
            }
            throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
        }, RULE_ACTION_FIELD, ValueType.STRING);
        PARSER.declareString(Builder::setTargetFieldName, TARGET_FIELD_NAME_FIELD);
        PARSER.declareString(Builder::setTargetFieldValue, TARGET_FIELD_VALUE_FIELD);
        PARSER.declareField(Builder::setConditionsConnective, p -> {
            if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
                return Connective.fromString(p.text());
            }
            throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
        }, CONDITIONS_CONNECTIVE_FIELD, ValueType.STRING);
        PARSER.declareObjectArray(Builder::setRuleConditions,
                (parser, parseFieldMatcher) -> RuleCondition.PARSER.apply(parser, parseFieldMatcher), RULE_CONDITIONS_FIELD);
    }

    private final RuleAction ruleAction;
    private final String targetFieldName;
    private final String targetFieldValue;
    private final Connective conditionsConnective;
    private final List<RuleCondition> ruleConditions;

    private DetectionRule(RuleAction ruleAction, @Nullable String targetFieldName, @Nullable String targetFieldValue,
                          Connective conditionsConnective, List<RuleCondition> ruleConditions) {
        this.ruleAction = Objects.requireNonNull(ruleAction);
        this.targetFieldName = targetFieldName;
        this.targetFieldValue = targetFieldValue;
        this.conditionsConnective = Objects.requireNonNull(conditionsConnective);
        this.ruleConditions = Collections.unmodifiableList(ruleConditions);
    }

    public DetectionRule(StreamInput in) throws IOException {
        ruleAction = RuleAction.readFromStream(in);
        conditionsConnective = Connective.readFromStream(in);
        int size = in.readVInt();
        ruleConditions = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            ruleConditions.add(new RuleCondition(in));
        }
        targetFieldName = in.readOptionalString();
        targetFieldValue = in.readOptionalString();
    }

    @Override
    public void writeTo(StreamOutput out) throws IOException {
        ruleAction.writeTo(out);
        conditionsConnective.writeTo(out);
        out.writeVInt(ruleConditions.size());
        for (RuleCondition condition : ruleConditions) {
            condition.writeTo(out);
        }
        out.writeOptionalString(targetFieldName);
        out.writeOptionalString(targetFieldValue);
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject();
        builder.field(RULE_ACTION_FIELD.getPreferredName(), ruleAction);
        builder.field(CONDITIONS_CONNECTIVE_FIELD.getPreferredName(), conditionsConnective);
        if (targetFieldName != null) {
            builder.field(TARGET_FIELD_NAME_FIELD.getPreferredName(), targetFieldName);
        }
        if (targetFieldValue != null) {
            builder.field(TARGET_FIELD_VALUE_FIELD.getPreferredName(), targetFieldValue);
        }
        builder.field(RULE_CONDITIONS_FIELD.getPreferredName(), ruleConditions);
        builder.endObject();
        return builder;
    }<commit_msg>[TEST] Change the serialization order back to keep tests happy<commit_after>/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.xpack.ml.job.config;

import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.ml.job.messages.Messages;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public class DetectionRule extends ToXContentToBytes implements Writeable {

    public static final ParseField DETECTION_RULE_FIELD = new ParseField("detection_rule");
    public static final ParseField RULE_ACTION_FIELD = new ParseField("rule_action");
    public static final ParseField TARGET_FIELD_NAME_FIELD = new ParseField("target_field_name");
    public static final ParseField TARGET_FIELD_VALUE_FIELD = new ParseField("target_field_value");
    public static final ParseField CONDITIONS_CONNECTIVE_FIELD = new ParseField("conditions_connective");
    public static final ParseField RULE_CONDITIONS_FIELD = new ParseField("rule_conditions");

    public static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>(DETECTION_RULE_FIELD.getPreferredName(), Builder::new);

    static {
        PARSER.declareField(Builder::setRuleAction, p -> {
            if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
                return RuleAction.fromString(p.text());
            }
            throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
        }, RULE_ACTION_FIELD, ValueType.STRING);
        PARSER.declareString(Builder::setTargetFieldName, TARGET_FIELD_NAME_FIELD);
        PARSER.declareString(Builder::setTargetFieldValue, TARGET_FIELD_VALUE_FIELD);
        PARSER.declareField(Builder::setConditionsConnective, p -> {
            if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
                return Connective.fromString(p.text());
            }
            throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
        }, CONDITIONS_CONNECTIVE_FIELD, ValueType.STRING);
        PARSER.declareObjectArray(Builder::setRuleConditions,
                (parser, parseFieldMatcher) -> RuleCondition.PARSER.apply(parser, parseFieldMatcher), RULE_CONDITIONS_FIELD);
    }

    private final RuleAction ruleAction;
    private final String targetFieldName;
    private final String targetFieldValue;
    private final Connective conditionsConnective;
    private final List<RuleCondition> ruleConditions;

    private DetectionRule(RuleAction ruleAction, @Nullable String targetFieldName, @Nullable String targetFieldValue,
                          Connective conditionsConnective, List<RuleCondition> ruleConditions) {
        this.ruleAction = Objects.requireNonNull(ruleAction);
        this.targetFieldName = targetFieldName;
        this.targetFieldValue = targetFieldValue;
        this.conditionsConnective = Objects.requireNonNull(conditionsConnective);
        this.ruleConditions = Collections.unmodifiableList(ruleConditions);
    }

    public DetectionRule(StreamInput in) throws IOException {
        ruleAction = RuleAction.readFromStream(in);
        conditionsConnective = Connective.readFromStream(in);
        int size = in.readVInt();
        ruleConditions = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            ruleConditions.add(new RuleCondition(in));
        }
        targetFieldName = in.readOptionalString();
        targetFieldValue = in.readOptionalString();
    }

    @Override
    public void writeTo(StreamOutput out) throws IOException {
        ruleAction.writeTo(out);
        conditionsConnective.writeTo(out);
        out.writeVInt(ruleConditions.size());
        for (RuleCondition condition : ruleConditions) {
            condition.writeTo(out);
        }
        out.writeOptionalString(targetFieldName);
        out.writeOptionalString(targetFieldValue);
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject();
        builder.field(RULE_ACTION_FIELD.getPreferredName(), ruleAction);
        builder.field(CONDITIONS_CONNECTIVE_FIELD.getPreferredName(), conditionsConnective);
        builder.field(RULE_CONDITIONS_FIELD.getPreferredName(), ruleConditions);
        if (targetFieldName != null) {
            builder.field(TARGET_FIELD_NAME_FIELD.getPreferredName(), targetFieldName);
        }
        if (targetFieldValue != null) {
            builder.field(TARGET_FIELD_VALUE_FIELD.getPreferredName(), targetFieldValue);
        }
        builder.endObject();
        return builder;
    }
{'fraction_non_alphanumeric': 0.089369105393279, 'fraction_numerical': 0.0001831334126911455, 'mean_word_length': 3.9645454545454544, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '6543889', 'n_tokens_mistral': 2851, 'n_tokens_neox': 2860, 'n_words': 585}