1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.smartics.ci.config.utils;
17
18 import java.io.ByteArrayOutputStream;
19 import java.util.Iterator;
20
21 import org.apache.commons.lang.StringUtils;
22 import org.jdom.Content;
23 import org.jdom.Document;
24 import org.jdom.Element;
25 import org.jdom.JDOMException;
26 import org.jdom.output.XMLOutputter;
27
28 import de.smartics.ci.config.hudson.HudsonConfigElement;
29 import de.smartics.util.lang.Arguments;
30 import de.smartics.util.lang.NullArgumentException;
31
32
33
34
35 public final class JDomUtils
36 {
37
38
39
40
41
42
43
44
45
46
47
48
49
50 private JDomUtils()
51 {
52 }
53
54
55
56
57
58
59 private static final class Cursor
60 {
61
62
63
64 private Element parent;
65
66
67
68
69 private final Iterator<?> toBeMergedIterator;
70
71
72
73
74 private final Element targetRootElement;
75
76
77
78
79 private final Element rootElement;
80
81 private Cursor(final Element target, final Element toBeMerged)
82 {
83 checkRootElementsMatch(target, toBeMerged);
84
85 targetRootElement = target;
86 rootElement = toBeMerged;
87 parent = target;
88 toBeMergedIterator = toBeMerged.getChildren().iterator();
89 }
90
91 private static void checkRootElementsMatch(final Element target,
92 final Element toBeMerged)
93 {
94 Arguments.checkNotNull("target", target);
95 Arguments.checkNotNull("toBeMerged", toBeMerged);
96 final String targetName = target.getName();
97 final String toBeMergedName = toBeMerged.getName();
98 if (!targetName.equals(toBeMergedName))
99 {
100 throw new IllegalArgumentException(
101 "The root element names of the XML subtrees to be merged do not"
102 + " match. Target is '" + targetName + "' and toBeMerged is '"
103 + toBeMergedName + "'.");
104 }
105 }
106
107 private boolean hasNextToMerge()
108 {
109 return toBeMergedIterator.hasNext();
110 }
111
112 private Content nextSiblingMergeContent()
113 {
114 return (Content) toBeMergedIterator.next();
115 }
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 public static void merge(final Document target, final Document toBeMerged)
142 throws NullArgumentException, JDOMException
143 {
144 Arguments.checkNotNull("target", target);
145 Arguments.checkNotNull("toBeMerged", toBeMerged);
146
147 merge(target.getRootElement(), toBeMerged.getRootElement());
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 public static void merge(final Element target, final Element toBeMerged)
164 throws JDOMException
165 {
166 final Cursor cursor = new Cursor(target, toBeMerged);
167
168 if (target.getChildren().isEmpty())
169 {
170 if (cursor.hasNextToMerge())
171 {
172 fillWithMergeElements(cursor);
173 return;
174 }
175 else
176 {
177 overwriteTextContent(target, toBeMerged);
178 }
179 }
180
181 while (cursor.hasNextToMerge())
182 {
183 final Content content = cursor.nextSiblingMergeContent();
184 if (content instanceof Element)
185 {
186 handleAsElement(cursor, content);
187 }
188 }
189 }
190
191 private static void fillWithMergeElements(final Cursor cursor)
192 {
193 while (cursor.hasNextToMerge())
194 {
195 final Content content = cursor.nextSiblingMergeContent();
196 cursor.parent.addContent((Content) content.clone());
197 }
198 }
199
200 private static void overwriteTextContent(final Element target,
201 final Element toBeMerged)
202 {
203 final String textContent = toBeMerged.getText();
204 if (StringUtils.isNotBlank(textContent))
205 {
206 target.setText(textContent);
207 }
208 }
209
210 private static void handleAsElement(final Cursor cursor, final Content content)
211 throws JDOMException
212 {
213 final Element element = (Element) content;
214
215 final Element targetElement = findElementInTarget(cursor, element);
216 final boolean exists = targetElement != null;
217 if (exists)
218 {
219 merge(targetElement, element);
220 }
221 else
222 {
223 cursor.parent.addContent((Content) element.clone());
224 }
225 }
226
227 private static Element findElementInTarget(final Cursor cursor,
228 final Element element) throws JDOMException
229 {
230 final Element targetElement =
231 HudsonConfigElement.findElement(cursor.rootElement, element,
232 cursor.targetRootElement);
233 return targetElement;
234 }
235
236
237
238
239
240
241
242
243
244
245 public static String calcXPath(final Element rootElement,
246 final Element element)
247 {
248 Arguments.checkNotNull("element", element);
249
250 final StringBuilder buffer = new StringBuilder(32);
251
252 buffer.append(element.getName());
253 Element current = element;
254 while ((current = current.getParentElement()) != rootElement)
255 {
256 buffer.insert(0, '/').insert(0, current.getName());
257 }
258
259 if (rootElement == null)
260 {
261 buffer.insert(0, '/');
262 }
263 return buffer.toString();
264 }
265
266
267
268
269
270
271
272 public static String toString(final Document document)
273 {
274 try
275 {
276 final ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
277
278 final XMLOutputter outp = new XMLOutputter();
279 outp.output(document, out);
280 final String string = out.toString("UTF-8");
281 return string;
282 }
283 catch (final Exception e)
284 {
285 throw new IllegalStateException("Cannot stringify document.", e);
286 }
287 }
288
289
290
291 }