blob: 0ef9aefaaa2fed19236ebba81de65d00b5b4b626 [file] [log] [blame]
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.store.service;
18
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070019import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
21import org.apache.commons.collections.CollectionUtils;
22import org.apache.commons.lang.StringUtils;
23
Jordan Haltermanb0ac5902017-07-30 12:31:01 -070024import java.util.ArrayList;
Madan Jampaniad5b8c72016-09-12 15:05:01 -070025import java.util.Arrays;
Madan Jampani98094222016-09-15 21:12:46 -070026import java.util.Collection;
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070027import java.util.Iterator;
28import java.util.List;
29import java.util.Objects;
30
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070031import static com.google.common.base.Preconditions.checkNotNull;
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070032
Madan Jampani5bdebd52016-09-07 16:18:12 -070033/**
34 * Unique key for nodes in the {@link DocumentTree}.
35 */
36public class DocumentPath implements Comparable<DocumentPath> {
37
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070038 /** Default path separator. */
39 public static final String DEFAULT_SEPARATOR = "|";
40
41 /** Default path separator regex. */
42 public static final String DEFAULT_SEPARATOR_RE = "\\|";
43
44 // TODO: Add means to set the path separator and separator ERE.
45 private static String pathSeparator = DEFAULT_SEPARATOR;
46 private static String pathSeparatorRE = DEFAULT_SEPARATOR_RE;
47
Jordan Haltermancb1e02c2017-08-25 16:20:43 -070048 /** Root document tree path. */
49 public static final DocumentPath ROOT = DocumentPath.from("root");
50
Madan Jampani5bdebd52016-09-07 16:18:12 -070051 private final List<String> pathElements = Lists.newArrayList();
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070052
53 /**
54 * Private utility constructor for internal generation of partial paths only.
55 *
Madan Jampani5bdebd52016-09-07 16:18:12 -070056 * @param pathElements list of path elements
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070057 */
Madan Jampani5bdebd52016-09-07 16:18:12 -070058 private DocumentPath(List<String> pathElements) {
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070059 checkNotNull(pathElements);
Madan Jampani5bdebd52016-09-07 16:18:12 -070060 this.pathElements.addAll(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070061 }
62
63 /**
Thomas Vachuskac3984c62016-09-07 17:51:45 -070064 * Constructs a new document path.
Madan Jampani5bdebd52016-09-07 16:18:12 -070065 * <p>
66 * New paths must contain at least one name and string names may NOT contain any period characters.
67 * If one field is {@code null} that field will be ignored.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070068 *
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070069 * @param nodeName the name of the last level of this path
Madan Jampani5bdebd52016-09-07 16:18:12 -070070 * @param parentPath the path representing the parent leading up to this
71 * node, in the case of the root this should be {@code null}
72 * @throws IllegalDocumentNameException if both parameters are null or name contains an illegal character ('.')
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070073 */
74 public DocumentPath(String nodeName, DocumentPath parentPath) {
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070075 checkNotNull(nodeName, "Node name cannot be null");
76 if (nodeName.contains(pathSeparator)) {
Yuta HIGUCHI153d3582017-09-01 16:59:52 -070077 throw new IllegalDocumentNameException("'" + pathSeparator + "'" +
78 " are not allowed in names.");
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070079 }
80 if (parentPath != null) {
Madan Jampani5bdebd52016-09-07 16:18:12 -070081 pathElements.addAll(parentPath.pathElements());
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070082 }
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070083 pathElements.add(nodeName);
Madan Jampani5bdebd52016-09-07 16:18:12 -070084 if (pathElements.isEmpty()) {
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070085 throw new IllegalDocumentNameException("A document path must contain at" +
86 "least one non-null" +
87 "element.");
88 }
89 }
90
91 /**
Madan Jampaniad5b8c72016-09-12 15:05:01 -070092 * Creates a new {@code DocumentPath} from a period delimited path string.
93 *
94 * @param path path string
95 * @return {@code DocumentPath} instance
96 */
97 public static DocumentPath from(String path) {
Thomas Vachuskae2bd1152017-03-23 13:42:32 -070098 return new DocumentPath(Arrays.asList(path.split(pathSeparatorRE)));
Madan Jampaniad5b8c72016-09-12 15:05:01 -070099 }
100
101 /**
Jordan Haltermanb0ac5902017-07-30 12:31:01 -0700102 * Creates a new {@code DocumentPath} from a list of path elements.
103 *
104 * @param elements path elements
105 * @return {@code DocumentPath} instance
106 */
107 public static DocumentPath from(String... elements) {
108 return from(Arrays.asList(elements));
109 }
110
111 /**
112 * Creates a new {@code DocumentPath} from a list of path elements.
113 *
114 * @param elements path elements
115 * @return {@code DocumentPath} instance
116 */
117 public static DocumentPath from(List<String> elements) {
118 return new DocumentPath(elements);
119 }
120
121 /**
122 * Creates a new {@code DocumentPath} from a list of path elements.
123 *
124 * @param elements path elements
125 * @param child child element
126 * @return {@code DocumentPath} instance
127 */
128 public static DocumentPath from(List<String> elements, String child) {
129 elements = new ArrayList<>(elements);
130 elements.add(child);
131 return from(elements);
132 }
133
134 /**
Sithara Punnassery589fac22016-10-03 11:51:53 -0700135 * Returns the relative path to the given node.
136 *
137 * @return relative path to the given node.
138 */
139 public DocumentPath childPath() {
140 if (pathElements.size() <= 1) {
141 return null;
142 }
143 return new DocumentPath(this.pathElements.subList(pathElements.size() - 1, pathElements.size()));
144 }
145 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -0700146 * Returns a path for the parent of this node.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700147 *
Madan Jampani5bdebd52016-09-07 16:18:12 -0700148 * @return parent node path. If this path is for the root, returns {@code null}.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700149 */
150 public DocumentPath parent() {
Madan Jampani5bdebd52016-09-07 16:18:12 -0700151 if (pathElements.size() <= 1) {
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700152 return null;
153 }
Madan Jampani5bdebd52016-09-07 16:18:12 -0700154 return new DocumentPath(this.pathElements.subList(0, pathElements.size() - 1));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700155 }
156
157 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -0700158 * Returns the list of path elements representing this path in correct
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700159 * order.
160 *
Madan Jampani5bdebd52016-09-07 16:18:12 -0700161 * @return a list of elements that make up this path
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700162 */
Madan Jampani5bdebd52016-09-07 16:18:12 -0700163 public List<String> pathElements() {
164 return ImmutableList.copyOf(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700165 }
166
Madan Jampani98094222016-09-15 21:12:46 -0700167 /**
168 * Returns if the specified path belongs to a direct ancestor of the node pointed at by this path.
169 * <p>
170 * Example: {@code root.a} is a direct ancestor of {@code r.a.b.c}; while {@code r.a.x} is not.
171 *
172 * @param other other path
173 * @return {@code true} is yes; {@code false} otherwise.
174 */
175 public boolean isAncestorOf(DocumentPath other) {
176 return !other.equals(this) && other.toString().startsWith(toString());
177 }
178
179 /**
180 * Returns if the specified path is belongs to a subtree rooted this path.
181 * <p>
182 * Example: {@code root.a.b} and {@code root.a.b.c.d.e} are descendants of {@code r.a.b};
183 * while {@code r.a.x.c} is not.
184 *
185 * @param other other path
186 * @return {@code true} is yes; {@code false} otherwise.
187 */
188 public boolean isDescendentOf(DocumentPath other) {
189 return other.equals(this) || other.isAncestorOf(this);
190 }
191
192 /**
193 * Returns the path that points to the least common ancestor of the specified
194 * collection of paths.
195 * @param paths collection of path
196 * @return path to least common ancestor
197 */
198 public static DocumentPath leastCommonAncestor(Collection<DocumentPath> paths) {
199 if (CollectionUtils.isEmpty(paths)) {
200 return null;
201 }
202 return DocumentPath.from(StringUtils.getCommonPrefix(paths.stream()
203 .map(DocumentPath::toString)
204 .toArray(String[]::new)));
205 }
206
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700207 @Override
208 public int hashCode() {
Madan Jampani5bdebd52016-09-07 16:18:12 -0700209 return Objects.hash(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700210 }
211
212 @Override
213 public boolean equals(Object obj) {
214 if (obj instanceof DocumentPath) {
215 DocumentPath that = (DocumentPath) obj;
Madan Jampani5bdebd52016-09-07 16:18:12 -0700216 return this.pathElements.equals(that.pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700217 }
218 return false;
219 }
220
221 @Override
222 public String toString() {
223 StringBuilder stringBuilder = new StringBuilder();
Madan Jampani5bdebd52016-09-07 16:18:12 -0700224 Iterator<String> iter = pathElements.iterator();
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700225 while (iter.hasNext()) {
226 stringBuilder.append(iter.next());
227 if (iter.hasNext()) {
Thomas Vachuskae2bd1152017-03-23 13:42:32 -0700228 stringBuilder.append(pathSeparator);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700229 }
230 }
231 return stringBuilder.toString();
232 }
233
234 @Override
Madan Jampani5bdebd52016-09-07 16:18:12 -0700235 public int compareTo(DocumentPath that) {
236 int shorterLength = this.pathElements.size() > that.pathElements.size()
237 ? that.pathElements.size() : this.pathElements.size();
238 for (int i = 0; i < shorterLength; i++) {
239 if (this.pathElements.get(i).compareTo(that.pathElements.get(i)) != 0) {
240 return this.pathElements.get(i).compareTo(that.pathElements.get(i));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700241 }
242 }
Madan Jampani5bdebd52016-09-07 16:18:12 -0700243 if (this.pathElements.size() > that.pathElements.size()) {
244 return 1;
245 } else if (that.pathElements.size() > this.pathElements.size()) {
246 return -1;
247 } else {
248 return 0;
249 }
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700250 }
251}