-
Notifications
You must be signed in to change notification settings - Fork 215
/
DefaultMetadataHeaderKey.java
173 lines (149 loc) · 5.71 KB
/
DefaultMetadataHeaderKey.java
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
/*
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.base.model.headers.metadata;
import static org.eclipse.ditto.base.model.common.ConditionChecker.argumentNotEmpty;
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.text.MessageFormat;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.eclipse.ditto.json.JsonKey;
import org.eclipse.ditto.json.JsonPointer;
/**
* Default implementation of {@link org.eclipse.ditto.base.model.headers.metadata.MetadataHeaderKey}.
*
* @since 1.2.0
*/
@Immutable
final class DefaultMetadataHeaderKey implements MetadataHeaderKey {
static final JsonKey HIERARCHY_WILDCARD = JsonKey.of("*");
private final JsonPointer path;
private DefaultMetadataHeaderKey(final JsonPointer path) {
this.path = path;
}
/**
* Parses the given char sequence to obtain an instance of DefaultMetadataHeaderKey.
*
* @param key the key to be parsed.
* @return the MetadataHeaderKey.
* @throws NullPointerException if {@code key} is {@code null}.
* @throws IllegalArgumentException if {@code key}
* <ul>
* <li>is empty,</li>
* <li>starts with an asterisk ({@code *}) and has not exactly two levels,</li>
* <li>contains an asterisk at any level but the first.</li>
* </ul>
*/
public static DefaultMetadataHeaderKey parse(final CharSequence key) {
return of(JsonPointer.of(argumentNotEmpty(key, "key")));
}
/**
* Returns an instance of DefaultMetadataHeaderKey.
*
* @param path the path of the key.
* @return the instance.
* @throws NullPointerException if {@code path} is {@code null}.
* @throws IllegalArgumentException if {@code key}
* <ul>
* <li>is empty,</li>
* <li>starts with an asterisk ({@code *}) and has not exactly two levels,</li>
* <li>contains an asterisk at any level but the first.</li>
* </ul>
*/
public static DefaultMetadataHeaderKey of(final JsonPointer path) {
final DefaultMetadataHeaderKey result = new DefaultMetadataHeaderKey(checkNotNull(path, "path"));
result.validate();
return result;
}
private void validate() {
if (path.isEmpty()) {
throw new IllegalArgumentException("The path of a metadata header key must not be empty!");
}
if (appliesToAllLeaves() && 2 != path.getLevelCount()) {
throw new IllegalArgumentException(MessageFormat.format(
"A wildcard path of a metadata header key must have exactly two levels but it had <{0}>!",
path.getLevelCount()));
}
}
@Override
public boolean appliesToAllLeaves() {
return path.getRoot()
.filter(HIERARCHY_WILDCARD::equals)
.isPresent();
}
@Override
public JsonPointer getPath() {
final JsonPointer result;
if (appliesToAllLeaves()) {
// The sub-pointer consists only of the leaf in this case. This is guaranteed by validation.
result = path.getSubPointer(1).orElseThrow(NoSuchElementException::new);
} else {
result = path;
}
return result;
}
@Override
public int compareTo(final MetadataHeaderKey other) {
checkNotNull(other, "other");
final int result;
if (equals(other)) {
result = 0;
} else if (appliesToAllLeaves()) {
if (other.appliesToAllLeaves()) {
result = comparePaths(other.getPath());
} else {
// This path has a wildcard, the other path is specific.
// This path has to be less than the other one in order to let the value of a specific path overwrite
// the value of a wildcard path.
result = -1;
}
} else {
if (!other.appliesToAllLeaves()) {
result = comparePaths(other.getPath());
} else {
// This path is specific, the other path has a wildcard.
// This path has to be greater than the other one in order to let the value of a specific path overwrite
// the value of a wildcard path.
result = 1;
}
}
return result;
}
private int comparePaths(final JsonPointer otherPath) {
final Comparator<JsonPointer> jsonPointerComparator = Comparator.comparing(JsonPointer::toString);
// String comparison returns the difference of the length of the strings.
// This operation normalizes the string comparison result to either -1, 0 or 1 to facilitate testing.
return Integer.compare(jsonPointerComparator.compare(path, otherPath), 0);
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DefaultMetadataHeaderKey that = (DefaultMetadataHeaderKey) o;
return Objects.equals(path, that.path);
}
@Override
public int hashCode() {
return Objects.hash(path);
}
@Override
public String toString() {
return path.toString();
}
}