1
16 package org.springframework.data.mapping.context;
17
18 import lombok.EqualsAndHashCode;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.stream.Collectors;
25
26 import org.springframework.core.convert.converter.Converter;
27 import org.springframework.data.mapping.PersistentProperty;
28 import org.springframework.data.mapping.PersistentPropertyPath;
29 import org.springframework.data.util.TypeInformation;
30 import org.springframework.lang.Nullable;
31 import org.springframework.util.Assert;
32 import org.springframework.util.StringUtils;
33
34
40 @EqualsAndHashCode
41 class DefaultPersistentPropertyPath<P extends PersistentProperty<P>> implements PersistentPropertyPath<P> {
42
43 private static final Converter<PersistentProperty<?>, String> DEFAULT_CONVERTER = (source) -> source.getName();
44 private static final String DEFAULT_DELIMITER = ".";
45
46 private final List<P> properties;
47
48
53 public DefaultPersistentPropertyPath(List<P> properties) {
54
55 Assert.notNull(properties, "Properties must not be null!");
56
57 this.properties = properties;
58 }
59
60
65 public static <T extends PersistentProperty<T>> DefaultPersistentPropertyPath<T> empty() {
66 return new DefaultPersistentPropertyPath<T>(Collections.emptyList());
67 }
68
69
76 public DefaultPersistentPropertyPath<P> append(P property) {
77
78 Assert.notNull(property, "Property must not be null!");
79
80 if (isEmpty()) {
81 return new DefaultPersistentPropertyPath<>(Collections.singletonList(property));
82 }
83
84 @SuppressWarnings("null")
85 Class<?> leafPropertyType = getLeafProperty().getActualType();
86
87 Assert.isTrue(property.getOwner().getType().equals(leafPropertyType),
88 () -> String.format("Cannot append property %s to type %s!", property.getName(), leafPropertyType.getName()));
89
90 List<P> properties = new ArrayList<>(this.properties);
91 properties.add(property);
92
93 return new DefaultPersistentPropertyPath<>(properties);
94 }
95
96
100 @Nullable
101 public String toDotPath() {
102 return toPath(DEFAULT_DELIMITER, DEFAULT_CONVERTER);
103 }
104
105
109 @Nullable
110 public String toDotPath(Converter<? super P, String> converter) {
111 return toPath(DEFAULT_DELIMITER, converter);
112 }
113
114
118 @Nullable
119 public String toPath(String delimiter) {
120 return toPath(delimiter, DEFAULT_CONVERTER);
121 }
122
123
127 @Nullable
128 public String toPath(String delimiter, Converter<? super P, String> converter) {
129
130 Assert.hasText(delimiter, "Delimiter must not be null or empty!");
131 Assert.notNull(converter, "Converter must not be null!");
132
133 String result = properties.stream()
134 .map(converter::convert)
135 .filter(StringUtils::hasText)
136 .collect(Collectors.joining(delimiter));
137
138 return result.isEmpty() ? null : result;
139 }
140
141
145 @Nullable
146 public P getLeafProperty() {
147 return properties.isEmpty() ? null : properties.get(properties.size() - 1);
148 }
149
150
154 @Nullable
155 public P getBaseProperty() {
156 return properties.isEmpty() ? null : properties.get(0);
157 }
158
159
163 public boolean isBasePathOf(PersistentPropertyPath<P> path) {
164
165 Assert.notNull(path, "PersistentPropertyPath must not be null!");
166
167 Iterator<P> iterator = path.iterator();
168
169 for (P property : this) {
170
171 if (!iterator.hasNext()) {
172 return false;
173 }
174
175 P reference = iterator.next();
176
177 if (!property.equals(reference)) {
178 return false;
179 }
180 }
181
182 return true;
183 }
184
185
189 public PersistentPropertyPath<P> getExtensionForBaseOf(PersistentPropertyPath<P> base) {
190
191 if (!base.isBasePathOf(this)) {
192 return this;
193 }
194
195 List<P> result = new ArrayList<>();
196 Iterator<P> iterator = iterator();
197
198 for (int i = 0; i < base.getLength(); i++) {
199 iterator.next();
200 }
201
202 while (iterator.hasNext()) {
203 result.add(iterator.next());
204 }
205
206 return new DefaultPersistentPropertyPath<>(result);
207 }
208
209
213 public PersistentPropertyPath<P> getParentPath() {
214
215 int size = properties.size();
216
217 return size == 0 ? this : new DefaultPersistentPropertyPath<>(properties.subList(0, size - 1));
218 }
219
220
224 public int getLength() {
225 return properties.size();
226 }
227
228
232 public Iterator<P> iterator() {
233 return properties.iterator();
234 }
235
236
242 public boolean containsPropertyOfType(@Nullable TypeInformation<?> type) {
243
244 return type == null
245 ? false
246 : properties.stream()
247 .anyMatch(property -> type.equals(property.getTypeInformation().getActualType()));
248 }
249
250
254 @Override
255 @Nullable
256 public String toString() {
257 return toDotPath();
258 }
259 }
260