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.lang3.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.MCRClassTools;
42  import org.mycore.common.MCRConstants;
43  import org.mycore.common.MCRException;
44  import org.mycore.common.content.MCRContent;
45  import org.mycore.common.content.MCRWrappedContent;
46  import org.mycore.common.content.transformer.MCRContentTransformer;
47  import org.mycore.common.content.transformer.MCRContentTransformerFactory;
48  import org.mycore.common.content.transformer.MCRParameterizedTransformer;
49  import org.mycore.common.content.transformer.MCRXSLTransformer;
50  import org.mycore.common.xml.MCRURIResolver;
51  import org.mycore.common.xml.MCRXPathEvaluator;
52  import org.mycore.common.xsl.MCRParameterCollector;
53  import org.mycore.frontend.xeditor.target.MCRInsertTarget;
54  import org.mycore.frontend.xeditor.target.MCRSubselectTarget;
55  import org.mycore.frontend.xeditor.target.MCRSwapTarget;
56  import org.mycore.frontend.xeditor.validation.MCRValidator;
57  import org.w3c.dom.Attr;
58  import org.w3c.dom.NamedNodeMap;
59  import org.w3c.dom.Node;
60  import org.xml.sax.SAXException;
61  
62  /**
63   * @author Frank L\u00FCtzenkirchen
64   */
65  public class MCRXEditorTransformer {
66  
67      public int anchorID = 0;
68  
69      private MCREditorSession editorSession;
70  
71      private MCRBinding currentBinding;
72  
73      private MCRParameterCollector transformationParameters;
74  
75      private boolean withinSelectElement = false;
76  
77      private boolean withinSelectMultiple = false;
78  
79      public MCRXEditorTransformer(MCREditorSession editorSession, MCRParameterCollector transformationParameters) {
80          this.editorSession = editorSession;
81          this.transformationParameters = transformationParameters;
82      }
83  
84      public MCRContent transform(MCRContent editorSource) throws IOException, JDOMException, SAXException {
85          editorSession.getValidator().clearRules();
86          editorSession.getSubmission().clear();
87  
88          MCRContentTransformer transformer = MCRContentTransformerFactory.getTransformer("xeditor");
89          if (transformer instanceof MCRParameterizedTransformer) {
90              transformationParameters.setParameter("transformer", this);
91              MCRContent result = ((MCRParameterizedTransformer) transformer).transform(editorSource,
92                  transformationParameters);
93              if (result instanceof MCRWrappedContent
94                  && result.getClass().getName().contains(MCRXSLTransformer.class.getName())) {
95                  //lazy transformation make JUnit tests fail
96                  result = ((MCRWrappedContent) result).getBaseContent();
97              }
98              editorSession.getValidator().clearValidationResults();
99              return result;
100         } else {
101             throw new MCRException("Xeditor needs parameterized MCRContentTransformer: " + transformer);
102         }
103     }
104 
105     public void addNamespace(String prefix, String uri) {
106         MCRConstants.registerNamespace(Namespace.getNamespace(prefix, uri));
107     }
108 
109     public void readSourceXML(String uri) throws JDOMException, IOException, SAXException, TransformerException {
110         editorSession.setEditedXML(uri);
111     }
112 
113     public void setCancelURL(String cancelURL) {
114         editorSession.setCancelURL(cancelURL);
115     }
116 
117     public void initializePostprocessor(Node postProcessorNode) {
118         NamedNodeMap attributes = postProcessorNode.getAttributes();
119         int attributesLength = attributes.getLength();
120         HashMap<String, String> attributeMap = new HashMap<>();
121         for (int i = 0; i < attributesLength; i++) {
122             Attr item = (Attr) attributes.item(i); // this should be save because we called getAttributes earlier
123             String attrName = item.getName();
124             String attrValue = item.getValue();
125             attributeMap.put(attrName, attrValue);
126         }
127 
128         editorSession.getPostProcessor().setAttributes(attributeMap);
129     }
130 
131     public void setPostProcessor(String clazz) {
132         try {
133             MCRXEditorPostProcessor instance = ((MCRXEditorPostProcessor) MCRClassTools.forName(clazz)
134                 .getDeclaredConstructor()
135                 .newInstance());
136             editorSession.setPostProcessor(instance);
137         } catch (ReflectiveOperationException e) {
138             throw new MCRException("Could not initialize Post-Processor with class" + clazz, e);
139         }
140     }
141 
142     public String replaceParameters(String uri) {
143         return getXPathEvaluator().replaceXPaths(uri, false);
144     }
145 
146     public void bind(String xPath, String initialValue, String name) throws JDOMException, JaxenException {
147         if (editorSession.getEditedXML() == null) {
148             createEmptyDocumentFromXPath(xPath);
149         }
150 
151         if (currentBinding == null) {
152             currentBinding = editorSession.getRootBinding();
153         }
154 
155         setCurrentBinding(new MCRBinding(xPath, initialValue, name, currentBinding));
156     }
157 
158     private void setCurrentBinding(MCRBinding binding) {
159         this.currentBinding = binding;
160         editorSession.getValidator().setValidationMarker(currentBinding);
161     }
162 
163     private void createEmptyDocumentFromXPath(String xPath) throws JaxenException, JDOMException {
164         Element root = createRootElement(xPath);
165         editorSession.setEditedXML(new Document(root));
166         editorSession.setBreakpoint("Starting with empty XML document");
167     }
168 
169     private Element createRootElement(String xPath) throws JaxenException {
170         BaseXPath baseXPath = new BaseXPath(xPath, new DocumentNavigator());
171         LocationPath lp = (LocationPath) (baseXPath.getRootExpr());
172         NameStep nameStep = (NameStep) (lp.getSteps().get(0));
173         String prefix = nameStep.getPrefix();
174         Namespace ns = prefix.isEmpty() ? Namespace.NO_NAMESPACE : MCRConstants.getStandardNamespace(prefix);
175         return new Element(nameStep.getLocalName(), ns);
176     }
177 
178     public void setValues(String value) {
179         currentBinding.setValues(value);
180     }
181 
182     public void setDefault(String value) throws JaxenException, JDOMException {
183         currentBinding.setDefault(value);
184         editorSession.getSubmission().markDefaultValue(currentBinding.getAbsoluteXPath(), value);
185     }
186 
187     public void unbind() {
188         setCurrentBinding(currentBinding.getParent());
189     }
190 
191     public String getAbsoluteXPath() {
192         return currentBinding.getAbsoluteXPath();
193     }
194 
195     public String getValue() {
196         return currentBinding.getValue();
197     }
198 
199     public boolean hasValue(String value) {
200         editorSession.getSubmission().mark2checkResubmission(currentBinding);
201         return currentBinding.hasValue(value);
202     }
203 
204     public void toggleWithinSelectElement(String attrMultiple) {
205         withinSelectElement = !withinSelectElement;
206         withinSelectMultiple = "multiple".equals(attrMultiple);
207     }
208 
209     public boolean isWithinSelectElement() {
210         return withinSelectElement;
211     }
212 
213     public boolean isWithinSelectMultiple() {
214         return withinSelectMultiple;
215     }
216 
217     public String replaceXPaths(String text) {
218         return getXPathEvaluator().replaceXPaths(text, false);
219     }
220 
221     public String replaceXPathOrI18n(String expression) {
222         return getXPathEvaluator().replaceXPathOrI18n(expression);
223     }
224 
225     public String evaluateXPath(String xPathExpression) {
226         return getXPathEvaluator().evaluateXPath(xPathExpression);
227     }
228 
229     public boolean test(String xPathExpression) {
230         return getXPathEvaluator().test(xPathExpression);
231     }
232 
233     public MCRXPathEvaluator getXPathEvaluator() {
234         if (currentBinding != null) {
235             return currentBinding.getXPathEvaluator();
236         } else {
237             return new MCRXPathEvaluator(editorSession.getVariables(), (Parent) null);
238         }
239     }
240 
241     public String repeat(String xPath, int minRepeats, int maxRepeats, String method)
242         throws JDOMException, JaxenException {
243         MCRRepeatBinding repeat = new MCRRepeatBinding(xPath, currentBinding, minRepeats, maxRepeats, method);
244         setCurrentBinding(repeat);
245         return StringUtils.repeat("a ", repeat.getBoundNodes().size());
246     }
247 
248     private MCRRepeatBinding getCurrentRepeat() {
249         MCRBinding binding = currentBinding;
250         while (!(binding instanceof MCRRepeatBinding)) {
251             binding = binding.getParent();
252         }
253         return (MCRRepeatBinding) binding;
254     }
255 
256     public int getNumRepeats() {
257         return getCurrentRepeat().getBoundNodes().size();
258     }
259 
260     public int getMaxRepeats() {
261         return getCurrentRepeat().getMaxRepeats();
262     }
263 
264     public int getRepeatPosition() {
265         return getCurrentRepeat().getRepeatPosition();
266     }
267 
268     public void bindRepeatPosition() throws JDOMException, JaxenException {
269         setCurrentBinding(getCurrentRepeat().bindRepeatPosition());
270         editorSession.getValidator().setValidationMarker(currentBinding);
271     }
272 
273     public String getSwapParameter(String action) throws JaxenException {
274         boolean direction = "down".equals(action) ? MCRSwapTarget.MOVE_DOWN : MCRSwapTarget.MOVE_UP;
275         return MCRSwapTarget.getSwapParameter(getCurrentRepeat(), direction);
276     }
277 
278     public String getInsertParameter() throws JaxenException {
279         return MCRInsertTarget.getInsertParameter(getCurrentRepeat());
280     }
281 
282     public int nextAnchorID() {
283         return ++anchorID;
284     }
285 
286     public int getAnchorID() {
287         return anchorID;
288     }
289 
290     public int previousAnchorID() {
291         return (anchorID == 0 ? 1 : anchorID - 1);
292     }
293 
294     public void loadResource(String uri, String name) {
295         Element resource = MCRURIResolver.instance().resolve(uri);
296         editorSession.getVariables().put(name, resource);
297     }
298 
299     public void addValidationRule(Node ruleElement) {
300         editorSession.getValidator().addRule(currentBinding.getAbsoluteXPath(), ruleElement);
301     }
302 
303     public boolean hasValidationError() {
304         return editorSession.getValidator().hasError(currentBinding);
305     }
306 
307     public Node getFailedValidationRule() {
308         return editorSession.getValidator().getFailedRule(currentBinding).getRuleElement();
309     }
310 
311     public NodeSet getFailedValidationRules() {
312         NodeSet nodeSet = new NodeSet();
313         for (MCRValidator failedRule : editorSession.getValidator().getFailedRules()) {
314             nodeSet.addNode(failedRule.getRuleElement());
315         }
316         return nodeSet;
317     }
318 
319     public String getSubselectParam(String href) {
320         return currentBinding.getAbsoluteXPath() + ":" + MCRSubselectTarget.encode(href);
321     }
322 
323     public NodeSet getAdditionalParameters() throws ParserConfigurationException, TransformerException {
324         org.w3c.dom.Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
325         NodeSet nodeSet = new NodeSet();
326 
327         Map<String, String[]> parameters = editorSession.getRequestParameters();
328         for (String name : parameters.keySet()) {
329             for (String value : parameters.get(name)) {
330                 if ((value != null) && !value.isEmpty()) {
331                     nodeSet.addNode(buildAdditionalParameterElement(dom, name, value));
332                 }
333             }
334         }
335 
336         String xPaths2CheckResubmission = editorSession.getSubmission().getXPaths2CheckResubmission();
337         if (!xPaths2CheckResubmission.isEmpty()) {
338             nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSubmission.PREFIX_CHECK_RESUBMISSION,
339                 xPaths2CheckResubmission));
340         }
341 
342         Map<String, String> defaultValues = editorSession.getSubmission().getDefaultValues();
343         for (String xPath : defaultValues.keySet()) {
344             nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSubmission.PREFIX_DEFAULT_VALUE + xPath,
345                 defaultValues.get(xPath)));
346         }
347 
348         editorSession.setBreakpoint("After transformation to HTML");
349         nodeSet.addNode(buildAdditionalParameterElement(dom, MCREditorSessionStore.XEDITOR_SESSION_PARAM,
350             editorSession.getCombinedSessionStepID()));
351 
352         return nodeSet;
353     }
354 
355     private org.w3c.dom.Element buildAdditionalParameterElement(org.w3c.dom.Document doc, String name, String value) {
356         org.w3c.dom.Element element = doc.createElement("param");
357         element.setAttribute("name", name);
358         element.setTextContent(value);
359         return element;
360     }
361 
362     public void addCleanupRule(String xPath, String relevantIf) {
363         editorSession.getXMLCleaner().addRule(xPath, relevantIf);
364     }
365 
366     public void declareParameter(String name, String defaultValue) {
367         Object currentValue = editorSession.getVariables().get(name);
368 
369         if ((currentValue == null) || "".equals(currentValue)) {
370             editorSession.getVariables().put(name, defaultValue == null ? "" : defaultValue);
371         }
372     }
373 }