1 17 18 19 20 package org.apache.lenya.cms.publication; 21 22 import java.io.File ; 23 import java.io.IOException ; 24 import java.util.ArrayList ; 25 import java.util.Date ; 26 import java.util.List ; 27 import java.util.StringTokenizer ; 28 29 import javax.xml.parsers.ParserConfigurationException ; 30 import javax.xml.transform.TransformerException ; 31 32 import org.apache.lenya.xml.DocumentHelper; 33 import org.apache.lenya.xml.NamespaceHelper; 34 import org.apache.log4j.Logger; 35 import org.apache.xpath.XPathAPI; 36 import org.w3c.dom.Document ; 37 import org.w3c.dom.Element ; 38 import org.w3c.dom.NamedNodeMap ; 39 import org.w3c.dom.Node ; 40 import org.w3c.dom.NodeList ; 41 import org.xml.sax.SAXException ; 42 43 46 public class DefaultSiteTree implements SiteTree, LastModified { 47 private static Logger log = Logger.getLogger(DefaultSiteTree.class); 48 49 private static Object lock = new Object (); 50 51 public static final String SITE_TREE_FILENAME = "sitetree.xml"; 52 53 private Document document = null; 54 private File treefile = null; 55 private String area = ""; 57 58 private long lastModified = 0; 59 60 68 protected DefaultSiteTree(File pubDir, String area) throws SiteTreeException { 69 this( 70 new File ( 71 pubDir, 72 Publication.CONTENT_PATH 73 + File.separator 74 + area 75 + File.separator 76 + SITE_TREE_FILENAME)); 77 this.area = area; 78 } 79 80 89 public DefaultSiteTree(String treefilename) throws SiteTreeException { 90 this(new File (treefilename)); 91 } 92 93 102 public DefaultSiteTree(File treefile) throws SiteTreeException { 103 this.treefile = treefile; 104 105 try { 106 if (!treefile.isFile()) { 107 109 document = createDocument(); 110 } else { 111 document = DocumentHelper.readDocument(treefile); 113 } 114 } catch (ParserConfigurationException e) { 115 throw new SiteTreeException(e); 116 } catch (SAXException e) { 117 throw new SiteTreeException(e); 118 } catch (IOException e) { 119 throw new SiteTreeException(e); 120 } 121 122 } 123 124 128 protected synchronized void checkModified() { 129 if (area.equals(Publication.LIVE_AREA) 130 && treefile.isFile() 131 && treefile.lastModified() > lastModified) { 132 133 if (log.isDebugEnabled()) { 134 log.debug("Sitetree [" + treefile + "] has changed: reloading."); 135 } 136 137 try { 138 document = DocumentHelper.readDocument(treefile); 139 } catch (Exception e) { 140 throw new IllegalStateException (e.getMessage()); 141 } 142 lastModified = treefile.lastModified(); 143 } 144 } 145 146 153 public synchronized Document createDocument() throws ParserConfigurationException { 154 document = DocumentHelper.createDocument(NAMESPACE_URI, "site", null); 155 156 Element root = document.getDocumentElement(); 157 root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); 158 root.setAttribute( 159 "xsi:schemaLocation", 160 "http://apache.org/cocoon/lenya/sitetree/1.0 ../../../../resources/entities/sitetree.xsd"); 161 162 return document; 163 } 164 165 175 protected synchronized Node findNode(Node node, List ids) { 176 177 checkModified(); 178 179 if (ids.size() < 1) { 180 return node; 181 } else { 182 NodeList nodes = node.getChildNodes(); 183 184 for (int i = 0; i < nodes.getLength(); i++) { 185 NamedNodeMap attributes = nodes.item(i).getAttributes(); 186 187 if (attributes != null) { 188 Node idAttribute = attributes.getNamedItem("id"); 189 190 if (idAttribute != null 191 && !"".equals(idAttribute.getNodeValue()) 192 && idAttribute.getNodeValue().equals(ids.get(0))) { 193 return findNode(nodes.item(i), ids.subList(1, ids.size())); 194 } 195 } 196 } 197 } 198 199 return null; 201 } 202 203 206 public synchronized void addNode(SiteTreeNode node, String refDocumentId) throws SiteTreeException { 207 this.addNode( 208 node.getAbsoluteParentId(), 209 node.getId(), 210 node.getLabels(), 211 node.visibleInNav(), 212 node.getHref(), 213 node.getSuffix(), 214 node.hasLink(), 215 refDocumentId); 216 } 217 218 221 public synchronized void addNode(String parentid, String id, Label[] labels, boolean visibleInNav ) throws SiteTreeException { 222 addNode(parentid, id, labels, visibleInNav, null, null, false); 223 } 224 225 228 public synchronized void addNode(String parentid, String id, Label[] labels) throws SiteTreeException { 229 addNode(parentid, id, labels, true); 230 } 231 232 235 public synchronized void addNode(SiteTreeNode node) throws SiteTreeException { 236 this.addNode(node, null); 237 } 238 239 242 public synchronized void addNode( 243 String documentid, 244 Label[] labels, 245 boolean visibleInNav, 246 String href, 247 String suffix, 248 boolean link, 249 String refDocumentId) 250 throws SiteTreeException { 251 String parentid = ""; 252 StringTokenizer st = new StringTokenizer (documentid, "/"); 253 int length = st.countTokens(); 254 255 for (int i = 0; i < (length - 1); i++) { 256 parentid = parentid + "/" + st.nextToken(); 257 } 258 259 String id = st.nextToken(); 260 this.addNode(parentid, id, labels, visibleInNav, href, suffix, link, refDocumentId); 261 } 262 263 266 public synchronized void addNode( 267 String documentid, 268 Label[] labels, 269 boolean visibleInNav, 270 String href, 271 String suffix, 272 boolean link) 273 throws SiteTreeException { 274 this.addNode(documentid, labels, visibleInNav, href, suffix, link, null); 275 } 276 279 public synchronized void addNode( 280 String parentid, 281 String id, 282 Label[] labels, 283 boolean visibleInNav, 284 String href, 285 String suffix, 286 boolean link) 287 throws SiteTreeException { 288 this.addNode(parentid, id, labels, visibleInNav, href, suffix, link, null); 289 } 290 291 294 public synchronized void addNode( 295 String parentid, 296 String id, 297 Label[] labels, 298 boolean visibleInNav, 299 String href, 300 String suffix, 301 boolean link, 302 String refDocumentId) 303 throws SiteTreeException { 304 305 Node parentNode = getNodeInternal(parentid); 306 307 if (parentNode == null) { 308 throw new SiteTreeException( 309 "Parentid: " + parentid + " in " + area + " tree not found"); 310 } 311 312 log.debug("PARENT ELEMENT: " + parentNode); 313 314 Node childNode = getNodeInternal(parentid + "/" + id); 316 317 if (childNode != null) { 318 log.info("This node: " + parentid + "/" + id + " has already been inserted"); 319 320 return; 321 } 322 323 NamespaceHelper helper = new NamespaceHelper(NAMESPACE_URI, "", document); 325 Element child = helper.createElement(SiteTreeNodeImpl.NODE_NAME); 326 child.setAttribute(SiteTreeNodeImpl.ID_ATTRIBUTE_NAME, id); 327 328 if (visibleInNav) { 329 child.setAttribute(SiteTreeNodeImpl.VISIBLEINNAV_ATTRIBUTE_NAME, "true"); 330 } else { 331 child.setAttribute(SiteTreeNodeImpl.VISIBLEINNAV_ATTRIBUTE_NAME, "false"); 332 } 333 334 if ((href != null) && (href.length() > 0)) { 335 child.setAttribute(SiteTreeNodeImpl.HREF_ATTRIBUTE_NAME, href); 336 } 337 338 if ((suffix != null) && (suffix.length() > 0)) { 339 child.setAttribute(SiteTreeNodeImpl.SUFFIX_ATTRIBUTE_NAME, suffix); 340 } 341 342 if (link) { 343 child.setAttribute(SiteTreeNodeImpl.LINK_ATTRIBUTE_NAME, "true"); 344 } 345 346 for (int i = 0; i < labels.length; i++) { 347 String labelName = labels[i].getLabel(); 348 Element label = helper.createElement(SiteTreeNodeImpl.LABEL_NAME, labelName); 349 String labelLanguage = labels[i].getLanguage(); 350 351 if ((labelLanguage != null) && (labelLanguage.length() > 0)) { 352 label.setAttribute(SiteTreeNodeImpl.LANGUAGE_ATTRIBUTE_NAME, labelLanguage); 353 } 354 355 child.appendChild(label); 356 } 357 358 if (refDocumentId != null && !refDocumentId.equals("")) { 360 Node nextSibling = getNodeInternal(refDocumentId); 361 if (nextSibling != null) { 362 parentNode.insertBefore(child, nextSibling); 363 } else { 364 parentNode.appendChild(child); 365 } 366 } else { 367 parentNode.appendChild(child); 368 } 369 log.debug("Tree has been modified: " + document.getDocumentElement()); 370 } 371 375 public synchronized void addLabel(String documentId, Label label) { 376 SiteTreeNode node = getNode(documentId); 377 if (node != null) { 378 node.addLabel(label); 379 } 380 } 381 382 386 public synchronized void removeLabel(String documentId, Label label) { 387 SiteTreeNode node = getNode(documentId); 388 if (node != null) { 389 node.removeLabel(label); 390 } 391 } 392 393 396 public synchronized SiteTreeNode removeNode(String documentId) { 397 assert documentId != null; 398 399 Node node = removeNodeInternal(documentId); 400 401 if (node == null) { 402 return null; 403 } 404 405 return new SiteTreeNodeImpl(node, this); 406 } 407 408 411 public void deleteNode(String documentId) throws SiteTreeException { 412 Node node = this.getNodeInternal(documentId); 413 Node parentNode = node.getParentNode(); 414 Node newNode = parentNode.removeChild(node); 415 } 416 417 418 426 private synchronized Node removeNodeInternal(String documentId) { 427 Node node = this.getNodeInternal(documentId); 428 Node parentNode = node.getParentNode(); 429 Node newNode = parentNode.removeChild(node); 430 431 return newNode; 432 } 433 434 441 private synchronized Node getNodeInternal(String documentId) { 442 StringTokenizer st = new StringTokenizer (documentId, "/"); 443 ArrayList ids = new ArrayList (); 444 445 while (st.hasMoreTokens()) { 446 ids.add(st.nextToken()); 447 } 448 449 Node node = findNode(document.getDocumentElement(), ids); 450 return node; 451 } 452 453 456 public synchronized SiteTreeNode getNode(String documentId) { 457 assert documentId != null; 458 459 SiteTreeNode treeNode = null; 460 461 Node node = getNodeInternal(documentId); 462 if (node != null) { 463 treeNode = new SiteTreeNodeImpl(node, this); 464 } 465 466 return treeNode; 467 } 468 469 472 public SiteTreeNode[] getTopNodes() { 473 List childElements = new ArrayList (); 474 475 NamespaceHelper helper = new NamespaceHelper(NAMESPACE_URI, "", document); 476 477 Element [] elements = helper.getChildren((Element ) document.getDocumentElement(), SiteTreeNodeImpl.NODE_NAME); 478 479 for (int i = 0; i < elements.length; i++) { 480 SiteTreeNode newNode = new SiteTreeNodeImpl(elements[i], this); 481 childElements.add(newNode); 482 } 483 484 return (SiteTreeNode[]) childElements.toArray(new SiteTreeNode[childElements.size()]); 485 } 486 487 493 public synchronized void moveUp(String documentid) throws SiteTreeException { 494 Node node = this.getNodeInternal(documentid); 495 if (node == null) { 496 throw new SiteTreeException("Node to move: " + documentid + " not found"); 497 } 498 Node parentNode = node.getParentNode(); 499 if (parentNode == null) { 500 throw new SiteTreeException( 501 "Parentid of node with documentid: " + documentid + " not found"); 502 } 503 504 Node previousNode; 505 try { 506 previousNode = 507 XPathAPI.selectSingleNode( 508 node, 509 "(preceding-sibling::*[local-name() = 'node'])[last()]"); 510 } catch (TransformerException e) { 511 throw new SiteTreeException(e); 512 } 513 514 if (previousNode == null) { 515 log.warn("Couldn't found a preceding sibling"); 516 return; 517 } 518 Node insertNode = parentNode.removeChild(node); 519 parentNode.insertBefore(insertNode, previousNode); 520 } 521 522 528 public synchronized void moveDown(String documentid) throws SiteTreeException { 529 Node node = this.getNodeInternal(documentid); 530 if (node == null) { 531 throw new SiteTreeException("Node to move: " + documentid + " not found"); 532 } 533 Node parentNode = node.getParentNode(); 534 if (parentNode == null) { 535 throw new SiteTreeException( 536 "Parentid of node with documentid: " + documentid + " not found"); 537 } 538 Node nextNode; 539 try { 540 nextNode = 541 XPathAPI.selectSingleNode( 542 node, 543 "following-sibling::*[local-name() = 'node'][position()=2]"); 544 } catch (TransformerException e) { 545 throw new SiteTreeException(e); 546 } 547 548 Node insertNode = parentNode.removeChild(node); 549 550 if (nextNode == null) { 551 log.warn("Couldn't found the second following sibling"); 552 parentNode.appendChild(insertNode); 553 } else { 554 parentNode.insertBefore(insertNode, nextNode); 555 } 556 } 557 558 561 public synchronized void importSubtree( 562 SiteTreeNode newParent, 563 SiteTreeNode subtreeRoot, 564 String newid, 565 String refDocumentId) 566 throws SiteTreeException { 567 assert subtreeRoot != null; 568 assert newParent != null; 569 String parentId = newParent.getAbsoluteId(); 570 String id = newid; 571 572 this.addNode( 573 parentId, 574 id, 575 subtreeRoot.getLabels(), 576 subtreeRoot.visibleInNav(), 577 subtreeRoot.getHref(), 578 subtreeRoot.getSuffix(), 579 subtreeRoot.hasLink(), 580 refDocumentId); 581 newParent = this.getNode(parentId + "/" + id); 582 if (newParent == null) { 583 throw new SiteTreeException("The added node was not found."); 584 } 585 SiteTreeNode[] children = subtreeRoot.getChildren(); 586 if (children == null) { 587 log.info("The node " + subtreeRoot.toString() + " has no children"); 588 return; 589 } else { for (int i = 0; i < children.length; i++) { 590 importSubtree(newParent, children[i], children[i].getId(), null); 591 } 592 } 593 } 594 595 598 public synchronized void save() throws SiteTreeException { 599 try { 600 DocumentHelper.writeDocument(document, treefile); 601 } catch (TransformerException e) { 602 throw new SiteTreeException( 603 "The document [" + document.getLocalName() + "] could not be transformed"); 604 } catch (IOException e) { 605 throw new SiteTreeException( 606 "The saving of document [" + document.getLocalName() + "] failed"); 607 } 608 lastModified = new Date ().getTime(); 609 } 610 611 614 public synchronized void setLabel(String documentId, Label label) { 615 SiteTreeNode node = getNode(documentId); 616 if (node != null) { 617 node.setLabel(label); 618 } 619 } 620 621 624 public void copy(SiteTreeNode src, SiteTreeNode dst, String newId, String followingSibling) throws SiteTreeException { 625 assert dst instanceof SiteTreeNodeImpl; 626 627 SiteTreeNodeImpl dstNode = (SiteTreeNodeImpl)dst; 628 if (this.equals(dstNode.getDefaultSiteTree())) { 629 synchronized(DefaultSiteTree.lock) { 632 DefaultSiteTree srcSiteTree = ((SiteTreeNodeImpl)src).getDefaultSiteTree(); 633 synchronized(srcSiteTree) { 634 synchronized(this) { 635 String parentId = dst.getAbsoluteId(); 636 String id = newId; 637 638 this.addNode( 639 parentId, 640 id, 641 src.getLabels(), 642 src.visibleInNav(), 643 src.getHref(), 644 src.getSuffix(), 645 src.hasLink(), 646 followingSibling); 647 SiteTreeNode node = this.getNode(parentId + "/" + id); 648 if (node == null) { 649 throw new SiteTreeException("The added node was not found."); 650 } 651 SiteTreeNode[] children = src.getChildren(); 652 if (children == null) { 653 log.debug("The node " + src.toString() + " has no children"); 654 return; 655 } else { 656 for (int i = 0; i < children.length; i++) { 657 copy(children[i], node, children[i].getId(), null); 658 } 659 } 660 } 661 } 662 } 663 } else { 664 dstNode.getDefaultSiteTree().copy(src, dst, newId, followingSibling); 666 } 667 } 668 669 672 public void move(SiteTreeNode src, SiteTreeNode dst, String newId, String followingSibling) throws SiteTreeException { 673 assert dst != null; 674 assert src instanceof SiteTreeNodeImpl; 675 676 synchronized(DefaultSiteTree.lock) { 678 synchronized(((SiteTreeNodeImpl)src).getDefaultSiteTree()) { 680 synchronized(((SiteTreeNodeImpl)dst).getDefaultSiteTree()) { 681 copy(src, dst, newId, followingSibling); 682 DefaultSiteTree sitetree = ((SiteTreeNodeImpl)src).getDefaultSiteTree(); 683 sitetree.deleteNode(src.getAbsoluteId()); 684 } 685 } 686 } 687 } 688 689 692 public long getLastModified() { 693 return lastModified; 694 } 695 } 696 | Popular Tags |