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