1 17 18 19 20 package org.apache.lenya.cms.cocoon.acting; 21 22 import java.io.File ; 23 import java.net.URL ; 24 import java.util.Enumeration ; 25 import java.util.HashMap ; 26 import java.util.Map ; 27 28 import javax.xml.parsers.DocumentBuilder ; 29 import javax.xml.parsers.DocumentBuilderFactory ; 30 import javax.xml.transform.Transformer ; 31 import javax.xml.transform.TransformerFactory ; 32 import javax.xml.transform.dom.DOMSource ; 33 import javax.xml.transform.dom.DOMResult ; 34 import javax.xml.transform.stream.StreamResult ; 35 import javax.xml.transform.stream.StreamSource ; 36 37 import org.apache.avalon.framework.parameters.Parameters; 38 import org.apache.avalon.framework.thread.ThreadSafe; 39 import org.apache.cocoon.acting.AbstractConfigurableAction; 40 import org.apache.cocoon.environment.ObjectModelHelper; 41 import org.apache.cocoon.environment.Redirector; 42 import org.apache.cocoon.environment.Request; 43 import org.apache.cocoon.environment.SourceResolver; 44 import org.apache.commons.lang.StringUtils; 45 import org.apache.lenya.xml.DocumentHelper; 46 import org.apache.lenya.xml.RelaxNG; 47 import org.apache.lenya.xml.XPath; 48 49 import org.w3c.dom.Document ; 50 import org.w3c.dom.Node ; 51 import org.w3c.dom.NodeList ; 52 53 import org.xmldb.common.xml.queries.XObject; 54 import org.xmldb.common.xml.queries.XPathQuery; 55 import org.xmldb.common.xml.queries.XPathQueryFactory; 56 import org.xmldb.common.xml.queries.XUpdateQuery; 57 import org.xmldb.xupdate.lexus.XUpdateQueryImpl; 58 59 73 public class HTMLFormSaveAction extends AbstractConfigurableAction implements ThreadSafe { 74 75 class XUpdateAttributes { 76 public String xupdateAttrExpr = ""; 77 public String tagID = ""; 78 79 public XUpdateAttributes(String xupdateAttrExpr, String tagID) { 80 this.xupdateAttrExpr = xupdateAttrExpr; 81 this.tagID = tagID; 82 } 83 } 84 85 98 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, 99 Parameters parameters) throws Exception { 100 File sitemap = new File (new URL (resolver.resolveURI("").getURI()).getFile()); 101 File file = new File (sitemap.getAbsolutePath() + File.separator 102 + parameters.getParameter("file")); 103 File schema = new File (sitemap.getAbsolutePath() + File.separator 104 + parameters.getParameter("schema")); 105 File unnumberTagsXSL = new File (sitemap.getAbsolutePath() + File.separator 106 + parameters.getParameter("unnumberTagsXSL")); 107 File numberTagsXSL = new File (sitemap.getAbsolutePath() + File.separator 108 + parameters.getParameter("numberTagsXSL")); 109 110 Request request = ObjectModelHelper.getRequest(objectModel); 111 112 if (request.getParameter("cancel") != null) { 113 getLogger().warn(".act(): Editing has been canceled"); 114 file.delete(); 115 return null; 116 } else { 117 if (file.isFile()) { 118 getLogger().debug(".act(): Save modifications to " + file.getAbsolutePath()); 119 120 try { 121 Document document = null; 122 DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance(); 123 parserFactory.setValidating(false); 124 parserFactory.setNamespaceAware(true); 125 parserFactory.setIgnoringElementContentWhitespace(true); 126 DocumentBuilder builder = parserFactory.newDocumentBuilder(); 127 document = builder.parse(file.getAbsolutePath()); 128 System.setProperty("org.xmldb.common.xml.queries.XPathQueryFactory", 129 "org.xmldb.common.xml.queries.xalan2.XPathQueryFactoryImpl"); 130 131 XPathQuery xpath = XPathQueryFactory.newInstance().newXPathQuery(); 132 XUpdateQuery xq = new XUpdateQueryImpl(); 133 134 String editSelect = null; 135 Enumeration params = request.getParameterNames(); 136 while (params.hasMoreElements()) { 137 String pname = (String ) params.nextElement(); 138 getLogger().debug("Parameter: " + pname + " (" 139 + request.getParameter(pname) + ")"); 140 141 if (editSelect == null && pname.indexOf("edit[") >= 0 143 && pname.endsWith("].x")) { 144 editSelect = pname.substring(5, pname.length() - 3); 145 getLogger().debug("Edit: " + editSelect); 146 } 147 148 if (pname.indexOf("<xupdate:") == 0) { 151 String select = pname.substring(pname.indexOf("select") + 8); 152 select = select.substring(0, select.indexOf("\"")); 153 getLogger().debug(".act() Select Node: " + select); 154 155 xpath.setQString(select); 157 XObject result = xpath.execute(document); 158 NodeList selectionNodeList = result.nodeset(); 159 if (selectionNodeList.getLength() == 0) { 160 getLogger() 161 .debug(".act(): Node does not exist (might have been deleted during update): " 162 + select); 163 } else { 164 String xupdateModifications = null; 165 if (pname.indexOf("xupdate:update-parent") > 0) { 168 getLogger().debug("UPDATE PARENT Node: " + pname); 169 if (pname.indexOf("<![CDATA[") > 0) { 172 xupdateModifications = updateCDATA(request, pname, true); 173 } else { 174 xupdateModifications = update(request, 175 pname, 176 select, 177 selectionNodeList, 178 true); 179 } 180 } else if (pname.indexOf("xupdate:update") > 0) { 181 getLogger().debug("UPDATE Node: " + pname); 182 if (pname.indexOf("<![CDATA[") > 0) { 185 xupdateModifications = updateCDATA(request, pname, false); 186 } else { 187 xupdateModifications = update(request, 188 pname, 189 select, 190 selectionNodeList, 191 false); 192 } 193 } else if (pname.indexOf("xupdate:append") > 0 194 && pname.endsWith(">.x")) { 195 xupdateModifications = append(pname.substring(0, 196 pname.length() - 2)); 197 } else if (pname.indexOf("xupdate:insert-before") > 0 199 && pname.endsWith("/>")) { 200 if (!request.getParameter(pname).equals("null")) { 201 xupdateModifications = insertBefore(request 202 .getParameter(pname)); 203 } 204 } else if (pname.indexOf("xupdate:insert-before") > 0 206 && pname.endsWith(">.x")) { 207 xupdateModifications = insertBefore(pname.substring(0, pname 208 .length() - 2)); 209 } else if (pname.indexOf("xupdate:insert-after") > 0 211 && pname.endsWith("/>")) { 212 if (!request.getParameter(pname).equals("null")) { 213 xupdateModifications = insertAfter(request 214 .getParameter(pname)); 215 } 216 } else if (pname.indexOf("xupdate:insert-after") > 0 218 && pname.endsWith(">.x")) { 219 xupdateModifications = insertAfter(pname.substring(0, pname 220 .length() - 2)); 221 } else if (pname.indexOf("xupdate:remove") > 0 222 && pname.endsWith("/>.x")) { 223 xupdateModifications = remove(pname.substring(0, 224 pname.length() - 2)); 225 } else if (pname.endsWith(">.y")) { 226 getLogger().debug("Don't handle this: " + pname); 227 } else { 228 getLogger().debug("Don't handle this either: " + pname); 229 } 230 231 String namespaces = request.getParameter("namespaces"); 233 234 if (xupdateModifications != null) { 238 xupdateModifications = "<?xml version=\"1.0\"?>" 239 + addHiddenNamespaces(namespaces, xupdateModifications); 240 } 241 242 if (xupdateModifications != null) { 244 getLogger().info("Execute XUpdate Modifications: " 245 + xupdateModifications); 246 xq.setQString(xupdateModifications); 247 xq.execute(document); 248 } else { 249 getLogger() 250 .debug("Parameter did not match any xupdate command: " 251 + pname); 252 } 253 } 254 } 255 } 256 257 269 270 if (schema.isFile()) { 272 DocumentHelper.writeDocument(document, new File (file.getCanonicalPath() 273 + ".validate")); 274 String message = validateDocument(schema, new File (file.getCanonicalPath() 275 + ".validate"), unnumberTagsXSL); 276 if (message != null) { 277 getLogger().error("RELAX NG Validation failed: " + message); 278 HashMap hmap = new HashMap (); 279 hmap.put("message", "RELAX NG Validation failed: " + message); 280 return hmap; 281 } 282 } else { 283 getLogger().warn("No such schema: " + schema.getAbsolutePath()); 284 } 285 286 Document renumberedDocument = renumberDocument(document, 287 unnumberTagsXSL, 288 numberTagsXSL); 289 DocumentHelper.writeDocument(renumberedDocument, file); 290 291 if (request.getParameter("save") != null) { 293 getLogger().info(".act(): Save"); 294 return null; 295 } else { 296 299 HashMap hmap = new HashMap (); 300 if (editSelect != null) { 301 hmap.put("editSelect", editSelect); 302 } 303 return hmap; 304 } 305 } catch (NullPointerException e) { 306 getLogger().error("NullPointerException", e); 307 HashMap hmap = new HashMap (); 308 hmap.put("message", "NullPointerException"); 309 return hmap; 310 } catch (Exception e) { 311 getLogger().error("Exception: " + e.getMessage(), e); 312 HashMap hmap = new HashMap (); 313 if (e.getMessage() != null) { 314 hmap.put("message", e.getMessage()); 315 } else { 316 hmap.put("message", "No message (" + e.getClass().getName() + ")"); 317 } 318 return hmap; 319 } 320 } else { 321 getLogger().error("No such file: " + file.getAbsolutePath()); 322 HashMap hmap = new HashMap (); 323 hmap.put("message", "No such file: " + file.getAbsolutePath()); 324 return hmap; 325 } 326 } 327 } 328 329 334 private XUpdateAttributes getAttributes(Node node) { 335 336 String xupdateString = ""; 337 String tagID = ""; 338 org.w3c.dom.NamedNodeMap attributes = node.getAttributes(); 339 if (attributes != null) { 340 for (int i = 0; i < attributes.getLength(); i++) { 341 org.w3c.dom.Attr attribute = (org.w3c.dom.Attr ) attributes.item(i); 342 getLogger().debug(".getAttributes(): " + attribute.getName() + " " 343 + attribute.getValue()); 344 if (!attribute.getName().equals("tagID")) { 345 String namespace = attribute.getNamespaceURI(); 346 getLogger().debug(".getAttributes(): Namespace: " + namespace); 347 String namespaceAttribute = ""; 348 if (namespace != null) { 349 namespaceAttribute = " namespace=\"" + namespace + "\""; 350 } 351 xupdateString = xupdateString + "<xupdate:attribute name=\"" 352 + attribute.getName() + "\"" + namespaceAttribute + ">" 353 + attribute.getValue() + "</xupdate:attribute>"; 354 } else { 355 xupdateString = xupdateString 356 + "<xupdate:attribute name=\"tagID\">temp</xupdate:attribute>"; 357 tagID = attribute.getValue(); 358 } 359 } 360 } else { 361 xupdateString = ""; 362 } 363 getLogger().debug("Attributes: " + xupdateString); 364 365 return new XUpdateAttributes(xupdateString, tagID); 366 } 367 368 373 private XUpdateAttributes getAttributes(String update, String tagID) { 374 getLogger().debug(update); 375 376 String xupdateString = "<xupdate:attribute name=\"tagID\">temp</xupdate:attribute>"; 377 378 String [] attributes = update.substring(0, update.indexOf(">")).split(" "); 379 for (int i = 1; i < attributes.length; i++) { 380 int index = attributes[i].indexOf("="); 382 if (index > 0) { 383 String name = attributes[i].substring(0, index); 384 String value = attributes[i].substring(index + 2, attributes[i].length() - 1); 385 if (name.indexOf("xmlns") < 0) { 386 xupdateString = xupdateString + "<xupdate:attribute name=\"" + name + "\">" 387 + value + "</xupdate:attribute>"; 388 } 389 } 390 } 391 392 getLogger().debug("Attributes: " + xupdateString); 393 394 return new XUpdateAttributes(xupdateString, tagID); 395 } 396 397 403 private String update(Request request, String pname, String select, NodeList selectionNodeList, 404 boolean parent) { 405 getLogger().debug("Update node: " + select); 406 407 Node nodeToCopy = selectionNodeList.item(0); 408 if (nodeToCopy.getNodeType() == Node.ATTRIBUTE_NODE) { 410 getLogger().debug("Update attribute: " + select); 411 412 String xupdateUpdate = pname + request.getParameter(pname) + "</xupdate:update>"; 413 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" 414 + xupdateUpdate + "</xupdate:modifications>"; 415 421 } else { 422 getLogger().debug("Update element: " + select); 423 424 String namespace = nodeToCopy.getNamespaceURI(); 425 String namespaceAttribute = ""; 426 if (namespace != null) { 427 namespaceAttribute = " namespace=\"" + namespace + "\""; 428 } 429 XUpdateAttributes xa = getAttributes(nodeToCopy); 432 String xupdateInsertAfter = null; 433 if (parent) { 434 xa = getAttributes(request.getParameter(pname), xa.tagID); 435 xupdateInsertAfter = "<xupdate:insert-after select=\"" + select 436 + " \"><xupdate:element name=\"" 437 + new XPath(select).getNameWithoutPredicates() + "\"" + namespaceAttribute 438 + ">" + xa.xupdateAttrExpr + removeParent(request.getParameter(pname)) 439 + "</xupdate:element></xupdate:insert-after>"; 440 } else { 441 xupdateInsertAfter = "<xupdate:insert-after select=\"" + select 442 + " \"><xupdate:element name=\"" 443 + new XPath(select).getNameWithoutPredicates() + "\"" + namespaceAttribute 444 + ">" + xa.xupdateAttrExpr + request.getParameter(pname) 445 + "</xupdate:element></xupdate:insert-after>"; 446 } 447 getLogger().debug(".update(): Update Node (insert-after): " + xupdateInsertAfter); 448 449 String xupdateRemove = "<xupdate:remove select=\"" + select + " \"/>"; 450 getLogger().debug(".update(): Update Node (remove): " + xupdateRemove); 451 452 String xupdateUpdateAttribute = "<xupdate:update select=\"" 453 + new XPath(select).removePredicates(select) + "[@tagID='temp']/@tagID" 454 + " \">" + xa.tagID + "</xupdate:update>"; 455 getLogger().debug(".update(): Update Node (update tagID attribute): " 456 + xupdateUpdateAttribute); 457 458 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" 459 + xupdateInsertAfter + xupdateRemove + xupdateUpdateAttribute 460 + "</xupdate:modifications>"; 461 } 462 } 463 464 469 private String updateCDATA(Request request, String pname, boolean parent) { 470 String xupdateUpdate = pname + request.getParameter(pname) + "]]></xupdate:update>"; 471 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" 472 + xupdateUpdate + "</xupdate:modifications>"; 473 } 474 475 478 private String append(String pname) { 479 getLogger().debug(".append() APPEND Node: " + pname); 480 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" + pname 481 + "</xupdate:modifications>"; 482 } 483 484 487 private String insertBefore(String pname) { 488 getLogger().debug(".insertBefore() INSERT-BEFORE Node: " + pname); 489 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" + pname 490 + "</xupdate:modifications>"; 491 } 492 493 496 private String insertAfter(String pname) { 497 getLogger().debug(".insertAfter() INSERT-AFTER Node: " + pname); 498 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" + pname 499 + "</xupdate:modifications>"; 500 } 501 502 505 private String remove(String pname) { 506 getLogger().debug(".remove() REMOVE Node: " + pname); 507 return "<xupdate:modifications xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" + pname 508 + "</xupdate:modifications>"; 509 } 510 511 514 private String validateDocument(File schema, File file, File unnumberTagsXSL) { 515 try { 516 TransformerFactory tf = TransformerFactory.newInstance(); 518 Transformer t = tf.newTransformer(new StreamSource (unnumberTagsXSL)); 519 t.transform(new StreamSource (file), new StreamResult (new File (file.getAbsolutePath() 520 + ".unnumber"))); 521 522 return RelaxNG.validate(schema, new File (file.getAbsolutePath() + ".unnumber")); 524 } catch (Exception e) { 525 getLogger().error("Validating failed:", e); 526 return "" + e; 527 } 528 } 529 530 533 private Document renumberDocument(Document doc, File unnumberTagsXSL, File numberTagsXSL) { 534 535 try { 536 DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance(); 537 parserFactory.setValidating(false); 538 parserFactory.setNamespaceAware(true); 539 parserFactory.setIgnoringElementContentWhitespace(true); 540 DocumentBuilder builder = parserFactory.newDocumentBuilder(); 541 542 TransformerFactory tf = TransformerFactory.newInstance(); 543 544 Transformer ut = tf.newTransformer(new StreamSource (unnumberTagsXSL)); 546 Document unnumberedDocument = builder.newDocument(); 547 ut.transform(new DOMSource (doc), new DOMResult (unnumberedDocument)); 548 549 Transformer nt = tf.newTransformer(new StreamSource (numberTagsXSL)); 551 Document renumberedDocument = builder.newDocument(); 552 nt.transform(new DOMSource (unnumberedDocument), new DOMResult (renumberedDocument)); 553 554 return renumberedDocument; 555 } catch (Exception e) { 556 getLogger().error("" + e); 557 } 558 559 return null; 560 } 561 562 565 private String removeParent(String xmlSnippet) { 566 String xmlSnippetWithoutParent = xmlSnippet; 567 xmlSnippetWithoutParent = xmlSnippetWithoutParent.substring(xmlSnippetWithoutParent 568 .indexOf(">") + 1); 569 xmlSnippetWithoutParent = StringUtils.reverse(xmlSnippetWithoutParent); 570 xmlSnippetWithoutParent = xmlSnippetWithoutParent.substring(xmlSnippetWithoutParent 571 .indexOf("<") + 1); 572 xmlSnippetWithoutParent = StringUtils.reverse(xmlSnippetWithoutParent); 573 return xmlSnippetWithoutParent; 574 } 575 576 579 private String addHiddenNamespaces(String namespaces, String xupdateModifications) { 580 getLogger().debug("Namespaces: " + namespaces); 581 582 if (namespaces == null) { 583 getLogger().debug("No additional namespaces"); 584 return xupdateModifications; 585 } 586 587 String [] namespace = namespaces.split(" "); 588 String ns = ""; 589 for (int i = 0; i < namespace.length; i++) { 590 if ((ns.indexOf(namespace[i]) < 0) && (xupdateModifications.indexOf(namespace[i]) < 0)) { 591 ns = ns + " " + namespace[i]; 592 } else { 593 getLogger().debug("Redundant namespace: " + namespace[i]); 594 } 595 } 596 597 int endOfFirstNode = xupdateModifications.indexOf(">"); 598 return xupdateModifications.substring(0, endOfFirstNode) + " " + ns 599 + xupdateModifications.substring(endOfFirstNode); 600 } 601 } | Popular Tags |