1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package com.jcabi.xml;
31
32 import com.jcabi.log.Logger;
33 import java.util.Collection;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.ListIterator;
37 import org.w3c.dom.Node;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 @SuppressWarnings("PMD.TooManyMethods")
85 final class ListWrapper<T> implements List<T> {
86
87
88
89 private final transient List<T> original;
90
91
92
93
94 private final transient Node dom;
95
96
97
98
99 private final transient String xpath;
100
101
102
103
104
105
106
107 ListWrapper(final List<T> list, final Node node, final String addr) {
108 super();
109 this.original = list;
110 this.dom = node;
111 this.xpath = addr;
112 }
113
114 @Override
115 public String toString() {
116 return this.original.toString();
117 }
118
119 @Override
120 public boolean add(final T element) {
121 throw new UnsupportedOperationException("#add(T)");
122 }
123
124 @Override
125 public void add(final int index, final T element) {
126 throw new UnsupportedOperationException("#add(int, T)");
127 }
128
129 @Override
130 public boolean addAll(final Collection<? extends T> elements) {
131 throw new UnsupportedOperationException("#addAll(Collection)");
132 }
133
134 @Override
135 public boolean addAll(final int index, final Collection<? extends T> elms) {
136 throw new UnsupportedOperationException("#add(int, Collection)");
137 }
138
139 @Override
140 public void clear() {
141 throw new UnsupportedOperationException("#clear()");
142 }
143
144 @Override
145 public boolean contains(final Object element) {
146 return this.original.contains(element);
147 }
148
149 @Override
150 public boolean containsAll(final Collection<?> elements) {
151 return this.original.containsAll(elements);
152 }
153
154 @Override
155 public T get(final int index) {
156 if (index >= this.size()) {
157 throw new ListWrapper.NodeNotFoundException(
158 String.format(
159 "Index (%d) is out of bounds (size=%d)",
160 index, this.size()
161 ),
162 this.dom,
163 this.xpath
164 );
165 }
166 return this.original.get(index);
167 }
168
169 @Override
170 public int indexOf(final Object element) {
171 return this.original.indexOf(element);
172 }
173
174 @Override
175 public boolean isEmpty() {
176 return this.original.isEmpty();
177 }
178
179 @Override
180 public Iterator<T> iterator() {
181 return this.original.iterator();
182 }
183
184 @Override
185 public int lastIndexOf(final Object element) {
186 return this.original.lastIndexOf(element);
187 }
188
189 @Override
190 public ListIterator<T> listIterator() {
191 return this.original.listIterator();
192 }
193
194 @Override
195 public ListIterator<T> listIterator(final int index) {
196 return this.original.listIterator(index);
197 }
198
199 @Override
200 public boolean equals(final Object other) {
201 return this.original.equals(other);
202 }
203
204 @Override
205 public int hashCode() {
206 return this.original.hashCode();
207 }
208
209 @Override
210 public T remove(final int index) {
211 throw new UnsupportedOperationException("#remove(int)");
212 }
213
214 @Override
215 public boolean remove(final Object element) {
216 throw new UnsupportedOperationException("#remove(Object)");
217 }
218
219 @Override
220 public boolean removeAll(final Collection<?> elements) {
221 throw new UnsupportedOperationException("#removeAll(Collection)");
222 }
223
224 @Override
225 public boolean retainAll(final Collection<?> elements) {
226 throw new UnsupportedOperationException("#retainAll(Collection)");
227 }
228
229 @Override
230 public T set(final int index, final T element) {
231 throw new UnsupportedOperationException("#set(int, T)");
232 }
233
234 @Override
235 public int size() {
236 return this.original.size();
237 }
238
239 @Override
240 public List<T> subList(final int start, final int end) {
241 if (start >= this.size()) {
242 throw new ListWrapper.NodeNotFoundException(
243 String.format(
244 "Start of subList (%d) is out of bounds (size=%d)",
245 start, this.size()
246 ),
247 this.dom,
248 this.xpath
249 );
250 }
251 if (end >= this.size()) {
252 throw new ListWrapper.NodeNotFoundException(
253 String.format(
254 "End of subList (%d) is out of bounds (size=%d)",
255 end, this.size()
256 ),
257 this.dom,
258 this.xpath
259 );
260 }
261 return this.original.subList(start, end);
262 }
263
264 @Override
265 public Object[] toArray() {
266 return this.original.toArray();
267 }
268
269 @Override
270 @SuppressWarnings("PMD.UseVarargs")
271 public <E> E[] toArray(final E[] array) {
272 return this.original.toArray(array);
273 }
274
275
276
277
278
279
280 private static final class NodeNotFoundException
281 extends IndexOutOfBoundsException {
282
283
284
285 private static final long serialVersionUID = 0x7526FA78EEDAC470L;
286
287
288
289
290
291
292
293 NodeNotFoundException(final String message, final Node node,
294 final CharSequence query) {
295 super(
296 Logger.format(
297 "XPath '%s' not found in '%[text]s': %s",
298 ListWrapper.NodeNotFoundException.escapeUnicode(query),
299 ListWrapper.NodeNotFoundException.escapeUnicode(
300 new XMLDocument(node).toString()
301 ),
302 message
303 )
304 );
305 }
306
307
308
309
310
311
312 private static String escapeUnicode(final CharSequence input) {
313 final int length = input.length();
314 final StringBuilder output = new StringBuilder(length);
315 for (int index = 0; index < length; index += 1) {
316 final char character = input.charAt(index);
317 if (character < 32 || character > 0x7f) {
318 output.append(String.format("\\u%X", (int) character));
319 } else {
320 output.append(character);
321 }
322 }
323 return output.toString();
324 }
325 }
326
327 }