001package io.prometheus.metrics.model.snapshots;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.Collection;
006import java.util.Collections;
007import java.util.Iterator;
008import java.util.List;
009
010/**
011 * Immutable container for Exemplars.
012 * <p>
013 * This is currently backed by a {@code List<Exemplar>}. May be refactored later to use a more efficient data structure.
014 */
015public class Exemplars implements Iterable<Exemplar> {
016
017    /**
018     * EMPTY means no Exemplars.
019     */
020    public static final Exemplars EMPTY = new Exemplars(Collections.emptyList());
021    private final List<Exemplar> exemplars;
022
023    private Exemplars(Collection<Exemplar> exemplars) {
024        this.exemplars = Collections.unmodifiableList(new ArrayList<>(exemplars));
025    }
026
027    /**
028     * Create a new Exemplars instance.
029     * You can either create Exemplars with one of the static {@code Exemplars.of(...)} methods,
030     * or you can use the {@link Exemplars#builder()}.
031     *
032     * @param exemplars a copy of the exemplars collection will be created.
033     */
034    public static Exemplars of(Collection<Exemplar> exemplars) {
035        return new Exemplars(exemplars);
036    }
037
038    /**
039     * Create a new Exemplars instance.
040     * You can either create Exemplars with one of the static {@code Exemplars.of(...)} methods,
041     * or you can use the {@link Exemplars#builder()}.
042     *
043     * @param exemplars a copy of the exemplars array will be created.
044     */
045    public static Exemplars of(Exemplar... exemplars) {
046        return new Exemplars(Arrays.asList(exemplars));
047    }
048
049    @Override
050    public Iterator<Exemplar> iterator() {
051        return exemplars.iterator();
052    }
053
054    public int size() {
055        return exemplars.size();
056    }
057
058    public Exemplar get(int index) {
059        return exemplars.get(index);
060    }
061
062    /**
063     * This is used by classic histograms to find an exemplar with a value between lowerBound and upperBound.
064     */
065    public Exemplar get(double lowerBound, double upperBound) {
066        for (int i = 0; i < exemplars.size(); i++) {
067            Exemplar exemplar = exemplars.get(i);
068            double value = exemplar.getValue();
069            if (value > lowerBound && value <= upperBound) {
070                return exemplar;
071            }
072        }
073        return null;
074    }
075
076    /**
077     * Find the Exemplar with the newest timestamp. May return {@code null}.
078     */
079    public Exemplar getLatest() {
080        Exemplar latest = null;
081        for (int i=0; i<exemplars.size(); i++) {
082            Exemplar candidate = exemplars.get(i);
083            if (candidate == null) {
084                continue;
085            }
086            if (latest == null) {
087                latest = candidate;
088                continue;
089            }
090            if (!latest.hasTimestamp()) {
091                latest = candidate;
092                continue;
093            }
094            if (candidate.hasTimestamp()) {
095                if (latest.getTimestampMillis() < candidate.getTimestampMillis()) {
096                    latest = candidate;
097                }
098            }
099        }
100        return latest;
101    }
102
103    public static Builder builder() {
104        return new Builder();
105    }
106
107    public static class Builder {
108
109        private final ArrayList<Exemplar> exemplars = new ArrayList<>();
110
111        private Builder() {
112        }
113
114        /**
115         * Add an exemplar. This can be called multiple times to add multiple exemplars.
116         */
117        public Builder exemplar(Exemplar exemplar) {
118            exemplars.add(exemplar);
119            return this;
120        }
121
122        /**
123         * Add all exemplars form the collection.
124         */
125        public Builder exemplars(Collection<Exemplar> exemplars) {
126            this.exemplars.addAll(exemplars);
127            return this;
128        }
129
130        public Exemplars build() {
131            return Exemplars.of(exemplars);
132        }
133    }
134}