source: josm/trunk/src/org/openstreetmap/josm/data/gpx/GpxExtensionCollection.java@ 16553

Last change on this file since 16553 was 16553, checked in by Don-vip, 4 years ago

see #19334 - javadoc fixes + protected constructors for abstract classes

  • Property svn:eol-style set to native
File size: 8.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.gpx;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Objects;
7import java.util.Optional;
8import java.util.Stack;
9import java.util.stream.Collectors;
10import java.util.stream.Stream;
11
12import org.apache.commons.jcs3.access.exception.InvalidArgumentException;
13import org.openstreetmap.josm.io.GpxReader;
14import org.xml.sax.Attributes;
15
16/**
17 * Class extending <code>ArrayList&lt;GpxExtension&gt;</code>.
18 * Can be used to collect {@link GpxExtension}s while reading GPX files, see {@link GpxReader}
19 * @since 15496
20 */
21public class GpxExtensionCollection extends ArrayList<GpxExtension> {
22
23 private static final long serialVersionUID = 1L;
24
25 private Stack<GpxExtension> childStack = new Stack<>();
26 private IWithAttributes parent;
27
28 /**
29 * Constructs a new {@link GpxExtensionCollection}
30 */
31 public GpxExtensionCollection() {}
32
33 /**
34 * Constructs a new {@link GpxExtensionCollection} with the given parent
35 * @param parent the parent extending {@link IWithAttributes}
36 */
37 public GpxExtensionCollection(IWithAttributes parent) {
38 this.parent = parent;
39 }
40
41 /**
42 * Adds a child extension to the last extension and pushes it to the stack.
43 * @param namespaceURI the URI of the XML namespace, used to determine supported
44 * extensions (josm, gpxx, gpxd) regardless of the prefix.
45 * @param qName the qualified name of the XML element including prefix
46 * @param atts the attributes
47 */
48 public void openChild(String namespaceURI, String qName, Attributes atts) {
49 GpxExtension child = new GpxExtension(namespaceURI, qName, atts);
50 if (!childStack.isEmpty()) {
51 childStack.lastElement().getExtensions().add(child);
52 } else {
53 this.add(child);
54 }
55 childStack.add(child);
56 }
57
58 /**
59 * Sets the value for the last child and pops it from the stack, so the next one will be added to its parent.
60 * The qualified name is verified.
61 * @param qName the qualified name
62 * @param value the value
63 */
64 public void closeChild(String qName, String value) {
65 if (childStack.isEmpty())
66 throw new InvalidArgumentException("Can't close child " + qName + ", no element in stack.");
67
68 GpxExtension child = childStack.pop();
69
70 String childQN = child.getQualifiedName();
71
72 if (!childQN.equals(qName))
73 throw new InvalidArgumentException("Can't close child " + qName + ", must close " + childQN + " first.");
74
75 child.setValue(value);
76 }
77
78 @Override
79 public boolean add(GpxExtension gpx) {
80 gpx.setParent(parent);
81 return super.add(gpx);
82 }
83
84 /**
85 * Creates and adds a new {@link GpxExtension} from the given parameters.
86 * @param prefix the prefix
87 * @param key the key/tag
88 * @return the added GpxExtension
89 */
90 public GpxExtension add(String prefix, String key) {
91 return add(prefix, key, null);
92 }
93
94 /**
95 * Creates and adds a new {@link GpxExtension} from the given parameters.
96 * @param prefix the prefix
97 * @param key the key/tag
98 * @param value the value, can be <code>null</code>
99 * @return the added GpxExtension
100 */
101 public GpxExtension add(String prefix, String key, String value) {
102 GpxExtension gpx = new GpxExtension(prefix, key, value);
103 add(gpx);
104 return gpx;
105 }
106
107 /**
108 * Creates and adds a new {@link GpxExtension}, if it hasn't been added yet. Shows it if it has.
109 * @param prefix the prefix
110 * @param key the key/tag
111 * @return the added or found GpxExtension
112 * @see GpxExtension#show()
113 */
114 public GpxExtension addIfNotPresent(String prefix, String key) {
115 GpxExtension gpx = get(prefix, key);
116 if (gpx != null) {
117 gpx.show();
118 return gpx;
119 }
120 return add(prefix, key);
121 }
122
123 /**
124 * Creates and adds a new {@link GpxExtension} or updates its value and shows it if already present.
125 * @param prefix the prefix
126 * @param key the key/tag
127 * @param value the value
128 * @return the added or found GpxExtension
129 * @see GpxExtension#show()
130 */
131 public GpxExtension addOrUpdate(String prefix, String key, String value) {
132 GpxExtension gpx = get(prefix, key);
133 if (gpx != null) {
134 gpx.show();
135 gpx.setValue(value);
136 return gpx;
137 } else {
138 return add(prefix, key, value);
139 }
140 }
141
142 @Override
143 public boolean addAll(Collection<? extends GpxExtension> extensions) {
144 extensions.forEach(e -> e.setParent(parent));
145 return super.addAll(extensions);
146 }
147
148 /**
149 * Adds an extension from a flat chain without prefix, e.g. when converting from OSM
150 * @param chain the full key chain, e.g. ["extension", "gpxx", "TrackExtension", "DisplayColor"]
151 * @param value the value
152 */
153 public void addFlat(String[] chain, String value) {
154 if (chain.length >= 3 && "extension".equals(chain[0])) {
155 String prefix = "other".equals(chain[1]) ? "" : chain[1];
156 GpxExtensionCollection previous = this;
157 for (int i = 2; i < chain.length; i++) {
158 if (i != 2 || !"segment".equals(chain[2])) {
159 previous = previous.add(prefix, chain[i], i == chain.length - 1 ? value : null).getExtensions();
160 }
161 }
162 }
163 }
164
165 /**
166 * Gets the extension with the given prefix and key
167 * @param prefix the prefix
168 * @param key the key/tag
169 * @return the {@link GpxExtension} if found or <code>null</code>
170 */
171 public GpxExtension get(String prefix, String key) {
172 return stream(prefix, key).findAny().orElse(null);
173 }
174
175 /**
176 * Gets all extensions with the given prefix and key
177 * @param prefix the prefix
178 * @param key the key/tag
179 * @return a {@link GpxExtensionCollection} with the extensions, empty collection if none found
180 */
181 public GpxExtensionCollection getAll(String prefix, String key) {
182 GpxExtensionCollection copy = new GpxExtensionCollection(this.parent);
183 copy.addAll(stream(prefix, key).collect(Collectors.toList()));
184 return copy;
185 }
186
187 /**
188 * Gets a stream with all extensions with the given prefix and key
189 * @param prefix the prefix
190 * @param key the key/tag
191 * @return the <code>Stream&lt;{@link GpxExtension}&gt;</code>
192 */
193 public Stream<GpxExtension> stream(String prefix, String key) {
194 return stream().filter(e -> Objects.equals(prefix, e.getPrefix()) && Objects.equals(key, e.getKey()));
195 }
196
197 /**
198 * Searches recursively for the extension with the given prefix and key in all children
199 * @param prefix the prefix to look for
200 * @param key the key to look for
201 * @return the extension if found, otherwise <code>null</code>
202 */
203 public GpxExtension find(String prefix, String key) {
204 return this.stream()
205 .map(child -> child.findExtension(prefix, key)).filter(Objects::nonNull)
206 .findFirst().orElse(null);
207 }
208
209 /**
210 * Searches and removes recursively all extensions with the given prefix and key in all children
211 * @param prefix the prefix to look for
212 * @param key the key to look for
213 */
214 public void findAndRemove(String prefix, String key) {
215 Optional.ofNullable(find(prefix, key)).ifPresent(GpxExtension::remove);
216 }
217
218 /**
219 * Removes all {@link GpxExtension}s with the given prefix and key in direct children
220 * @param prefix the prefix
221 * @param key the key/tag
222 */
223 public void remove(String prefix, String key) {
224 stream(prefix, key)
225 .collect(Collectors.toList()) //needs to be collected to avoid concurrent modification
226 .forEach(e -> super.remove(e));
227 }
228
229 /**
230 * Removes all extensions with the given prefix in direct children
231 * @param prefix the prefix
232 */
233 public void removeAllWithPrefix(String prefix) {
234 stream()
235 .filter(e -> Objects.equals(prefix, e.getPrefix()))
236 .collect(Collectors.toList()) //needs to be collected to avoid concurrent modification
237 .forEach(e -> super.remove(e));
238 }
239
240 /**
241 * Gets all prefixes of direct (writable) children
242 * @return stream with the prefixes
243 */
244 public Stream<String> getPrefixesStream() {
245 return stream()
246 .filter(GpxExtension::isVisible)
247 .map(GpxExtension::getPrefix)
248 .distinct();
249 }
250
251 /**
252 * Determines if this collection contains writable extensions.
253 * @return <code>true</code> if this collection contains writable extensions
254 */
255 public boolean isVisible() {
256 return stream().anyMatch(GpxExtension::isVisible);
257 }
258
259 @Override
260 public void clear() {
261 childStack.clear();
262 super.clear();
263 }
264
265}
Note: See TracBrowser for help on using the repository browser.