1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common.xml;
20
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.jaxen.BaseXPath;
28 import org.jaxen.JaxenException;
29 import org.jaxen.dom.DocumentNavigator;
30 import org.jaxen.expr.EqualityExpr;
31 import org.jaxen.expr.Expr;
32 import org.jaxen.expr.LiteralExpr;
33 import org.jaxen.expr.LocationPath;
34 import org.jaxen.expr.NameStep;
35 import org.jaxen.expr.Predicate;
36 import org.jaxen.expr.Step;
37 import org.jaxen.saxpath.Axis;
38 import org.jdom2.Attribute;
39 import org.jdom2.Document;
40 import org.jdom2.Element;
41 import org.jdom2.Namespace;
42 import org.jdom2.Parent;
43 import org.mycore.common.MCRConstants;
44
45
46
47
48 public class MCRNodeBuilder {
49
50 private static final Logger LOGGER = LogManager.getLogger(MCRNodeBuilder.class);
51
52 private Map<String, Object> variables;
53
54 private Object firstNodeBuilt = null;
55
56 public MCRNodeBuilder() {
57 }
58
59 public MCRNodeBuilder(Map<String, Object> variables) {
60 this.variables = variables;
61 }
62
63 public Object getFirstNodeBuilt() {
64 return firstNodeBuilt;
65 }
66
67 public Element buildElement(String xPath, String value, Parent parent) throws JaxenException {
68 return (Element) buildNode(xPath, value, parent);
69 }
70
71 public Attribute buildAttribute(String xPath, String value, Parent parent) throws JaxenException {
72 return (Attribute) buildNode(xPath, value, parent);
73 }
74
75 public Object buildNode(String xPath, String value, Parent parent) throws JaxenException {
76 BaseXPath baseXPath = new BaseXPath(xPath, new DocumentNavigator());
77 if (LOGGER.isDebugEnabled()) {
78 LOGGER.debug("start building {} relative to {}", simplify(xPath), MCRXPathBuilder.buildXPath(parent));
79 }
80 return buildExpression(baseXPath.getRootExpr(), value, parent);
81 }
82
83 private Object buildExpression(Expr expression, String value, Parent parent) throws JaxenException {
84 if (expression instanceof EqualityExpr) {
85 return buildEqualityExpression((EqualityExpr) expression, parent);
86 } else if (expression instanceof LocationPath) {
87 return buildLocationPath((LocationPath) expression, value, parent);
88 } else {
89 return canNotBuild(expression);
90 }
91 }
92
93 @SuppressWarnings("unchecked")
94 private Object buildLocationPath(LocationPath locationPath, String value, Parent parent) throws JaxenException {
95 Object existingNode = null;
96 List<Step> steps = locationPath.getSteps();
97 int i, indexOfLastStep = steps.size() - 1;
98
99 for (i = indexOfLastStep; i >= 0; i--) {
100 String xPath = buildXPath(steps.subList(0, i + 1));
101 existingNode = evaluateFirst(xPath, parent);
102
103 if (existingNode instanceof Element) {
104 if (LOGGER.isDebugEnabled()) {
105 LOGGER.debug("element already existing");
106 }
107 parent = (Element) existingNode;
108 break;
109 } else if (existingNode instanceof Attribute) {
110 if (LOGGER.isDebugEnabled()) {
111 LOGGER.debug("attribute already existing");
112 }
113 break;
114 } else if (LOGGER.isDebugEnabled()) {
115 LOGGER.debug("{} does not exist or is not a node, will try to build it", xPath);
116 }
117 }
118
119 if (i == indexOfLastStep) {
120 return existingNode;
121 } else {
122 return buildLocationSteps(steps.subList(i + 1, steps.size()), value, parent);
123 }
124 }
125
126 private Object evaluateFirst(String xPath, Parent parent) {
127 return new MCRXPathEvaluator(variables, parent).evaluateFirst(xPath);
128 }
129
130 private String buildXPath(List<Step> steps) {
131 StringBuilder path = new StringBuilder();
132 for (Step step : steps) {
133 path.append("/").append(step.getText());
134 }
135 return simplify(path.substring(1));
136 }
137
138 private Object buildLocationSteps(List<Step> steps, String value, Parent parent) throws JaxenException {
139 Object built = null;
140
141 for (Iterator<Step> iterator = steps.iterator(); iterator.hasNext();) {
142 Step step = iterator.next();
143
144 built = buildStep(step, iterator.hasNext() ? null : value, parent);
145 if (built == null) {
146 return parent;
147 } else if (firstNodeBuilt == null) {
148 firstNodeBuilt = built;
149 }
150
151 if (built instanceof Parent) {
152 parent = (Parent) built;
153 }
154 }
155
156 return built;
157 }
158
159 private Object buildStep(Step step, String value, Parent parent) throws JaxenException {
160 if (step instanceof NameStep) {
161 return buildNameStep((NameStep) step, value, parent);
162 } else {
163 if (LOGGER.isDebugEnabled()) {
164 LOGGER.debug("ignoring step, can not be built: {} {}", step.getClass().getName(),
165 simplify(step.getText()));
166 }
167 return null;
168 }
169 }
170
171 @SuppressWarnings("unchecked")
172 private Object buildNameStep(NameStep nameStep, String value, Parent parent) throws JaxenException {
173 String name = nameStep.getLocalName();
174 String prefix = nameStep.getPrefix();
175 Namespace ns = prefix.isEmpty() ? Namespace.NO_NAMESPACE : MCRConstants.getStandardNamespace(prefix);
176
177 if (nameStep.getAxis() == Axis.CHILD) {
178 if (parent instanceof Document) {
179 return buildPredicates(nameStep.getPredicates(), ((Document) parent).getRootElement());
180 } else {
181 return buildPredicates(nameStep.getPredicates(), buildElement(ns, name, value, (Element) parent));
182 }
183 } else if (nameStep.getAxis() == Axis.ATTRIBUTE) {
184 return buildAttribute(ns, name, value, (Element) parent);
185 } else {
186 if (LOGGER.isDebugEnabled()) {
187 LOGGER.debug("ignoring axis, can not be built: {} {}{}", nameStep.getAxis(),
188 prefix.isEmpty() ? "" : prefix + ":", name);
189 }
190 return null;
191 }
192 }
193
194 private Element buildPredicates(List<Predicate> predicates, Element parent) throws JaxenException {
195 for (Predicate predicate : predicates) {
196 new MCRNodeBuilder(variables).buildExpression(predicate.getExpr(), null, parent);
197 }
198 return parent;
199 }
200
201 private Object buildEqualityExpression(EqualityExpr ee, Parent parent) throws JaxenException {
202 if (ee.getOperator().equals("=")) {
203 if ((ee.getLHS() instanceof LocationPath) && (ee.getRHS() instanceof LiteralExpr)) {
204 return assignLiteral(ee.getLHS(), (LiteralExpr) (ee.getRHS()), parent);
205 } else if ((ee.getRHS() instanceof LocationPath) && (ee.getLHS() instanceof LiteralExpr)) {
206 return assignLiteral(ee.getRHS(), (LiteralExpr) (ee.getLHS()), parent);
207 } else if (ee.getLHS() instanceof LocationPath) {
208 String value = getValueOf(ee.getRHS().getText(), parent);
209 if (value != null) {
210 return assignLiteral(ee.getLHS(), value, parent);
211 }
212 }
213 }
214 return canNotBuild(ee);
215 }
216
217
218
219
220
221
222
223
224 public String getValueOf(String xPath, Parent parent) {
225 Object result = evaluateFirst(xPath, parent);
226
227 if (result instanceof String) {
228 return (String) result;
229 } else if (result instanceof Element) {
230 return ((Element) result).getText();
231 } else if (result instanceof Attribute) {
232 return ((Attribute) result).getValue();
233 } else {
234 return null;
235 }
236 }
237
238 private Object assignLiteral(Expr expression, LiteralExpr literal, Parent parent) throws JaxenException {
239 String xPath = simplify(expression.getText()) + "[.=" + literal.getText() + "]";
240 return assignLiteral(expression, literal.getLiteral(), parent, xPath);
241 }
242
243 private Object assignLiteral(Expr expression, String literal, Parent parent) throws JaxenException {
244 String delimiter = literal.contains("'") ? "\"" : "'";
245 String xPath = simplify(expression.getText()) + "[.=" + delimiter + literal + delimiter + "]";
246 return assignLiteral(expression, literal, parent, xPath);
247 }
248
249 private Object assignLiteral(Expr expression, String literal, Parent parent, String xPath) throws JaxenException {
250 Object result = evaluateFirst(xPath, parent);
251
252 if ((result instanceof Element) || (result instanceof Attribute)) {
253 return result;
254 } else {
255 xPath = simplify(expression.getText()) + "[9999]";
256 return buildNode(xPath, literal, parent);
257 }
258 }
259
260 private Element buildElement(Namespace ns, String name, String value, Element parent) {
261 Element element = new Element(name, ns);
262 if ((value != null) && !value.isEmpty()) {
263 element.setText(value);
264 }
265 if (LOGGER.isDebugEnabled()) {
266 LOGGER.debug("building new element {}", element.getName());
267 }
268 if (parent != null) {
269 parent.addContent(element);
270 }
271 return element;
272 }
273
274 private Attribute buildAttribute(Namespace ns, String name, String value, Element parent) {
275 Attribute attribute = new Attribute(name, value == null ? "" : value, ns);
276 if (LOGGER.isDebugEnabled()) {
277 LOGGER.debug("building new attribute {}", attribute.getName());
278 }
279 if (parent != null) {
280 parent.setAttribute(attribute);
281 }
282 return attribute;
283 }
284
285
286
287
288 public static String simplify(String xPath) {
289 return xPath.replaceAll("child::", "").replaceAll("attribute::", "@");
290 }
291
292 private Object canNotBuild(Expr expression) {
293 if (LOGGER.isDebugEnabled()) {
294 LOGGER.debug("ignoring expression, can not be built: {} {}", expression.getClass().getName(),
295 simplify(expression.getText()));
296 }
297 return null;
298 }
299 }