All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aliyun.openservices.shade.io.prometheus.client.exemplars.Exemplar Maven / Gradle / Ivy

There is a newer version: 1.9.4.Final
Show newest version
package com.aliyun.openservices.shade.io.prometheus.client.exemplars;

import java.util.Arrays;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Immutable data class holding an Exemplar.
 */
public class Exemplar {

  private final String[] labels;
  private final double value;
  private final Long timestampMs;

  private static final Pattern labelNameRegex = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");

  /**
   * Create an Exemplar without a timestamp
   *
   * @param value  the observed value
   * @param labels name/value pairs. Expecting an even number of strings. The combined length of the label names and
   *               values must not exceed 128 UTF-8 characters. Neither a label name nor a label value may be null.
   */
  public Exemplar(double value, String... labels) {
    this(value, null, labels);
  }

  /**
   * Create an Exemplar
   *
   * @param value       the observed value
   * @param timestampMs as in {@link System#currentTimeMillis()}
   * @param labels      name/value pairs. Expecting an even number of strings. The combined length of the
   *                    label names and values must not exceed 128 UTF-8 characters. Neither a label name
   *                    nor a label value may be null.
   */
  public Exemplar(double value, Long timestampMs, String... labels) {
    this.labels = sortedCopy(labels);
    this.value = value;
    this.timestampMs = timestampMs;
  }

  /**
   * Create an Exemplar
   *
   * @param value  the observed value
   * @param labels the labels. Must not be null. The combined length of the label names and values must not exceed
   *               128 UTF-8 characters. Neither a label name nor a label value may be null.
   */
  public Exemplar(double value, Map labels) {
    this(value, null, mapToArray(labels));
  }

  /**
   * Create an Exemplar
   *
   * @param value       the observed value
   * @param timestampMs as in {@link System#currentTimeMillis()}
   * @param labels      the labels. Must not be null. The combined length of the label names and values must not exceed
   *                    128 UTF-8 characters. Neither a label name nor a label value may be null.
   */
  public Exemplar(double value, Long timestampMs, Map labels) {
    this(value, timestampMs, mapToArray(labels));
  }

  public int getNumberOfLabels() {
    return labels.length / 2;
  }

  /**
   * Get the label name at index {@code i}.
   * @param i the index, must be >= 0 and < {@link #getNumberOfLabels()}.
   * @return the label name at index {@code i}
   */
  public String getLabelName(int i) {
    return labels[2 * i];
  }

  /**
   * Get the label value at index {@code i}.
   * @param i the index, must be >= 0 and < {@link #getNumberOfLabels()}.
   * @return the label value at index {@code i}
   */
  public String getLabelValue(int i) {
    return labels[2 * i + 1];
  }

  public double getValue() {
    return value;
  }

  /**
   * @return Unix timestamp or {@code null} if no timestamp is present.
   */
  public Long getTimestampMs() {
    return timestampMs;
  }

  private String[] sortedCopy(String... labels) {
    if (labels.length % 2 != 0) {
      throw new IllegalArgumentException("labels are name/value pairs, expecting an even number");
    }
    String[] result = new String[labels.length];
    int charsTotal = 0;
    for (int i = 0; i < labels.length; i+=2) {
      if (labels[i] == null) {
        throw new IllegalArgumentException("labels[" + i + "] is null");
      }
      if (labels[i+1] == null) {
        throw new IllegalArgumentException("labels[" + (i+1) + "] is null");
      }
      if (!labelNameRegex.matcher(labels[i]).matches()) {
        throw new IllegalArgumentException(labels[i] + " is not a valid label name");
      }
      result[i] = labels[i]; // name
      result[i+1] = labels[i+1]; // value
      charsTotal += labels[i].length() + labels[i+1].length();
      // Move the current tuple down while the previous name is greater than current name.
      for (int j=i-2; j>=0; j-=2) {
        int compareResult = result[j+2].compareTo(result[j]);
        if (compareResult == 0) {
          throw new IllegalArgumentException(result[j] + ": label name is not unique");
        } else if (compareResult < 0) {
          String tmp = result[j];
          result[j] = result[j+2];
          result[j+2] = tmp;
          tmp = result[j+1];
          result[j+1] = result[j+3];
          result[j+3] = tmp;
        } else {
          break;
        }
      }
    }
    if (charsTotal > 128) {
      throw new IllegalArgumentException(
          "the combined length of the label names and values must not exceed 128 UTF-8 characters");
    }
    return result;
  }

  /**
   * Convert the map to an array {@code [key1, value1, key2, value2, ...]}.
   */
  public static String[] mapToArray(Map labelMap) {
    if (labelMap == null) {
      return null;
    }
    String[] result = new String[2 * labelMap.size()];
    int i = 0;
    for (Map.Entry entry : labelMap.entrySet()) {
      result[i] = entry.getKey();
      result[i + 1] = entry.getValue();
      i += 2;
    }
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof Exemplar)) {
      return false;
    }
    Exemplar other = (Exemplar) obj;
    return Arrays.equals(this.labels, other.labels) &&
        Double.compare(other.value, value) == 0 &&
        (timestampMs == null && other.timestampMs == null
            || timestampMs != null && timestampMs.equals(other.timestampMs));
  }

  @Override
  public int hashCode() {
    int hash = Arrays.hashCode(labels);
    long d = Double.doubleToLongBits(value);
    hash = 37 * hash + (int) (d ^ (d >>> 32));
    if (timestampMs != null) {
      hash = 37 * hash + timestampMs.intValue();
    }
    return hash;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy