added (broken, but running) diff routines for block-level diffs
[wikiq] / dtl / Diff3.hpp
diff --git a/dtl/Diff3.hpp b/dtl/Diff3.hpp
new file mode 100644 (file)
index 0000000..1740ec4
--- /dev/null
@@ -0,0 +1,245 @@
+/**
+   dtl-1.12 -- Diff Template Library
+   
+   In short, Diff Template Library is distributed under so called "BSD license",
+   
+   Copyright (c) 2008-2011 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+   All rights reserved.
+   
+   Redistribution and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+   
+   * Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+   
+   * Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+   
+   * Neither the name of the authors nor the names of its contributors
+   may be used to endorse or promote products derived from this software 
+   without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_DIFF3_H
+#define DTL_DIFF3_H
+
+namespace dtl {
+    
+    /**
+     * diff3 class template
+     * sequence must support random_access_iterator.
+     */
+    template <typename elem, typename sequence = vector< elem >, typename comparator = Compare< elem > >
+    class Diff3
+    {
+    private:
+        dtl_typedefs(elem, sequence)
+        sequence                           A;
+        sequence                           B;
+        sequence                           C;
+        sequence                           S;
+        Diff< elem, sequence, comparator > diff_ba;
+        Diff< elem, sequence, comparator > diff_bc;
+        bool                               conflict;
+        elem                               csepabegin;
+        elem                               csepa;
+        elem                               csepaend;
+    public :
+        Diff3 () {}
+        Diff3 (const sequence& a, 
+               const sequence& b, 
+               const sequence& c) : A(a), B(b), C(c), 
+                                    diff_ba(b, a), diff_bc(b, c), 
+                                    conflict(false) {} 
+        
+        ~Diff3 () {}
+        
+        bool isConflict () const {
+            return conflict;
+        }
+        
+        sequence getMergedSequence () const {
+            return S;
+        }
+        
+        /**
+         * merge changes B and C to A
+         */
+        bool merge () {
+            if (diff_ba.getEditDistance() == 0) {     // A == B
+                if (diff_bc.getEditDistance() == 0) { // A == B == C
+                    S = B;
+                    return true;
+                }
+                S = C;
+                return true;
+            } else {                                  // A != B
+                if (diff_bc.getEditDistance() == 0) { // A != B == C
+                    S = A;                              
+                    return true;
+                } else {                              // A != B != C
+                    S = merge_();
+                    if (isConflict()) {               // conflict occured
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        
+        /**
+         * compose differences
+         */
+        void compose () {
+            diff_ba.compose();
+            diff_bc.compose();
+        }
+        
+    private :
+        /**
+         * merge implementation
+         */
+        sequence merge_ () {
+            elemVec         seq;
+            Ses< elem >     ses_ba   = diff_ba.getSes();
+            Ses< elem >     ses_bc   = diff_bc.getSes();
+            sesElemVec      ses_ba_v = ses_ba.getSequence();
+            sesElemVec      ses_bc_v = ses_bc.getSequence();
+            sesElemVec_iter ba_it    = ses_ba_v.begin();
+            sesElemVec_iter bc_it    = ses_bc_v.begin();
+            sesElemVec_iter ba_end   = ses_ba_v.end();
+            sesElemVec_iter bc_end   = ses_bc_v.end();
+            
+            while (!isEnd(ba_end, ba_it) || !isEnd(bc_end, bc_it)) {
+                while (true) {
+                    if (!isEnd(ba_end, ba_it)            && 
+                        !isEnd(bc_end, bc_it)            &&
+                        ba_it->first == bc_it->first     && 
+                        ba_it->second.type == SES_COMMON && 
+                        bc_it->second.type == SES_COMMON) {
+                        // do nothing
+                    } else {
+                        break;
+                    }
+                    if      (!isEnd(ba_end, ba_it)) seq.push_back(ba_it->first);
+                    else if (!isEnd(bc_end, bc_it)) seq.push_back(bc_it->first);
+                    forwardUntilEnd(ba_end, ba_it);
+                    forwardUntilEnd(bc_end, bc_it);
+                }
+                if (isEnd(ba_end, ba_it) || isEnd(bc_end, bc_it)) break;
+                if (   ba_it->second.type == SES_COMMON 
+                       && bc_it->second.type == SES_DELETE) {
+                    forwardUntilEnd(ba_end, ba_it);
+                    forwardUntilEnd(bc_end, bc_it);
+                } else if (ba_it->second.type == SES_COMMON && 
+                           bc_it->second.type == SES_ADD) {
+                    seq.push_back(bc_it->first);
+                    forwardUntilEnd(bc_end, bc_it);
+                } else if (ba_it->second.type == SES_DELETE && 
+                           bc_it->second.type == SES_COMMON) {
+                    forwardUntilEnd(ba_end, ba_it);
+                    forwardUntilEnd(bc_end, bc_it);
+                } else if (ba_it->second.type == SES_DELETE && 
+                           bc_it->second.type == SES_DELETE) {
+                    if (ba_it->first == bc_it->first) {
+                        forwardUntilEnd(ba_end, ba_it);
+                        forwardUntilEnd(bc_end, bc_it);
+                    } else {
+                        // conflict
+                        conflict = true;
+                        return B;
+                    }
+                } else if (ba_it->second.type == SES_DELETE && 
+                           bc_it->second.type == SES_ADD) {
+                    // conflict
+                    conflict = true;
+                    return B;
+                } else if (ba_it->second.type == SES_ADD && 
+                           bc_it->second.type == SES_COMMON) {
+                    seq.push_back(ba_it->first);
+                    forwardUntilEnd(ba_end, ba_it);
+                } else if (ba_it->second.type == SES_ADD && 
+                           bc_it->second.type == SES_DELETE) {
+                    // conflict
+                    conflict = true;
+                    return B;
+                } else if (ba_it->second.type == SES_ADD && 
+                           bc_it->second.type == SES_ADD) {
+                    if (ba_it->first == bc_it->first) {
+                        seq.push_back(ba_it->first);
+                        forwardUntilEnd(ba_end, ba_it);
+                        forwardUntilEnd(bc_end, bc_it);
+                    } else {
+                        // conflict
+                        conflict = true;
+                        return B;
+                    }
+                }
+            }
+            
+            if (isEnd(ba_end, ba_it)) {
+                addDecentSequence(bc_end, bc_it, seq);
+            } else if (isEnd(bc_end, bc_it)) {
+                addDecentSequence(ba_end, ba_it, seq);
+            }
+            
+            sequence mergedSeq(seq.begin(), seq.end());
+            return mergedSeq;
+        }
+        
+        /**
+         * join elem vectors
+         */
+        void inline joinElemVec (elemVec& s1, elemVec& s2) const {
+            if (!s2.empty()) {
+                for (elemVec_iter vit=s2.begin();vit!=s2.end();++vit) {
+                    s1.push_back(*vit);
+                }
+            }
+        }
+        
+        /**
+         * check sequence is end
+         */
+        template <typename T_iter>
+        bool inline isEnd (const T_iter& end, const T_iter& it) const {
+            return it == end ? true : false;
+        }
+        
+        /**
+         * increment iterator until iterator is end
+         */
+        template <typename T_iter>
+        void inline forwardUntilEnd (const T_iter& end, T_iter& it) const {
+            if (!isEnd(end, it)) ++it;
+        }
+        
+        /**
+         * add elements which SES's type is ADD
+         */
+        void inline addDecentSequence (const sesElemVec_iter& end, sesElemVec_iter& it, elemVec& seq) const {
+            while (!isEnd(end, it)) {
+                if (it->second.type == SES_ADD) seq.push_back(it->first);
+                ++it;
+            }      
+        }
+        
+    };
+}
+
+#endif // DTL_DIFF3_H

Benjamin Mako Hill || Want to submit a patch?