svn commit: r647009 [1/2] - in /lenya/sandbox/modules/diff: ./ config/ config/cocoon-xconf/ java/ java/src/ java/src/org/ java/src/org/apache/ java/src/org/apache/lenya/ java/src/org/apache/lenya/modules/ java/src/org/apache/lenya/modules/diff/ java/sr...

[email protected]
Newsgroups gmane.comp.cms.lenya.cvs
Message-ID <[email protected]>
Author: andreas
Date: Thu Apr 10 16:29:24 2008
New Revision: 647009

URL: http://svn.apache.org/viewvc?rev=647009&view=rev
Log:
Adding diff module to sandbox.

Added:
    lenya/sandbox/modules/diff/
    lenya/sandbox/modules/diff/config/
    lenya/sandbox/modules/diff/config/cocoon-xconf/
    lenya/sandbox/modules/diff/config/cocoon-xconf/change-notifier.xconf
    lenya/sandbox/modules/diff/config/cocoon-xconf/usecase-diff.xconf
    lenya/sandbox/modules/diff/config/module.xml
    lenya/sandbox/modules/diff/java/
    lenya/sandbox/modules/diff/java/src/
    lenya/sandbox/modules/diff/java/src/org/
    lenya/sandbox/modules/diff/java/src/org/apache/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/AbstractComparableNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Attribute.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ChangeNotifier.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Comparable.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableWriter.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeVisitor.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceResult.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTree.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeWriter.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Node.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/SimpleTreeDiff.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Tree.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/TreeDiff.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/Diff.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Attribute.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Comment.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilder.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilderFactory.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Element.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/NamedXmlNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/TextNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlDocument.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeVisitor.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeWriter.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/AttributeImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/CommentImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/DomTreeBuilderImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/ElementImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/NamedXmlNodeImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextNodeImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextValueXmlNode.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/XmlDocumentImpl.java
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/
    lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/Queue.java
    lenya/sandbox/modules/diff/java/test/
    lenya/sandbox/modules/diff/java/test/org/
    lenya/sandbox/modules/diff/java/test/org/apache/
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/DiffTest.java
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test1.xml
    lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test2.xml
    lenya/sandbox/modules/diff/resources/
    lenya/sandbox/modules/diff/resources/i18n/
    lenya/sandbox/modules/diff/resources/i18n/cmsui.xml
    lenya/sandbox/modules/diff/resources/i18n/cmsui_de.xml
    lenya/sandbox/modules/diff/usecases/
    lenya/sandbox/modules/diff/usecases/diff.jx

Added: lenya/sandbox/modules/diff/config/cocoon-xconf/change-notifier.xconf
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/config/cocoon-xconf/change-notifier.xconf?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/config/cocoon-xconf/change-notifier.xconf (added)
+++ lenya/sandbox/modules/diff/config/cocoon-xconf/change-notifier.xconf Thu Apr 10 16:29:24 2008
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<xconf xpath="/cocoon" unless="/cocoon/component[@role = 'org.apache.lenya.modules.diff.ChangeNotifier']">
+
+  <component role="org.apache.lenya.modules.diff.ChangeNotifier"
+    class="org.apache.lenya.modules.diff.ChangeNotifier"
+    logger="lenya.notification">
+    <parameter name="sender" value="alice"/>
+    <parameter name="recipient" value="lenya"/>
+  </component>
+  
+</xconf>

Added: lenya/sandbox/modules/diff/config/cocoon-xconf/usecase-diff.xconf
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/config/cocoon-xconf/usecase-diff.xconf?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/config/cocoon-xconf/usecase-diff.xconf (added)
+++ lenya/sandbox/modules/diff/config/cocoon-xconf/usecase-diff.xconf Thu Apr 10 16:29:24 2008
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- $Id: usecases-workflow-deactivate.xconf 348547 2005-11-23 20:13:01Z chestnut $ -->
+<!--
+    This file defines the publication specific use-cases
+-->
+
+  <xconf xpath="/cocoon/usecases" unless="/cocoon/usecases/component-instance[@name = 'diff.diff']">
+    <component-instance name="diff.diff" logger="lenya.site" class="org.apache.lenya.modules.diff.usecases.Diff">
+      <view uri="modules/diff/usecases/diff.jx" menu="true"
+        createContinuation="false">
+        <tab group="site" name="revisions"/>
+      </view>
+      <exit usecase="tab.revisions"/>
+    </component-instance>
+  </xconf>

Added: lenya/sandbox/modules/diff/config/module.xml
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/config/module.xml?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/config/module.xml (added)
+++ lenya/sandbox/modules/diff/config/module.xml Thu Apr 10 16:29:24 2008
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- $Id: publication.xml 374687 2006-02-03 15:24:55Z michi $ -->
+
+<module xmlns="http://apache.org/lenya/module/1.0">
+  <id>org.apache.lenya.modules.diff</id>
+  <export package="org.apache.lenya.modules.diff"/>
+  <export package="org.apache.lenya.modules.diff.xml"/>
+  <export package="org.apache.lenya.util"/>
+  <depends module="org.apache.lenya.modules.linking"/>
+  <depends module="org.apache.lenya.modules.notification"/>
+  <depends module="org.apache.lenya.modules.usecase"/>
+  <package>org.apache.lenya.modules</package>
+  <version>0.1-dev</version>
+  <name>diff</name>
+  <lenya-version>@lenya.version@</lenya-version>
+  <description>XML difference utilities</description>
+</module>

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/AbstractComparableNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/AbstractComparableNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/AbstractComparableNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/AbstractComparableNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,49 @@
+package org.apache.lenya.modules.diff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lenya.util.Assert;
+
+public abstract class AbstractComparableNode implements ComparableNode {
+    
+    public AbstractComparableNode() {
+    }
+    
+    public AbstractComparableNode(Node parent) {
+        Assert.notNull("parent", parent);
+        this.parent = parent;
+    }
+    
+    private Node parent;
+    private List children = new ArrayList();
+    private List attributes = new ArrayList();
+
+    public Node[] getChildren() {
+        return (Node[]) this.children.toArray(new Node[this.children.size()]);
+    }
+
+    public Node getParent() {
+        return this.parent;
+    }
+
+    public boolean equals(Comparable comparable) {
+        Assert.notNull("comparable", comparable);
+        return comparable.getClass() == getClass();
+    }
+
+    public void appendChild(Node node) {
+        Assert.notNull("node", node);
+        this.children.add(node);
+    }
+
+    public void addAttribute(Node node) {
+        Assert.notNull("attribute", node);
+        this.attributes.add(node);
+    }
+
+    public Attribute[] getAttributes() {
+        return (Attribute[]) this.attributes.toArray(new Attribute[this.attributes.size()]);
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Attribute.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Attribute.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Attribute.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Attribute.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,5 @@
+package org.apache.lenya.modules.diff;
+
+public interface Attribute {
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ChangeNotifier.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ChangeNotifier.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ChangeNotifier.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ChangeNotifier.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.lenya.modules.diff;
+
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameterizable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.Request;
+import org.apache.lenya.ac.AccessControlException;
+import org.apache.lenya.ac.User;
+import org.apache.lenya.cms.ac.PolicyUtil;
+import org.apache.lenya.cms.cocoon.components.context.ContextUtility;
+import org.apache.lenya.cms.linking.LinkRewriter;
+import org.apache.lenya.cms.linking.OutgoingLinkRewriter;
+import org.apache.lenya.cms.metadata.dublincore.DublinCoreHelper;
+import org.apache.lenya.cms.observation.AbstractRepositoryListener;
+import org.apache.lenya.cms.observation.DocumentEvent;
+import org.apache.lenya.cms.observation.RepositoryEvent;
+import org.apache.lenya.cms.publication.Area;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentFactory;
+import org.apache.lenya.cms.publication.DocumentUtil;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.repository.History;
+import org.apache.lenya.cms.repository.Revision;
+import org.apache.lenya.modules.diff.Comparable;
+import org.apache.lenya.modules.diff.DifferenceNode;
+import org.apache.lenya.modules.diff.DifferenceNodeVisitor;
+import org.apache.lenya.modules.diff.DifferenceTree;
+import org.apache.lenya.modules.diff.SimpleTreeDiff;
+import org.apache.lenya.modules.diff.Tree;
+import org.apache.lenya.modules.diff.TreeDiff;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilder;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilderFactory;
+import org.apache.lenya.notification.Message;
+import org.apache.lenya.notification.Notifier;
+import org.apache.lenya.xml.DocumentHelper;
+
+public class ChangeNotifier extends AbstractRepositoryListener implements Parameterizable {
+
+    protected static final String PARAM_RECIPIENT = "recipient";
+    protected static final String PARAM_SENDER = "sender";
+    protected static final String SUBJECT = "change-notification-subject";
+    protected static final String BODY = "change-notification-body";
+
+    private String sender;
+    private String recipient;
+
+    public void eventFired(RepositoryEvent event) {
+
+        if (event instanceof DocumentEvent && event.getDescriptor().equals(DocumentEvent.CHANGED)) {
+            DocumentEvent docEvent = (DocumentEvent) event;
+            int revision = docEvent.getRevision();
+
+            if (revision > -1) {
+
+                ContextUtility context = null;
+                try {
+                    context = (ContextUtility) this.manager.lookup(ContextUtility.ROLE);
+                    Request request = context.getRequest();
+                    DocumentFactory factory = DocumentUtil
+                            .getDocumentFactory(this.manager, request);
+
+                    String pubId = docEvent.getPublicationId();
+                    if (factory.existsPublication(pubId)) {
+                        Publication pub = factory.getPublication(pubId);
+                        Area area = pub.getArea(docEvent.getArea());
+                        String uuid = docEvent.getUuid();
+                        String language = docEvent.getLanguage();
+                        if (area.contains(uuid, language)) {
+                            Document doc = area.getDocument(uuid, language);
+                            int[] revisions = doc.getRepositoryNode().getHistory()
+                                    .getRevisionNumbers();
+                            int pos = 0;
+                            while (revisions[pos] != revision) {
+                                pos++;
+                            }
+                            if (pos > 0) {
+                                int nextRevision = revisions[pos - 1];
+                                notifyChange(doc, revision, nextRevision);
+                            }
+                        }
+                    }
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                } finally {
+                    if (context != null) {
+                        this.manager.release(context);
+                    }
+                }
+            }
+        }
+
+    }
+
+    protected void notifyChange(Document doc, int revNr1, int revNr2) throws Exception {
+
+        History history = doc.getRepositoryNode().getHistory();
+        Revision rev1 = history.getRevision(revNr1);
+        Revision rev2 = history.getRevision(revNr2);
+
+        org.w3c.dom.Document xml1 = DocumentHelper.readDocument(rev1.getInputStream());
+        org.w3c.dom.Document xml2 = DocumentHelper.readDocument(rev2.getInputStream());
+
+        if (xml1 == null || xml2 == null) {
+            getLogger().debug("Can't compare revisions for document " + doc);
+        } else {
+
+            DomTreeBuilder builder = DomTreeBuilderFactory.createBuilder();
+            Tree tree1 = builder.buildTree(xml1);
+            Tree tree2 = builder.buildTree(xml2);
+            TreeDiff diff = new SimpleTreeDiff();
+            DifferenceTree difference = diff.getDifferences(tree1, tree2);
+            DifferencesWriter writer = new DifferencesWriter();
+            difference.traverse(writer);
+
+            Notifier notifier = null;
+            try {
+                String title = DublinCoreHelper.getTitle(doc, true);
+                String[] subjectParams = { title };
+
+                String url = doc.getCanonicalWebappURL();
+
+                LinkRewriter rewriter = new OutgoingLinkRewriter(this.manager, doc.getSession(),
+                        url, false, false, false);
+                String documentUrl = rewriter.rewrite(url);
+                
+                String userId = rev2.getUserId();
+                User user = getUser(url, userId);
+                String userParam = user == null ? userId : user.getName() + " (" + userId + ")";
+                
+                String[] bodyParams = { title, documentUrl, userParam, writer.toString() };
+                User sender = getUser(url, this.sender);
+                User recipient = getUser(url, this.recipient);
+                User[] recipients = { recipient };
+
+                Message message = new Message(SUBJECT, subjectParams, BODY, bodyParams, sender,
+                        recipients);
+
+                notifier = (Notifier) this.manager.lookup(Notifier.ROLE);
+                notifier.notify(message);
+            } finally {
+                if (notifier != null) {
+                    this.manager.release(notifier);
+                }
+            }
+        }
+    }
+
+    protected User getUser(String url, String userId) throws AccessControlException {
+        return PolicyUtil.getUser(this.manager, url, userId, getLogger());
+    }
+
+    protected static class DifferencesWriter implements DifferenceNodeVisitor {
+
+        protected static final String DIFFERENCE_SEPARATOR = "\n=================================\n";
+        protected static final String BEFORE_AFTER_SEPARATOR = "\n---------------------------------\n";
+        private StringBuffer buffer = new StringBuffer();
+
+        public void visit(DifferenceNode node) {
+            Comparable left = node.getLeftSide();
+            Comparable right = node.getRightSide();
+            if (left == null && right != null) {
+                this.buffer.append(DIFFERENCE_SEPARATOR);
+                this.buffer.append("Added: " + node.getPath() + "\n");
+                this.buffer.append(toString(right));
+            }
+            if (left != null && right == null) {
+                this.buffer.append(DIFFERENCE_SEPARATOR);
+                this.buffer.append("Removed: " + node.getPath() + "\n");
+                this.buffer.append(toString(left));
+            }
+            if (left != null && right != null && !left.equals(right)) {
+                this.buffer.append(DIFFERENCE_SEPARATOR);
+                this.buffer.append(node.getPath());
+                this.buffer.append(BEFORE_AFTER_SEPARATOR);
+                this.buffer.append(toString(left));
+                this.buffer.append(BEFORE_AFTER_SEPARATOR);
+                this.buffer.append(toString(right));
+            }
+        }
+
+        protected String toString(Comparable right) {
+            String rightString = right != null ? right.toString() : "(n/a)";
+            return rightString;
+        }
+
+        public String toString() {
+            this.buffer.append(DIFFERENCE_SEPARATOR);
+            return this.buffer.toString();
+        }
+
+    }
+
+    public void parameterize(Parameters params) throws ParameterException {
+        this.sender = params.getParameter(PARAM_SENDER);
+        this.recipient = params.getParameter(PARAM_RECIPIENT);
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Comparable.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Comparable.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Comparable.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Comparable.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface Comparable {
+
+    boolean equals(Comparable comparable);
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface ComparableNode extends Node, Comparable {
+
+    Attribute[] getAttributes();
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableWriter.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableWriter.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableWriter.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/ComparableWriter.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface ComparableWriter {
+    
+    String toString(Comparable comparable);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface DifferenceNode extends DifferenceResult, Node {
+
+    void traverse(DifferenceNodeVisitor visitor);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,111 @@
+package org.apache.lenya.modules.diff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lenya.util.Assert;
+
+public class DifferenceNodeImpl implements DifferenceNode {
+
+    private ComparableNode leftNode;
+    private ComparableNode rightNode;
+
+    public DifferenceNodeImpl(DifferenceNode parent) {
+        Assert.notNull("parent", parent);
+        this.parent = parent;
+    }
+
+    public DifferenceNodeImpl() {
+    }
+
+    public void setLeftSide(ComparableNode leftNode) {
+        this.leftNode = leftNode;
+    }
+
+    public void setRightSide(ComparableNode rightNode) {
+        this.rightNode = rightNode;
+    }
+
+    public Comparable getLeftSide() {
+        return this.leftNode;
+    }
+
+    public Comparable getRightSide() {
+        return this.rightNode;
+    }
+
+    public boolean isEqual() {
+        if (this.leftNode == null || this.rightNode == null
+                || !this.leftNode.equals(this.rightNode)) {
+            return false;
+        }
+        Node[] children = getChildren();
+        for (int i = 0; i < children.length; i++) {
+            if (!((DifferenceNode) children[i]).isEqual()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private List children = new ArrayList();
+    private Node parent;
+    private int deviation;
+
+    public Node[] getChildren() {
+        return (Node[]) this.children.toArray(new Node[this.children.size()]);
+    }
+
+    public Node getParent() {
+        return this.parent;
+    }
+
+    public void appendChild(DifferenceNode child) {
+        Assert.notNull("node", child);
+        this.children.add(child);
+    }
+
+    public void traverse(DifferenceNodeVisitor visitor) {
+        visitor.visit(this);
+        Node[] children = getChildren();
+        for (int i = 0; i < children.length; i++) {
+            DifferenceNode diffNode = (DifferenceNode) children[i];
+            diffNode.traverse(visitor);
+        }
+    }
+
+    public String toString() {
+        return this.leftNode + " - " + this.rightNode;
+    }
+
+    public float getDeviation() {
+        return ((float) this.deviation) / getSize();
+    }
+
+    protected int getSize() {
+        int size = 1;
+        Node[] children = getChildren();
+        for (int i = 0; i < children.length; i++) {
+            DifferenceNodeImpl diffNode = (DifferenceNodeImpl) children[i];
+            size += diffNode.getSize();
+        }
+        return size;
+    }
+
+    protected void increaseDeviation() {
+        this.deviation++;
+        DifferenceNodeImpl parent = (DifferenceNodeImpl) getParent();
+        if (parent != null) {
+            parent.increaseDeviation();
+        }
+    }
+
+    public String getPath() {
+        if (this.leftNode == null && this.rightNode == null) {
+            return "";
+        }
+        Node node = this.leftNode != null ? this.leftNode : this.rightNode;
+        return node.getPath();
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeVisitor.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeVisitor.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeVisitor.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceNodeVisitor.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface DifferenceNodeVisitor {
+    
+    void visit(DifferenceNode node);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceResult.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceResult.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceResult.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceResult.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,13 @@
+package org.apache.lenya.modules.diff;
+
+public interface DifferenceResult {
+    
+    Comparable getLeftSide();
+    
+    Comparable getRightSide();
+
+    boolean isEqual();
+    
+    float getDeviation();
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTree.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTree.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTree.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTree.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,5 @@
+package org.apache.lenya.modules.diff;
+
+public interface DifferenceTree extends Tree, DifferenceNode {
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,5 @@
+package org.apache.lenya.modules.diff;
+
+public class DifferenceTreeImpl extends DifferenceNodeImpl implements DifferenceTree {
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeWriter.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeWriter.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeWriter.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/DifferenceTreeWriter.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,47 @@
+package org.apache.lenya.modules.diff;
+
+import org.apache.lenya.util.Assert;
+
+public class DifferenceTreeWriter implements DifferenceNodeVisitor {
+
+    private ComparableWriter writer;
+
+    public DifferenceTreeWriter(ComparableWriter writer) {
+        Assert.notNull("writer", writer);
+        this.writer = writer;
+    }
+
+    protected static final int INDENT = 2;
+    protected static final int WIDTH = 60;
+
+    public void visit(DifferenceNode node) {
+        int depth = getDepth(node);
+        String indent = getIndent(depth);
+        Comparable[] comparables = { node.getLeftSide(), node.getRightSide() };
+        for (int i = 0; i < 2; i++) {
+            String compString = comparables[i] == null ? "" : this.writer.toString(comparables[i]);
+            String s = indent + compString;
+            StringBuffer out = new StringBuffer();
+            out.append(s.length() <= WIDTH ? s : s.substring(0, WIDTH));
+            while (out.length() < WIDTH) {
+                out.append(" ");
+            }
+            System.out.print(out.toString() + " ");
+        }
+        System.out.println();
+    }
+
+    protected String getIndent(int depth) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < depth * INDENT; i++) {
+            buf.append(" ");
+        }
+        return buf.toString();
+    }
+
+    protected int getDepth(Node node) {
+        Node parent = node.getParent();
+        return parent == null ? 0 : getDepth(parent) + 1;
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Node.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Node.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Node.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Node.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,11 @@
+package org.apache.lenya.modules.diff;
+
+public interface Node {
+    
+    Node getParent();
+    
+    Node[] getChildren();
+    
+    String getPath();
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/SimpleTreeDiff.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/SimpleTreeDiff.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/SimpleTreeDiff.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/SimpleTreeDiff.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,280 @@
+package org.apache.lenya.modules.diff;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.lenya.util.Assert;
+import org.apache.lenya.util.Queue;
+
+public class SimpleTreeDiff implements TreeDiff {
+
+    protected static final String LEFT = "left";
+    protected static final String RIGHT = "right";
+    protected static final int MAX_THRESHOLD = 1;
+
+    public DifferenceTree getDifferences(Tree tree1, Tree tree2) {
+        DifferenceTreeImpl differenceTree = new DifferenceTreeImpl();
+        differenceTree.setLeftSide((ComparableNode) tree1);
+        differenceTree.setRightSide((ComparableNode) tree2);
+        computeDifference(differenceTree);
+        return differenceTree;
+    }
+
+    protected void computeDifference(DifferenceNodeImpl diffNode) {
+        ComparableNode leftNode = (ComparableNode) diffNode.getLeftSide();
+        Queue leftAttrs = new Queue();
+        Queue leftNodes = new Queue();
+        if (leftNode != null) {
+            leftAttrs.addAll(Arrays.asList(leftNode.getAttributes()));
+            leftNodes.addAll(Arrays.asList(leftNode.getChildren()));
+        }
+
+        ComparableNode rightNode = (ComparableNode) diffNode.getRightSide();
+        Queue rightAttrs = new Queue();
+        Queue rightNodes = new Queue();
+        if (rightNode != null) {
+            rightAttrs.addAll(Arrays.asList(rightNode.getAttributes()));
+            rightNodes.addAll(Arrays.asList(rightNode.getChildren()));
+        }
+
+        computeDifferences(diffNode, leftAttrs, rightAttrs);
+        computeDifferences(diffNode, leftNodes, rightNodes);
+    }
+
+    protected void computeDifferences(DifferenceNodeImpl diffNode, Queue leftNodes, Queue rightNodes) {
+
+        Queue leftSkipped = new Queue();
+        Queue rightSkipped = new Queue();
+
+        while (!leftNodes.isEmpty() || !rightNodes.isEmpty()) {
+            log("-----------------------------------");
+
+            if (leftNodes.isEmpty()) {
+                skipRight(diffNode, rightNodes, rightSkipped);
+                continue;
+            }
+
+            if (rightNodes.isEmpty()) {
+                skipLeft(diffNode, leftNodes, leftSkipped);
+                continue;
+            }
+
+            ComparableNode left = (ComparableNode) leftNodes.front();
+            ComparableNode right = (ComparableNode) rightNodes.front();
+
+            Match matchForLeft = null;
+            Match matchForRight = null;
+            int threshold = -1;
+            while (threshold < MAX_THRESHOLD && matchForLeft == null && matchForRight == null) {
+                threshold++;
+                matchForLeft = findMatch(left, rightNodes.getElements(), threshold);
+                matchForRight = findMatch(right, leftNodes.getElements(), threshold);
+            }
+
+            log("Match threshold: " + threshold);
+            log("Left match for " + left + ": " + matchForLeft);
+            log("Right match for " + right + ": " + matchForLeft);
+
+            int result = compareMatches(matchForLeft, matchForRight);
+
+            switch (result) {
+
+            case MATCH:
+                applySkippedNodes(diffNode, leftSkipped, rightSkipped);
+                leftNodes.out();
+                rightNodes.out();
+                computeDifference(diffNode, left, right);
+                break;
+
+            case NONE:
+                skipLeft(diffNode, leftNodes, leftSkipped);
+                skipRight(diffNode, rightNodes, rightSkipped);
+                break;
+
+            case EQUAL:
+                skipRight(diffNode, rightNodes, rightSkipped);
+                break;
+
+            case LEFT_BETTER:
+                skipRight(diffNode, rightNodes, rightSkipped);
+                break;
+
+            case RIGHT_BETTER:
+                skipLeft(diffNode, leftNodes, leftSkipped);
+                break;
+
+            }
+
+        }
+        applySkippedNodes(diffNode, leftSkipped, rightSkipped);
+    }
+
+    protected void skipLeft(DifferenceNodeImpl diffNode, Queue remaining, Queue skipped) {
+        skip(diffNode, remaining, skipped, LEFT);
+    }
+
+    protected void skipRight(DifferenceNodeImpl diffNode, Queue remaining, Queue skipped) {
+        skip(diffNode, remaining, skipped, RIGHT);
+    }
+
+    protected void skip(DifferenceNodeImpl diffNode, Queue remaining, Queue skipped, String side) {
+        diffNode.increaseDeviation();
+        ComparableNode node = (ComparableNode) remaining.out();
+        log("Skipping " + node + " (" + side + ")");
+        skipped.in(node);
+    }
+
+    protected void log(String message) {
+        System.out.println(message);
+    }
+
+    protected void applySkippedNodes(DifferenceNodeImpl parent, Queue leftSkipped,
+            Queue rightSkipped) {
+        while (!leftSkipped.isEmpty()) {
+            computeDifference(parent, (ComparableNode) leftSkipped.out(), null);
+        }
+        while (!rightSkipped.isEmpty()) {
+            computeDifference(parent, null, (ComparableNode) rightSkipped.out());
+        }
+    }
+
+    protected void computeDifference(DifferenceNodeImpl parent, ComparableNode left,
+            ComparableNode right) {
+        log("Adding difference: " + left + " - " + right);
+        DifferenceNodeImpl diff = new DifferenceNodeImpl(parent);
+        diff.setLeftSide(left);
+        diff.setRightSide(right);
+        parent.appendChild(diff);
+        computeDifference(diff);
+    }
+
+    protected Match findMatch(ComparableNode node, List otherNodes, int threshold) {
+        switch (threshold) {
+        case 0:
+            for (int i = 0; i < otherNodes.size(); i++) {
+                ComparableNode otherNode = (ComparableNode) otherNodes.get(i);
+                if (equalsDeep(node, otherNode)) {
+                    return new Match(node, otherNode, threshold, i);
+                }
+            }
+            break;
+        case 1:
+            for (int i = 0; i < otherNodes.size(); i++) {
+                ComparableNode otherNode = (ComparableNode) otherNodes.get(i);
+                if (node.equals(otherNode)) {
+                    return new Match(node, otherNode, threshold, i);
+                }
+            }
+            break;
+        }
+        return null;
+    }
+
+    protected boolean equalsDeep(ComparableNode left, ComparableNode right) {
+        Assert.notNull("left", left);
+        Assert.notNull("right", right);
+        if (!left.equals(right)) {
+            return false;
+        }
+        Attribute[] leftAttrs = left.getAttributes();
+        Attribute[] rightAttrs = right.getAttributes();
+        if (leftAttrs.length != rightAttrs.length) {
+            return false;
+        }
+        for (int i = 0; i < leftAttrs.length; i++) {
+            if (!equalsDeep((ComparableNode) leftAttrs[i], (ComparableNode) rightAttrs[i])) {
+                return false;
+            }
+        }
+        Node[] leftChildren = left.getChildren();
+        Node[] rightChildren = right.getChildren();
+        if (leftChildren.length != rightChildren.length) {
+            return false;
+        }
+        for (int i = 0; i < leftChildren.length; i++) {
+            if (!equalsDeep((ComparableNode) leftChildren[i], (ComparableNode) rightChildren[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    protected static final class Match {
+        protected int threshold = -1;
+        protected int position = -1;
+        protected ComparableNode node;
+        protected ComparableNode otherNode;
+
+        public Match(ComparableNode node, ComparableNode otherNode, int threshold, int position) {
+            this.node = node;
+            this.otherNode = otherNode;
+            this.threshold = threshold;
+            this.position = position;
+        }
+
+        public String toString() {
+            return "Threshold: " + this.threshold + ", Pos: " + this.position;
+        }
+    }
+
+    protected static final int MATCH = 0;
+    protected static final int NONE = 1;
+    protected static final int EQUAL = 2;
+    protected static final int LEFT_BETTER = 3;
+    protected static final int RIGHT_BETTER = 4;
+
+    protected int compareMatches(Match left, Match right) {
+
+        if (left == null && right == null) {
+            log("No upcoming match on either side.");
+            return NONE;
+        }
+
+        if (left == null || right != null && left.position > right.position) {
+            log("Upcoming right match expected.");
+            return RIGHT_BETTER;
+        }
+
+        if (right == null || left != null && left.position < right.position) {
+            log("Upcoming left match expected.");
+            return LEFT_BETTER;
+        }
+
+        if (left.position == 0 && right.position == 0) {
+            log("Match detected.");
+            return MATCH;
+        }
+
+        if (isBetter(left, right)) {
+            log("Better left match.");
+            return LEFT_BETTER;
+        }
+
+        if (isBetter(right, left)) {
+            log("Better right match.");
+            return RIGHT_BETTER;
+        }
+
+        log("Same depth, left has precedence.");
+        return EQUAL;
+    }
+
+    protected boolean isBetter(Match match, Match otherMatch) {
+        DifferenceNode diff1 = getDifference(match.node, match.otherNode);
+        DifferenceNode diff2 = getDifference(otherMatch.node, otherMatch.otherNode);
+        float dev1 = diff1.getDeviation();
+        float dev2 = diff2.getDeviation();
+        log("Deviation of " + diff1 + ": " + dev1);
+        log("Deviation of " + diff2 + ": " + dev2);
+        return dev1 < dev2;
+    }
+
+    protected DifferenceNode getDifference(ComparableNode left, ComparableNode right) {
+        DifferenceNodeImpl node = new DifferenceNodeImpl();
+        node.setLeftSide(left);
+        node.setRightSide(right);
+        computeDifference(node);
+        return node;
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Tree.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Tree.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Tree.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/Tree.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,5 @@
+package org.apache.lenya.modules.diff;
+
+public interface Tree extends Node {
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/TreeDiff.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/TreeDiff.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/TreeDiff.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/TreeDiff.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff;
+
+public interface TreeDiff {
+    
+    DifferenceTree getDifferences(Tree tree1, Tree tree2);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/Diff.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/Diff.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/Diff.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/usecases/Diff.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.lenya.modules.diff.usecases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lenya.cms.repository.History;
+import org.apache.lenya.cms.repository.Revision;
+import org.apache.lenya.cms.usecase.DocumentUsecase;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilder;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilderFactory;
+import org.apache.lenya.modules.diff.xml.XmlNodeWriter;
+import org.apache.lenya.modules.diff.xml.impl.DomTreeBuilderImpl;
+import org.apache.lenya.modules.diff.Comparable;
+import org.apache.lenya.modules.diff.ComparableWriter;
+import org.apache.lenya.modules.diff.DifferenceNode;
+import org.apache.lenya.modules.diff.DifferenceNodeVisitor;
+import org.apache.lenya.modules.diff.DifferenceTree;
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.SimpleTreeDiff;
+import org.apache.lenya.modules.diff.Tree;
+import org.apache.lenya.modules.diff.TreeDiff;
+import org.apache.lenya.xml.DocumentHelper;
+import org.w3c.dom.Document;
+
+public class Diff extends DocumentUsecase {
+
+    protected static final String PARAM_REVISION = "rev";
+    protected static final String PARAM_DIFFERENCES = "differences";
+
+    protected void prepareView() throws Exception {
+        super.prepareView();
+        
+        Revision[] revisions = new Revision[2];
+        History history = getSourceDocument().getRepositoryNode().getHistory();
+        int[] revNumbers = history.getRevisionNumbers();
+        for (int i = 0; i < 2; i++) {
+            String paramName = PARAM_REVISION + (i + 1);
+            int revNr = getParameterAsInteger(paramName, -1);
+            if (revNr == -1) {
+                addErrorMessage("Missing parameter [" + paramName + "]");
+            }
+            for (int r = 0; r < revNumbers.length; r++) {
+                if (revNumbers[r] == revNr) {
+                    revisions[i] = history.getRevision(revNr);
+                }
+            }
+            if (revisions[i] == null) {
+                addErrorMessage("Revision [" + revNr + "] doesn't exist.");
+            }
+        }
+        
+        if (!hasErrors()) {
+            Document doc1 = DocumentHelper.readDocument(revisions[0].getInputStream());
+            Document doc2 = DocumentHelper.readDocument(revisions[1].getInputStream());
+            
+            if (doc1 == null || doc2 == null) {
+                addErrorMessage("diff-only-for-xml");
+            }
+            else {
+                
+                DomTreeBuilder builder = DomTreeBuilderFactory.createBuilder();
+                Tree tree1 = builder.buildTree(doc1);
+                Tree tree2 = builder.buildTree(doc2);
+                TreeDiff diff = new SimpleTreeDiff();
+                DifferenceTree difference = diff.getDifferences(tree1, tree2);
+                DifferencesWriter writer = new DifferencesWriter();
+                difference.traverse(writer);
+                setParameter(PARAM_DIFFERENCES, writer.getDifferences());
+
+                /*
+                 * org.custommonkey.xmlunit.Diff diff = new
+                 * org.custommonkey.xmlunit.Diff(doc1, doc2); DetailedDiff
+                 * detailedDiff = new DetailedDiff(diff); List differences =
+                 * detailedDiff.getAllDifferences();
+                 * setParameter(PARAM_DIFFERENCES, differences);
+                 */
+            }
+        }
+    }
+
+    public static class Difference {
+        int depth;
+        String left;
+        String right;
+
+        public Difference(int depth, String left, String right) {
+            this.depth = depth;
+            this.left = left;
+            this.right = right;
+        }
+
+        public int getDepth() {
+            return depth;
+        }
+
+        public String getLeft() {
+            return left;
+        }
+
+        public String getRight() {
+            return right;
+        }
+
+    }
+
+    protected static class DifferencesWriter implements DifferenceNodeVisitor {
+
+        private List differences = new ArrayList();
+        private ComparableWriter writer = new XmlNodeWriter();
+
+        public void visit(DifferenceNode node) {
+            int depth = getDepth(node);
+            String left = toString(node.getLeftSide());
+            String right = toString(node.getRightSide());
+            Difference diff = new Difference(depth, left, right);
+            this.differences.add(diff);
+        }
+
+        protected String toString(Comparable comparable) {
+            return comparable == null ? "" : this.writer.toString(comparable);
+        }
+
+        protected int getDepth(Node node) {
+            Node parent = node.getParent();
+            return parent == null ? 0 : getDepth(parent) + 1;
+        }
+        
+        protected Difference[] getDifferences() {
+            return (Difference[]) this.differences.toArray(new Difference[this.differences.size()]);
+        }
+
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Attribute.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Attribute.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Attribute.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Attribute.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface Attribute extends NamedXmlNode, org.apache.lenya.modules.diff.Attribute {
+
+    String getValue();
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Comment.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Comment.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Comment.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Comment.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface Comment {
+    
+    String getText();
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilder.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilder.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilder.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilder.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.lenya.modules.diff.xml;
+
+import org.apache.lenya.modules.diff.Tree;
+import org.w3c.dom.Document;
+
+public interface DomTreeBuilder {
+    
+    Tree buildTree(Document xml);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilderFactory.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilderFactory.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilderFactory.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/DomTreeBuilderFactory.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.lenya.modules.diff.xml;
+
+public class DomTreeBuilderFactory {
+
+    protected static final String BUILDER_CLASS = "org.apache.lenya.modules.diff.xml.impl.DomTreeBuilderImpl";
+
+    public static DomTreeBuilder createBuilder() {
+        try {
+            return (DomTreeBuilder) Class.forName(BUILDER_CLASS).newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Element.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Element.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Element.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/Element.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,5 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface Element extends NamedXmlNode {
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/NamedXmlNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/NamedXmlNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/NamedXmlNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/NamedXmlNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,9 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface NamedXmlNode extends XmlNode {
+    
+    String getNamespaceUri();
+    
+    String getLocalName();
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/TextNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/TextNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/TextNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/TextNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface TextNode {
+    
+    String getText();
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlDocument.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlDocument.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlDocument.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlDocument.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+package org.apache.lenya.modules.diff.xml;
+
+import org.apache.lenya.modules.diff.Tree;
+
+public interface XmlDocument extends Tree, XmlNode {
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,9 @@
+package org.apache.lenya.modules.diff.xml;
+
+import org.apache.lenya.modules.diff.Node;
+
+public interface XmlNode extends Node {
+    
+    void visit(XmlNodeVisitor visitor);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeVisitor.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeVisitor.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeVisitor.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeVisitor.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,15 @@
+package org.apache.lenya.modules.diff.xml;
+
+public interface XmlNodeVisitor {
+
+    void attributeOccured(Attribute attribute);
+
+    void elementOccured(Element element);
+
+    void commentOccured(Comment comment);
+
+    void textNodeOccured(TextNode textNode);
+
+    void documentOccured(XmlDocument xmlDocument);
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeWriter.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeWriter.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeWriter.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/XmlNodeWriter.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,37 @@
+package org.apache.lenya.modules.diff.xml;
+
+import org.apache.lenya.modules.diff.Comparable;
+import org.apache.lenya.modules.diff.ComparableWriter;
+import org.apache.lenya.util.Assert;
+
+public class XmlNodeWriter implements ComparableWriter, XmlNodeVisitor {
+    
+    private String string;
+
+    public String toString(Comparable comparable) {
+        Assert.notNull("comparable", comparable);
+        ((XmlNode) comparable).visit(this);
+        return this.string;
+    }
+
+    public void attributeOccured(Attribute attribute) {
+        this.string = "[attr] " + attribute.getLocalName() + "=\"" + attribute.getValue() + "\""; 
+    }
+
+    public void commentOccured(Comment comment) {
+        this.string = "<!-- " + comment.getText() + "-->";
+    }
+
+    public void documentOccured(XmlDocument xmlDocument) {
+        this.string = "[doc]";
+    }
+
+    public void elementOccured(Element element) {
+        this.string = "<" + element.getLocalName() + ">";
+    }
+
+    public void textNodeOccured(TextNode textNode) {
+        this.string = "[text] " + textNode.getText().trim().replaceAll("\\n", "");
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/AttributeImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/AttributeImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/AttributeImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/AttributeImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,42 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import org.apache.lenya.modules.diff.xml.Attribute;
+import org.apache.lenya.modules.diff.xml.Element;
+import org.apache.lenya.modules.diff.xml.XmlNodeVisitor;
+import org.apache.lenya.util.Assert;
+
+public class AttributeImpl extends NamedXmlNodeImpl implements Attribute {
+
+    private String value;
+
+    public AttributeImpl(Element parent, String namespaceUri, String localName, String value) {
+        super(parent, namespaceUri, localName);
+        Assert.notNull("value", value);
+        this.value = value;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+
+    public void visit(XmlNodeVisitor visitor) {
+        visitor.attributeOccured(this);
+    }
+    
+    public boolean equals(Comparable comparable) {
+        if (!super.equals(comparable)) {
+            return false;
+        }
+        Attribute attr = (Attribute) comparable;
+        return getValue().equals(attr.getValue());
+    }
+    
+    public String toString() {
+        return "[attr] " + getLocalName() + "=" + getValue() + " (" + getNamespaceUri() + ")";
+    }
+
+    public String getPath() {
+        return getParent().getPath() + "/@" + getLocalName();
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/CommentImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/CommentImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/CommentImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/CommentImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,21 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.xml.Comment;
+import org.apache.lenya.modules.diff.xml.XmlNodeVisitor;
+
+public class CommentImpl extends TextValueXmlNode implements Comment {
+    
+    public CommentImpl(Node parent, String text) {
+        super(parent, text);
+    }
+
+    public void visit(XmlNodeVisitor visitor) {
+        visitor.commentOccured(this);
+    }
+
+    protected String getNodeType() {
+        return "comment";
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/DomTreeBuilderImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/DomTreeBuilderImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/DomTreeBuilderImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/DomTreeBuilderImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.lenya.modules.diff.xml.impl;
+
+import java.util.Iterator;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.lenya.modules.diff.AbstractComparableNode;
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.Tree;
+import org.apache.lenya.modules.diff.xml.Attribute;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilder;
+import org.apache.lenya.modules.diff.xml.Element;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+public class DomTreeBuilderImpl implements DomTreeBuilder {
+
+    protected static final String XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
+
+    public Tree buildTree(Document xml) {
+        XmlDocumentImpl tree = new XmlDocumentImpl();
+        appendChildren(xml, tree);
+        return tree;
+    }
+
+    protected Element buildElement(Node parent, org.w3c.dom.Element element) {
+        ElementImpl elem = new ElementImpl(parent, element.getNamespaceURI(), element
+                .getLocalName());
+        NamedNodeMap attrs = element.getAttributes();
+        SortedMap map = new TreeMap();
+        for (int i = 0; i < attrs.getLength(); i++) {
+            Attr attr = (Attr) attrs.item(i);
+            if (attr.getNamespaceURI() == null || !attr.getNamespaceURI().equals(XMLNS_NAMESPACE)) {
+                Attribute attribute = buildAttribute(elem, attr);
+                String key = NamedXmlNodeImpl.getKey(attribute.getNamespaceUri(), attribute
+                        .getLocalName());
+                map.put(key, attribute);
+            }
+        }
+        for (Iterator i = map.values().iterator(); i.hasNext();) {
+            elem.addAttribute((Node) i.next());
+        }
+        appendChildren(element, elem);
+        return elem;
+    }
+
+    protected void appendChildren(org.w3c.dom.Node domNode, AbstractComparableNode xmlNode) {
+        NodeList childNodes = domNode.getChildNodes();
+        StringBuffer textBuffer = null;
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            org.w3c.dom.Node node = (org.w3c.dom.Node) childNodes.item(i);
+            short type = node.getNodeType();
+            if (type != org.w3c.dom.Node.TEXT_NODE && type != org.w3c.dom.Node.CDATA_SECTION_NODE) {
+                textBuffer = appendText(xmlNode, textBuffer);
+            }
+            switch (type) {
+            case org.w3c.dom.Node.ELEMENT_NODE:
+                Node element = buildElement(xmlNode, (org.w3c.dom.Element) node);
+                xmlNode.appendChild(element);
+                break;
+            case org.w3c.dom.Node.TEXT_NODE:
+                if (textBuffer == null) {
+                    textBuffer = new StringBuffer();
+                }
+                String s = ((Text) node).getTextContent().replaceAll("\n", "");
+                textBuffer.append(s);
+                break;
+            case org.w3c.dom.Node.COMMENT_NODE:
+                String commentContent = ((Comment) node).getTextContent();
+                Node commentNode = new CommentImpl(xmlNode, commentContent);
+                xmlNode.appendChild(commentNode);
+                break;
+            case org.w3c.dom.Node.CDATA_SECTION_NODE:
+                if (textBuffer == null) {
+                    textBuffer = new StringBuffer();
+                }
+                textBuffer.append(((CDATASection) node).getTextContent());
+                break;
+            }
+        }
+        appendText(xmlNode, textBuffer);
+    }
+
+    protected StringBuffer appendText(AbstractComparableNode xmlNode, StringBuffer textBuffer) {
+        if (textBuffer != null) {
+            String s = textBuffer.toString().trim();
+            if (s.length() > 0) {
+                Node textNode = new TextNodeImpl(xmlNode, textBuffer.toString());
+                xmlNode.appendChild(textNode);
+            }
+            textBuffer = null;
+        }
+        return textBuffer;
+    }
+
+    protected Attribute buildAttribute(Element element, Attr attr) {
+        return new AttributeImpl(element, attr.getNamespaceURI(), attr.getLocalName(), attr
+                .getValue());
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/ElementImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/ElementImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/ElementImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/ElementImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,59 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.xml.Element;
+import org.apache.lenya.modules.diff.xml.XmlNodeVisitor;
+
+public class ElementImpl extends NamedXmlNodeImpl implements Element {
+
+    public ElementImpl(String namespaceUri, String localName) {
+        super(namespaceUri, localName);
+    }
+
+    public ElementImpl(Node parent, String namespaceUri, String localName) {
+        super(parent, namespaceUri, localName);
+    }
+
+    public void visit(XmlNodeVisitor visitor) {
+        visitor.elementOccured(this);
+    }
+
+    public String toString() {
+        return "<" + getLocalName() + "> (" + getNamespaceUri() + ")";
+    }
+
+    public String getPath() {
+        StringBuffer path = new StringBuffer();
+        int pos = -1;
+        Node parent = getParent();
+        path.append(parent.getPath());
+        Node[] children = parent.getChildren();
+        if (children.length > 1) {
+            List sameTypeChildren = new ArrayList();
+            for (int i = 0; i < children.length; i++) {
+                if (children[i] instanceof Element) {
+                    Element element = (Element) children[i];
+                    if (element.getLocalName().equals(getLocalName())
+                            && element.getNamespaceUri().equals(getNamespaceUri())) {
+                        sameTypeChildren.add(element);
+                    }
+                }
+            }
+            if (sameTypeChildren.size() > 1) {
+                pos = 0;
+                while (sameTypeChildren.get(pos) != this) {
+                    pos++;
+                }
+            }
+        }
+        path.append("/");
+        path.append(getLocalName());
+        if (pos > -1) {
+            path.append("[" + pos + "]");
+        }
+        return path.toString();
+    }
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/NamedXmlNodeImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/NamedXmlNodeImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/NamedXmlNodeImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/NamedXmlNodeImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,52 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import org.apache.lenya.modules.diff.AbstractComparableNode;
+import org.apache.lenya.modules.diff.Comparable;
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.xml.XmlNode;
+import org.apache.lenya.util.Assert;
+
+public abstract class NamedXmlNodeImpl extends AbstractComparableNode implements XmlNode {
+
+    private String namespaceUri;
+    private String localName;
+
+    public NamedXmlNodeImpl(String namespaceUri, String localName) {
+        initialize(namespaceUri, localName);
+    }
+
+    protected void initialize(String namespaceUri, String localName) {
+        Assert.notNull("localName", localName);
+        this.namespaceUri = namespaceUri;
+        this.localName = localName;
+    }
+
+    public NamedXmlNodeImpl(Node parent, String namespaceUri, String localName) {
+        super(parent);
+        initialize(namespaceUri, localName);
+    }
+
+    public String getNamespaceUri() {
+        return this.namespaceUri;
+    }
+
+    public String getLocalName() {
+        return this.localName;
+    }
+
+    public boolean equals(Comparable comparable) {
+        if (!super.equals(comparable)) {
+            return false;
+        }
+        NamedXmlNodeImpl otherNode = (NamedXmlNodeImpl) comparable;
+        String otherNs = otherNode.getNamespaceUri();
+        String ns = getNamespaceUri();
+        return ((ns == null && otherNs == null) || ns.equals(otherNs))
+                && otherNode.getLocalName().equals(getLocalName());
+    }
+
+    public static final String getKey(String namespaceUri, String localName) {
+        return namespaceUri + " " + localName;
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextNodeImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextNodeImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextNodeImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextNodeImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,21 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.xml.TextNode;
+import org.apache.lenya.modules.diff.xml.XmlNodeVisitor;
+
+public class TextNodeImpl extends TextValueXmlNode implements TextNode {
+    
+    public TextNodeImpl(Node parent, String text) {
+        super(parent, text);
+    }
+
+    public void visit(XmlNodeVisitor visitor) {
+        visitor.textNodeOccured(this);
+    }
+    
+    protected String getNodeType() {
+        return "text";
+    }
+
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextValueXmlNode.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextValueXmlNode.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextValueXmlNode.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/TextValueXmlNode.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,70 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lenya.modules.diff.AbstractComparableNode;
+import org.apache.lenya.modules.diff.Comparable;
+import org.apache.lenya.modules.diff.Node;
+import org.apache.lenya.modules.diff.xml.XmlNode;
+import org.apache.lenya.util.Assert;
+
+public abstract class TextValueXmlNode extends AbstractComparableNode implements XmlNode {
+
+    private String text;
+
+    public TextValueXmlNode(Node parent, String text) {
+        super(parent);
+        Assert.notNull("text", text);
+        this.text = text;
+    }
+
+    public boolean equals(Comparable comparable) {
+        if (!super.equals(comparable)) {
+            return false;
+        }
+        return getText().equals(((TextValueXmlNode) comparable).getText());
+    }
+
+    public String getText() {
+        return this.text;
+    }
+
+    public String toString() {
+        return getText();
+    }
+
+    protected int getPosition() {
+        Node parent = getParent();
+        Node[] children = parent.getChildren();
+        int pos = -1;
+        if (children.length > 1) {
+            List sameTypeChildren = new ArrayList();
+            for (int i = 0; i < children.length; i++) {
+                if (getClass().isInstance(children[i])) {
+                    sameTypeChildren.add(children[i]);
+                }
+            }
+            if (sameTypeChildren.size() > 1) {
+                pos = 0;
+                while (children[pos] != this) {
+                    pos++;
+                }
+            }
+        }
+        return pos;
+    }
+    
+    protected abstract String getNodeType();
+
+    public String getPath() {
+        int pos = getPosition();
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(getParent().getPath() + "/" + getNodeType() + "()");
+        if (pos > -1) {
+            buffer.append("[" + pos + "]");
+        }
+        return buffer.toString();
+    }
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/XmlDocumentImpl.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/XmlDocumentImpl.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/XmlDocumentImpl.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/modules/diff/xml/impl/XmlDocumentImpl.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,18 @@
+package org.apache.lenya.modules.diff.xml.impl;
+
+import org.apache.lenya.modules.diff.AbstractComparableNode;
+import org.apache.lenya.modules.diff.ComparableNode;
+import org.apache.lenya.modules.diff.xml.XmlDocument;
+import org.apache.lenya.modules.diff.xml.XmlNodeVisitor;
+
+public class XmlDocumentImpl extends AbstractComparableNode implements XmlDocument, ComparableNode {
+
+    public void visit(XmlNodeVisitor visitor) {
+        visitor.documentOccured(this);
+    }
+
+    public String getPath() {
+        return "";
+    }
+    
+}

Added: lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/Queue.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/Queue.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/Queue.java (added)
+++ lenya/sandbox/modules/diff/java/src/org/apache/lenya/util/Queue.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,67 @@
+package org.apache.lenya.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Queue {
+    
+    private List elements = new ArrayList();
+    
+    public Queue(List elements) {
+        Assert.notNull("elements", elements);
+        this.elements.addAll(elements);
+    }
+    
+    public Queue() {
+    }
+
+    public void in(Object object) {
+        this.elements.add(object);
+    }
+    
+    public Object out() {
+        if (isEmpty()) {
+            throw new IllegalStateException("The queue is empty.");
+        }
+        else {
+            Object o = this.elements.get(0);
+            this.elements.remove(0);
+            return o;
+        }
+    }
+    
+    public Object front() {
+        if (isEmpty()) {
+            throw new IllegalStateException("The queue is empty.");
+        }
+        else {
+            Object o = this.elements.get(0);
+            return o;
+        }
+    }
+    
+    public Iterator iterator() {
+        return this.elements.iterator();
+    }
+
+    public boolean isEmpty() {
+        return this.elements.isEmpty();
+    }
+    
+    public List getElements() {
+        return this.elements;
+    }
+
+    public int size() {
+        return this.elements.size();
+    }
+
+    public void addAll(List list) {
+        this.elements.addAll(list);
+    }
+    
+    public String toString() {
+        return this.elements.toString();
+    }
+}

Added: lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/DiffTest.java
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/DiffTest.java?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/DiffTest.java (added)
+++ lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/DiffTest.java Thu Apr 10 16:29:24 2008
@@ -0,0 +1,33 @@
+package org.apache.lenya.modules.diff.xml;
+
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+import org.apache.lenya.modules.diff.DifferenceTree;
+import org.apache.lenya.modules.diff.DifferenceTreeWriter;
+import org.apache.lenya.modules.diff.SimpleTreeDiff;
+import org.apache.lenya.modules.diff.Tree;
+import org.apache.lenya.modules.diff.TreeDiff;
+import org.apache.lenya.modules.diff.xml.DomTreeBuilder;
+import org.apache.lenya.modules.diff.xml.XmlNodeWriter;
+import org.apache.lenya.xml.DocumentHelper;
+import org.w3c.dom.Document;
+
+public class DiffTest extends TestCase {
+
+    public void testDiff() throws Exception {
+        
+        DomTreeBuilder builder = DomTreeBuilderFactory.createBuilder();
+        Tree[] trees = new Tree[2];
+        for (int i = 0; i < trees.length; i++) {
+            URL url = getClass().getResource("test" + (i + 1) + ".xml");
+            Document dom = DocumentHelper.readDocument(url);
+            trees[i] = builder.buildTree(dom);
+        }
+        TreeDiff diff = new SimpleTreeDiff();
+        DifferenceTree difference = diff.getDifferences(trees[0], trees[1]);
+        DifferenceTreeWriter diffWriter = new DifferenceTreeWriter(new XmlNodeWriter());
+        difference.traverse(diffWriter);
+    }
+}

Added: lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test1.xml
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test1.xml?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test1.xml (added)
+++ lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test1.xml Thu Apr 10 16:29:24 2008
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hello xmlns="http://apache.org/lenya/test/1.0">
+  <foo/>
+  <bar>
+    Hello
+    <baz a="x"/>
+  </bar>
+</hello>
\ No newline at end of file

Added: lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test2.xml
URL: http://svn.apache.org/viewvc/lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test2.xml?rev=647009&view=auto
==============================================================================
--- lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test2.xml (added)
+++ lenya/sandbox/modules/diff/java/test/org/apache/lenya/modules/diff/xml/test2.xml Thu Apr 10 16:29:24 2008
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<hello xmlns="http://apache.org/lenya/test/1.0" foo="bar">
+  <bar>
+    Hello<baz/>
+  </bar>
+  <foo a="b"/>
+</hello>
\ No newline at end of file
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.