blob: ada10b80247861dcbc409cafc0b2fc04645ee685 [file] [log] [blame]
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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
Madan Jampaniad5b8c72016-09-12 15:05:01 -070019import java.util.Arrays;
Madan Jampani98094222016-09-15 21:12:46 -070020import java.util.Collection;
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070021import java.util.Iterator;
22import java.util.List;
23import java.util.Objects;
24
Madan Jampani98094222016-09-15 21:12:46 -070025import org.apache.commons.collections.CollectionUtils;
26import org.apache.commons.lang.StringUtils;
27
Madan Jampani5bdebd52016-09-07 16:18:12 -070028import com.google.common.base.Preconditions;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.Lists;
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070031
Madan Jampani5bdebd52016-09-07 16:18:12 -070032/**
33 * Unique key for nodes in the {@link DocumentTree}.
34 */
35public class DocumentPath implements Comparable<DocumentPath> {
36
37 private final List<String> pathElements = Lists.newArrayList();
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070038
39 /**
40 * Private utility constructor for internal generation of partial paths only.
41 *
Madan Jampani5bdebd52016-09-07 16:18:12 -070042 * @param pathElements list of path elements
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070043 */
Madan Jampani5bdebd52016-09-07 16:18:12 -070044 private DocumentPath(List<String> pathElements) {
45 Preconditions.checkNotNull(pathElements);
46 this.pathElements.addAll(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070047 }
48
49 /**
Thomas Vachuskac3984c62016-09-07 17:51:45 -070050 * Constructs a new document path.
Madan Jampani5bdebd52016-09-07 16:18:12 -070051 * <p>
52 * New paths must contain at least one name and string names may NOT contain any period characters.
53 * If one field is {@code null} that field will be ignored.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070054 *
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070055 * @param nodeName the name of the last level of this path
Madan Jampani5bdebd52016-09-07 16:18:12 -070056 * @param parentPath the path representing the parent leading up to this
57 * node, in the case of the root this should be {@code null}
58 * @throws IllegalDocumentNameException if both parameters are null or name contains an illegal character ('.')
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070059 */
60 public DocumentPath(String nodeName, DocumentPath parentPath) {
61 if (nodeName.contains(".")) {
62 throw new IllegalDocumentNameException(
63 "Periods are not allowed in names.");
64 }
65 if (parentPath != null) {
Madan Jampani5bdebd52016-09-07 16:18:12 -070066 pathElements.addAll(parentPath.pathElements());
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070067 }
68 if (nodeName != null) {
Madan Jampani5bdebd52016-09-07 16:18:12 -070069 pathElements.add(nodeName);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070070 }
Madan Jampani5bdebd52016-09-07 16:18:12 -070071 if (pathElements.isEmpty()) {
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070072 throw new IllegalDocumentNameException("A document path must contain at" +
73 "least one non-null" +
74 "element.");
75 }
76 }
77
78 /**
Madan Jampaniad5b8c72016-09-12 15:05:01 -070079 * Creates a new {@code DocumentPath} from a period delimited path string.
80 *
81 * @param path path string
82 * @return {@code DocumentPath} instance
83 */
84 public static DocumentPath from(String path) {
85 return new DocumentPath(Arrays.asList(path.split("\\.")));
86 }
87
88 /**
Sithara Punnassery589fac22016-10-03 11:51:53 -070089 * Returns the relative path to the given node.
90 *
91 * @return relative path to the given node.
92 */
93 public DocumentPath childPath() {
94 if (pathElements.size() <= 1) {
95 return null;
96 }
97 return new DocumentPath(this.pathElements.subList(pathElements.size() - 1, pathElements.size()));
98 }
99 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -0700100 * Returns a path for the parent of this node.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700101 *
Madan Jampani5bdebd52016-09-07 16:18:12 -0700102 * @return parent node path. If this path is for the root, returns {@code null}.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700103 */
104 public DocumentPath parent() {
Madan Jampani5bdebd52016-09-07 16:18:12 -0700105 if (pathElements.size() <= 1) {
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700106 return null;
107 }
Madan Jampani5bdebd52016-09-07 16:18:12 -0700108 return new DocumentPath(this.pathElements.subList(0, pathElements.size() - 1));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700109 }
110
111 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -0700112 * Returns the list of path elements representing this path in correct
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700113 * order.
114 *
Madan Jampani5bdebd52016-09-07 16:18:12 -0700115 * @return a list of elements that make up this path
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700116 */
Madan Jampani5bdebd52016-09-07 16:18:12 -0700117 public List<String> pathElements() {
118 return ImmutableList.copyOf(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700119 }
120
Madan Jampani98094222016-09-15 21:12:46 -0700121 /**
122 * Returns if the specified path belongs to a direct ancestor of the node pointed at by this path.
123 * <p>
124 * Example: {@code root.a} is a direct ancestor of {@code r.a.b.c}; while {@code r.a.x} is not.
125 *
126 * @param other other path
127 * @return {@code true} is yes; {@code false} otherwise.
128 */
129 public boolean isAncestorOf(DocumentPath other) {
130 return !other.equals(this) && other.toString().startsWith(toString());
131 }
132
133 /**
134 * Returns if the specified path is belongs to a subtree rooted this path.
135 * <p>
136 * Example: {@code root.a.b} and {@code root.a.b.c.d.e} are descendants of {@code r.a.b};
137 * while {@code r.a.x.c} is not.
138 *
139 * @param other other path
140 * @return {@code true} is yes; {@code false} otherwise.
141 */
142 public boolean isDescendentOf(DocumentPath other) {
143 return other.equals(this) || other.isAncestorOf(this);
144 }
145
146 /**
147 * Returns the path that points to the least common ancestor of the specified
148 * collection of paths.
149 * @param paths collection of path
150 * @return path to least common ancestor
151 */
152 public static DocumentPath leastCommonAncestor(Collection<DocumentPath> paths) {
153 if (CollectionUtils.isEmpty(paths)) {
154 return null;
155 }
156 return DocumentPath.from(StringUtils.getCommonPrefix(paths.stream()
157 .map(DocumentPath::toString)
158 .toArray(String[]::new)));
159 }
160
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700161 @Override
162 public int hashCode() {
Madan Jampani5bdebd52016-09-07 16:18:12 -0700163 return Objects.hash(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700164 }
165
166 @Override
167 public boolean equals(Object obj) {
168 if (obj instanceof DocumentPath) {
169 DocumentPath that = (DocumentPath) obj;
Madan Jampani5bdebd52016-09-07 16:18:12 -0700170 return this.pathElements.equals(that.pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700171 }
172 return false;
173 }
174
175 @Override
176 public String toString() {
177 StringBuilder stringBuilder = new StringBuilder();
Madan Jampani5bdebd52016-09-07 16:18:12 -0700178 Iterator<String> iter = pathElements.iterator();
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700179 while (iter.hasNext()) {
180 stringBuilder.append(iter.next());
181 if (iter.hasNext()) {
182 stringBuilder.append(".");
183 }
184 }
185 return stringBuilder.toString();
186 }
187
188 @Override
Madan Jampani5bdebd52016-09-07 16:18:12 -0700189 public int compareTo(DocumentPath that) {
190 int shorterLength = this.pathElements.size() > that.pathElements.size()
191 ? that.pathElements.size() : this.pathElements.size();
192 for (int i = 0; i < shorterLength; i++) {
193 if (this.pathElements.get(i).compareTo(that.pathElements.get(i)) != 0) {
194 return this.pathElements.get(i).compareTo(that.pathElements.get(i));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700195 }
196 }
Madan Jampani5bdebd52016-09-07 16:18:12 -0700197 if (this.pathElements.size() > that.pathElements.size()) {
198 return 1;
199 } else if (that.pathElements.size() > this.pathElements.size()) {
200 return -1;
201 } else {
202 return 0;
203 }
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700204 }
205}