blob: 46b84154d22ae2d269170d9a950fe2043528c2e6 [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 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -070089 * Returns a path for the parent of this node.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070090 *
Madan Jampani5bdebd52016-09-07 16:18:12 -070091 * @return parent node path. If this path is for the root, returns {@code null}.
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070092 */
93 public DocumentPath parent() {
Madan Jampani5bdebd52016-09-07 16:18:12 -070094 if (pathElements.size() <= 1) {
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070095 return null;
96 }
Madan Jampani5bdebd52016-09-07 16:18:12 -070097 return new DocumentPath(this.pathElements.subList(0, pathElements.size() - 1));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -070098 }
99
100 /**
Madan Jampani5bdebd52016-09-07 16:18:12 -0700101 * Returns the list of path elements representing this path in correct
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700102 * order.
103 *
Madan Jampani5bdebd52016-09-07 16:18:12 -0700104 * @return a list of elements that make up this path
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700105 */
Madan Jampani5bdebd52016-09-07 16:18:12 -0700106 public List<String> pathElements() {
107 return ImmutableList.copyOf(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700108 }
109
Madan Jampani98094222016-09-15 21:12:46 -0700110 /**
111 * Returns if the specified path belongs to a direct ancestor of the node pointed at by this path.
112 * <p>
113 * Example: {@code root.a} is a direct ancestor of {@code r.a.b.c}; while {@code r.a.x} is not.
114 *
115 * @param other other path
116 * @return {@code true} is yes; {@code false} otherwise.
117 */
118 public boolean isAncestorOf(DocumentPath other) {
119 return !other.equals(this) && other.toString().startsWith(toString());
120 }
121
122 /**
123 * Returns if the specified path is belongs to a subtree rooted this path.
124 * <p>
125 * Example: {@code root.a.b} and {@code root.a.b.c.d.e} are descendants of {@code r.a.b};
126 * while {@code r.a.x.c} is not.
127 *
128 * @param other other path
129 * @return {@code true} is yes; {@code false} otherwise.
130 */
131 public boolean isDescendentOf(DocumentPath other) {
132 return other.equals(this) || other.isAncestorOf(this);
133 }
134
135 /**
136 * Returns the path that points to the least common ancestor of the specified
137 * collection of paths.
138 * @param paths collection of path
139 * @return path to least common ancestor
140 */
141 public static DocumentPath leastCommonAncestor(Collection<DocumentPath> paths) {
142 if (CollectionUtils.isEmpty(paths)) {
143 return null;
144 }
145 return DocumentPath.from(StringUtils.getCommonPrefix(paths.stream()
146 .map(DocumentPath::toString)
147 .toArray(String[]::new)));
148 }
149
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700150 @Override
151 public int hashCode() {
Madan Jampani5bdebd52016-09-07 16:18:12 -0700152 return Objects.hash(pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700153 }
154
155 @Override
156 public boolean equals(Object obj) {
157 if (obj instanceof DocumentPath) {
158 DocumentPath that = (DocumentPath) obj;
Madan Jampani5bdebd52016-09-07 16:18:12 -0700159 return this.pathElements.equals(that.pathElements);
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700160 }
161 return false;
162 }
163
164 @Override
165 public String toString() {
166 StringBuilder stringBuilder = new StringBuilder();
Madan Jampani5bdebd52016-09-07 16:18:12 -0700167 Iterator<String> iter = pathElements.iterator();
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700168 while (iter.hasNext()) {
169 stringBuilder.append(iter.next());
170 if (iter.hasNext()) {
171 stringBuilder.append(".");
172 }
173 }
174 return stringBuilder.toString();
175 }
176
177 @Override
Madan Jampani5bdebd52016-09-07 16:18:12 -0700178 public int compareTo(DocumentPath that) {
179 int shorterLength = this.pathElements.size() > that.pathElements.size()
180 ? that.pathElements.size() : this.pathElements.size();
181 for (int i = 0; i < shorterLength; i++) {
182 if (this.pathElements.get(i).compareTo(that.pathElements.get(i)) != 0) {
183 return this.pathElements.get(i).compareTo(that.pathElements.get(i));
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700184 }
185 }
Madan Jampani5bdebd52016-09-07 16:18:12 -0700186
187 if (this.pathElements.size() > that.pathElements.size()) {
188 return 1;
189 } else if (that.pathElements.size() > this.pathElements.size()) {
190 return -1;
191 } else {
192 return 0;
193 }
Aaron Kruglikovb789b5f2016-08-31 14:47:05 -0700194 }
195}