View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.frontend.xeditor;
20  
21  import java.io.IOException;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import javax.xml.parsers.DocumentBuilderFactory;
26  import javax.xml.parsers.ParserConfigurationException;
27  import javax.xml.transform.TransformerException;
28  
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.xpath.NodeSet;
31  import org.jaxen.BaseXPath;
32  import org.jaxen.JaxenException;
33  import org.jaxen.dom.DocumentNavigator;
34  import org.jaxen.expr.LocationPath;
35  import org.jaxen.expr.NameStep;
36  import org.jdom2.Document;
37  import org.jdom2.Element;
38  import org.jdom2.JDOMException;
39  import org.jdom2.Namespace;
40  import org.jdom2.Parent;
41  import org.mycore.common.MCRConstants;
42  import org.mycore.common.MCRException;
43  import org.mycore.common.content.MCRContent;
44  import org.mycore.common.content.MCRWrappedContent;
45  import org.mycore.common.content.transformer.MCRContentTransformer;
46  import org.mycore.common.content.transformer.MCRContentTransformerFactory;
47  import org.mycore.common.content.transformer.MCRParameterizedTransformer;
48  import org.mycore.common.content.transformer.MCRXSLTransformer;
49  import org.mycore.common.xml.MCRURIResolver;
50  import org.mycore.common.xml.MCRXPathEvaluator;
51  import org.mycore.common.xsl.MCRParameterCollector;
52  import org.mycore.frontend.xeditor.target.MCRInsertTarget;
53  import org.mycore.frontend.xeditor.target.MCRSubselectTarget;
54  import org.mycore.frontend.xeditor.target.MCRSwapTarget;
55  import org.mycore.frontend.xeditor.validation.MCRValidator;
56  import org.w3c.dom.Attr;
57  import org.w3c.dom.NamedNodeMap;
58  import org.w3c.dom.Node;
59  import org.xml.sax.SAXException;
60  
61  /**
62   * @author Frank L\u00FCtzenkirchen
63   */
64  public class MCRXEditorTransformer {
65  
66      public int anchorID = 0;
67  
68      private MCREditorSession editorSession;
69  
70      private MCRBinding currentBinding;
71  
72      private MCRParameterCollector transformationParameters;
73  
74      private boolean withinSelectElement = false;
75  
76      private boolean buildFilterXSL;
77  
78      public MCRXEditorTransformer(MCREditorSession editorSession, MCRParameterCollector transformationParameters) {
79          this.editorSession = editorSession;
80          this.transformationParameters = transformationParameters;
81          this.buildFilterXSL = "true".equals(transformationParameters.getParameter("buildFilterXSL", "false"));
82      }
83  
84      public static MCRXEditorTransformer getTransformer(String key) {
85          return MCRXEditorTransformerStore.getAndRemoveTransformer(key);
86      }
87  
88      public MCRContent transform(MCRContent editorSource) throws IOException, JDOMException, SAXException {
89          editorSession.getValidator().clearRules();
90          editorSession.getSubmission().clear();
91  
92          MCRContentTransformer transformer = MCRContentTransformerFactory.getTransformer("xeditor");
93          if (transformer instanceof MCRParameterizedTransformer) {
94              String key = MCRXEditorTransformerStore.storeTransformer(this);
95              transformationParameters.setParameter("XEditorTransformerKey", key);
96              MCRContent result = ((MCRParameterizedTransformer) transformer).transform(editorSource,
97                  transformationParameters);
98              if (result instanceof MCRWrappedContent
99                  && result.getClass().getName().contains(MCRXSLTransformer.class.getName())) {
100                 //lazy transformation make JUnit tests fail
101                 result = ((MCRWrappedContent) result).getBaseContent();
102             }
103             editorSession.getValidator().clearValidationResults();
104             return result;
105         } else {
106             throw new MCRException("Xeditor needs parameterized MCRContentTransformer: " + transformer);
107         }
108     }
109 
110     public void addNamespace(String prefix, String uri) {
111         MCRConstants.registerNamespace(Namespace.getNamespace(prefix, uri));
112     }
113 
114     public void readSourceXML(String uri) throws JDOMException, IOException, SAXException, TransformerException {
115         editorSession.setEditedXML(uri);
116     }
117 
118     public void setCancelURL(String cancelURL) {
119         editorSession.setCancelURL(cancelURL);
120     }
121 
122     public void initializePostprocessor(Node postProcessorNode) {
123         NamedNodeMap attributes = postProcessorNode.getAttributes();
124         int attributesLength = attributes.getLength();
125         HashMap<String, String> attributeMap = new HashMap<>();
126         for (int i = 0; i < attributesLength; i++) {
127             Attr item = (Attr) attributes.item(i); // this should be save because we called getAttributes earlier
128             String attrName = item.getName();
129             String attrValue = item.getValue();
130             attributeMap.put(attrName, attrValue);
131         }
132 
133         editorSession.getPostProcessor().setAttributes(attributeMap);
134     }
135 
136     public void setPostProcessor(String clazz) {
137         try {
138             MCRXEditorPostProcessorre/frontend/xeditor/MCRXEditorPostProcessor.html#MCRXEditorPostProcessor">MCRXEditorPostProcessor instance = ((MCRXEditorPostProcessor) Class.forName(clazz).getDeclaredConstructor()
139                 .newInstance());
140             editorSession.setPostProcessor(instance);
141         } catch (ReflectiveOperationException e) {
142             throw new MCRException("Could not initialize Post-Processor with class" + clazz, e);
143         }
144     }
145 
146     public String replaceParameters(String uri) {
147         return getXPathEvaluator().replaceXPaths(uri, false);
148     }
149 
150     public void bind(String xPath, String initialValue, String name) throws JDOMException, JaxenException {
151         if (editorSession.getEditedXML() == null) {
152             createEmptyDocumentFromXPath(xPath);
153         }
154 
155         if (currentBinding == null) {
156             currentBinding = editorSession.getRootBinding();
157         }
158 
159         setCurrentBinding(new MCRBinding(xPath, initialValue, name, currentBinding));
160     }
161 
162     private void setCurrentBinding(MCRBinding binding) {
163         this.currentBinding = binding;
164         editorSession.getValidator().setValidationMarker(currentBinding);
165     }
166 
167     private void createEmptyDocumentFromXPath(String xPath) throws JaxenException, JDOMException {
168         Element root = createRootElement(xPath);
169         editorSession.setEditedXML(new Document(root));
170         editorSession.setBreakpoint("Starting with empty XML document");
171     }
172 
173     private Element createRootElement(String xPath) throws JaxenException {
174         BaseXPath baseXPath = new BaseXPath(xPath, new DocumentNavigator());
175         LocationPath lp = (LocationPath) (baseXPath.getRootExpr());
176         NameStep nameStep = (NameStep) (lp.getSteps().get(0));
177         String prefix = nameStep.getPrefix();
178         Namespace ns = prefix.isEmpty() ? Namespace.NO_NAMESPACE : MCRConstants.getStandardNamespace(prefix);
179         return new Element(nameStep.getLocalName(), ns);
180     }
181 
182     public void setValues(String value) {
183         currentBinding.setValues(value);
184     }
185 
186     public void setDefault(String value) throws JaxenException, JDOMException {
187         if (buildFilterXSL) {
188             storeValueForFilterBuilder(value);
189         }
190 
191         currentBinding.setDefault(value);
192         editorSession.getSubmission().markDefaultValue(currentBinding.getAbsoluteXPath(), value);
193     }
194 
195     private void storeValueForFilterBuilder(String value) throws JDOMException, JaxenException {
196         String xPath = currentBinding.getAbsoluteXPath();
197         String suffix = (currentBinding.getBoundNode() instanceof Element ? "/@" : "") + "_values_";
198         new MCRBinding(xPath + suffix, value, null, editorSession.getRootBinding());
199     }
200 
201     public void unbind() {
202         setCurrentBinding(currentBinding.getParent());
203     }
204 
205     public String getAbsoluteXPath() {
206         return currentBinding.getAbsoluteXPath();
207     }
208 
209     public String getValue() {
210         return currentBinding.getValue();
211     }
212 
213     public boolean hasValue(String value) {
214         editorSession.getSubmission().mark2checkResubmission(currentBinding);
215         return currentBinding.hasValue(value);
216     }
217 
218     public void toggleWithinSelectElement() {
219         withinSelectElement = !withinSelectElement;
220     }
221 
222     public boolean isWithinSelectElement() {
223         return withinSelectElement;
224     }
225 
226     public String replaceXPaths(String text) {
227         return getXPathEvaluator().replaceXPaths(text, false);
228     }
229 
230     public String replaceXPathOrI18n(String expression) {
231         return getXPathEvaluator().replaceXPathOrI18n(expression);
232     }
233 
234     public String evaluateXPath(String xPathExpression) {
235         return getXPathEvaluator().evaluateXPath(xPathExpression);
236     }
237 
238     public boolean test(String xPathExpression) {
239         return getXPathEvaluator().test(xPathExpression);
240     }
241 
242     public MCRXPathEvaluator getXPathEvaluator() {
243         if (currentBinding != null) {
244             return currentBinding.getXPathEvaluator();
245         } else {
246             return new MCRXPathEvaluator(editorSession.getVariables(), (Parent) null);
247         }
248     }
249 
250     public String repeat(String xPath, int minRepeats, int maxRepeats, String method)
251         throws JDOMException, JaxenException {
252         int numRepeatsToBuild = buildFilterXSL ? 1 : minRepeats;
253         MCRRepeatBindinginding.html#MCRRepeatBinding">MCRRepeatBinding repeat = new MCRRepeatBinding(xPath, currentBinding, numRepeatsToBuild, maxRepeats, method);
254         setCurrentBinding(repeat);
255         return StringUtils.repeat("a ", repeat.getBoundNodes().size());
256     }
257 
258     private MCRRepeatBinding getCurrentRepeat() {
259         MCRBinding binding = currentBinding;
260         while (!(binding instanceof MCRRepeatBinding)) {
261             binding = binding.getParent();
262         }
263         return (MCRRepeatBinding) binding;
264     }
265 
266     public int getNumRepeats() {
267         return getCurrentRepeat().getBoundNodes().size();
268     }
269 
270     public int getMaxRepeats() {
271         return getCurrentRepeat().getMaxRepeats();
272     }
273 
274     public int getRepeatPosition() {
275         return getCurrentRepeat().getRepeatPosition();
276     }
277 
278     public void bindRepeatPosition() throws JDOMException, JaxenException {
279         setCurrentBinding(getCurrentRepeat().bindRepeatPosition());
280         editorSession.getValidator().setValidationMarker(currentBinding);
281     }
282 
283     public String getSwapParameter(String action) throws JaxenException {
284         boolean direction = action.equals("down") ? MCRSwapTarget.MOVE_DOWN : MCRSwapTarget.MOVE_UP;
285         return MCRSwapTarget.getSwapParameter(getCurrentRepeat(), direction);
286     }
287 
288     public String getInsertParameter() throws JaxenException {
289         return MCRInsertTarget.getInsertParameter(getCurrentRepeat());
290     }
291 
292     public int nextAnchorID() {
293         return ++anchorID;
294     }
295 
296     public int getAnchorID() {
297         return anchorID;
298     }
299 
300     public int previousAnchorID() {
301         return (anchorID == 0 ? 1 : anchorID - 1);
302     }
303 
304     public void loadResource(String uri, String name) {
305         Element resource = MCRURIResolver.instance().resolve(uri);
306         editorSession.getVariables().put(name, resource);
307     }
308 
309     public void addValidationRule(Node ruleElement) {
310         editorSession.getValidator().addRule(currentBinding.getAbsoluteXPath(), ruleElement);
311     }
312 
313     public boolean hasValidationError() {
314         return editorSession.getValidator().hasError(currentBinding);
315     }
316 
317     public Node getFailedValidationRule() {
318         return editorSession.getValidator().getFailedRule(currentBinding).getRuleElement();
319     }
320 
321     public NodeSet getFailedValidationRules() {
322         NodeSet nodeSet = new NodeSet();
323         for (MCRValidator failedRule : editorSession.getValidator().getFailedRules()) {
324             nodeSet.addNode(failedRule.getRuleElement());
325         }
326         return nodeSet;
327     }
328 
329     public String getSubselectParam(String href) {
330         return currentBinding.getAbsoluteXPath() + ":" + MCRSubselectTarget.encode(href);
331     }
332 
333     public NodeSet getAdditionalParameters() throws ParserConfigurationException, TransformerException {
334         org.w3c.dom.Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
335         NodeSet nodeSet = new NodeSet();
336 
337         Map<String, String[]> parameters = editorSession.getRequestParameters();
338         for (String name : parameters.keySet()) {
339             for (String value : parameters.get(name)) {
340                 if ((value != null) && !value.isEmpty()) {
341                     nodeSet.addNode(buildAdditionalParameterElement(dom, name, value));
342                 }
343             }
344         }
345 
346         String xPaths2CheckResubmission = editorSession.getSubmission().getXPaths2CheckResubmission();
347         if (!xPaths2CheckResubmission.isEmpty()) {
348             nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSubmission.PREFIX_CHECK_RESUBMISSION,
349                 xPaths2CheckResubmission));
350         }
351 
352         Map<String, String> defaultValues = editorSession.getSubmission().getDefaultValues();
353         for (String xPath : defaultValues.keySet()) {
354             nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSubmission.PREFIX_DEFAULT_VALUE + xPath,
355                 defaultValues.get(xPath)));
356         }
357 
358         editorSession.setBreakpoint("After transformation to HTML");
359         nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSessionStore.XEDITOR_SESSION_PARAM,
360             editorSession.getCombinedSessionStepID()));
361 
362         return nodeSet;
363     }
364 
365     private org.w3c.dom.Element buildAdditionalParameterElement(org.w3c.dom.Document doc, String name, String value) {
366         org.w3c.dom.Element element = doc.createElement("param");
367         element.setAttribute("name", name);
368         element.setTextContent(value);
369         return element;
370     }
371 
372     public void addCleanupRule(String xPath, String relevantIf) {
373         editorSession.getXMLCleaner().addRule(xPath, relevantIf);
374     }
375 
376     public void declareParameter(String name, String defaultValue) {
377         Object currentValue = editorSession.getVariables().get(name);
378 
379         if ((currentValue == null) || "".equals(currentValue)) {
380             editorSession.getVariables().put(name, defaultValue == null ? "" : defaultValue);
381         }
382     }
383 }