aboutsummaryrefslogtreecommitdiffstats
path: root/messagebus/src/main/java/com/yahoo/messagebus/routing/Route.java
blob: 7bf79a7b656b520f589a54cb9f4c2eb13c55a7ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.messagebus.routing;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>A route is a list of {@link Hop hops} that are resolved from first to last
 * as a routable moves from source to destination. A route may be changed at any
 * time be either application logic or an invoked {@link RoutingPolicy}, so no
 * guarantees on actual path can be given without the full knowledge of all that
 * logic.</p>
 *
 * <p>To construct a route you may either use the factory method {@link
 * #parse(String)} to produce a route instance from a string representation, or
 * you may build one programatically through the hop accessors.</p>
 *
 * @author bratseth
 * @author Simon Thoresen Hult
 */
public class Route {

    private final List<Hop> hops = new ArrayList<>();
    private String cache = null;

    /**
     * <p>Creates an empty route that contains no hops.</p>
     */
    public Route() {
        // empty
    }

    /**
     * <p>The copy constructor ignores integrity, it simply duplicates the list
     * of hops in the other route. If that route is illegal, then so is
     * this.</p>
     *
     * @param route The route to copy.
     */
    public Route(Route route) {
        this(route.hops);
        cache = route.cache;
    }

    private void setRaw(String s) {
        cache = s;
    }

    /**
     * Parses the given string as a list of space-separated hops. The {@link #toString()}
     * method is compatible with this parser.
     *
     * @param str the string to parse
     * @return a route that corresponds to the string
     */
    public static Route parse(String str) {
        if (str == null || str.length() == 0) {
            return new Route().addHop(new Hop().addDirective(new ErrorDirective("Failed to parse empty string.")));
        }
        RouteParser parser = new RouteParser(str);
        Route route = parser.route();
        route.setRaw(str);
        return route;
    }

    /**
     * <p>Constructs a route based on a list of hops.</p>
     *
     * @param hops The hops to be contained in this.
     */
    public Route(List<Hop> hops) {
        this.hops.addAll(hops);
    }

    /**
     * <p>Returns whether or not there are any hops in this route.</p>
     *
     * @return True if there is at least one hop.
     */
    public boolean hasHops() {
        return !hops.isEmpty();
    }

    /**
     * <p>Returns the number of hops that make up this route.</p>
     *
     * @return The number of hops.
     */
    public int getNumHops() {
        return hops.size();
    }

    /**
     * <p>Returns the hop at the given index.</p>
     *
     * @param i The index of the hop to return.
     * @return The hop.
     */
    public Hop getHop(int i) {
        return hops.get(i);
    }

    /**
     * <p>Adds a hop to the list of hops that make up this route.</p>
     *
     * @param hop The hop to add.
     * @return This, to allow chaining.
     */
    public Route addHop(Hop hop) {
        cache = null;
        hops.add(hop);
        return this;
    }

    /**
     * <p>Sets the hop at a given index.</p>
     *
     * @param i   The index at which to set the hop.
     * @param hop The hop to set.
     * @return This, to allow chaining.
     */
    public Route setHop(int i, Hop hop) {
        cache = null;
        hops.set(i, hop);
        return this;
    }

    /**
     * <p>Removes the hop at a given index.</p>
     *
     * @param i The index of the hop to remove.
     * @return The hop removed.
     */
    public Hop removeHop(int i) {
        cache = null;
        return hops.remove(i);
    }

    /**
     * <p>Clears the list of hops that make up this route.</p>
     *
     * @return This, to allow chaining.
     */
    public Route clearHops() {
        cache = null;
        hops.clear();
        return this;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Route)) {
            return false;
        }
        Route rhs = (Route)obj;
        if (hops.size() != rhs.hops.size()) {
            return false;
        }
        for (int i = 0; i < hops.size(); ++i) {
            if (!hops.get(i).equals(rhs.hops.get(i))) {
                return false;
            }
        }
        return true;
    }

    @Override
    public String toString() {
        if (cache == null) {
            StringBuilder ret = new StringBuilder("");
            for (int i = 0; i < hops.size(); ++i) {
                ret.append(hops.get(i));
                if (i < hops.size() - 1) {
                    ret.append(" ");
                }
            }
            cache = ret.toString();
        }
        return cache;
    }

    /**
     * <p>Returns a string representation of this that can be debugged but not
     * parsed.</p>
     *
     * @return The debug string.
     */
    public String toDebugString() {
        StringBuilder ret = new StringBuilder("Route(hops = { ");
        for (int i = 0; i < hops.size(); ++i) {
            ret.append(hops.get(i).toDebugString());
            if (i < hops.size() - 1) {
                ret.append(", ");
            }
        }
        ret.append(" })");
        return ret.toString();
    }

    @Override
    public int hashCode() {
        int result = hops != null ? hops.hashCode() : 0;
        result = 31 * result + (cache != null ? cache.hashCode() : 0);
        return result;
    }
}