OSDN Git Service

Avoid infinite loops in the RegularExpression::subst() function when the length of...
[winmerge-jp/winmerge-jp.git] / Src / DiffItem.h
1 /**
2  *  @file DiffItem.h
3  *
4  *  @brief Declaration of DIFFITEM
5  */
6 #pragma once
7
8 #include "DiffFileInfo.h"
9
10 // Uncomment this to show debug information in the folder comparison window.
11 // We don't use _DEBUG since the mapping of the setting (OPT_DIRVIEW_COLUMN_ORDERS or OPT_DIRVIEW3_COLUMN_ORDERS) shifts if this feature is enabled.
12 //#define SHOW_DIFFITEM_DEBUG_INFO
13
14 /**
15  * @brief Bitfield values for binary file sides.
16  * These values are used as bitfield values when determining which file(s)
17  * are binary files. There is no "both" -value since in bitfield "both" is
18  * when side1- and side2- bits are set (BINFILE_SIDE1 | BINFILE_SIDE2).
19  */
20 enum BINFILE_SIDE
21 {
22         BINFILE_NONE = 0, /**< No binary files detected. */
23         BINFILE_SIDE1, /**< First file was detected as binary file. */
24         BINFILE_SIDE2, /**< Second file was detected as binart file. */
25 };
26
27 /**
28  * @brief Status of one item comparison, stored as bitfields
29  *
30  * Bitmask can be seen as a 4 dimensional space; that is, there are four
31  * different attributes, and each entry picks one of each attribute
32  * independently.
33  *
34  * One dimension is how the compare went: same or different or
35  * skipped or error.
36  *
37  * One dimension is file mode: text or binary (text is only if
38  * both sides were text)
39  *
40  * One dimension is existence: both sides, left only, or right only
41  *
42  * One dimension is type: directory, or file
43  */
44 struct DIFFCODE
45 {
46         /**
47          * @brief values for DIFFITEM.diffcode
48          */
49         enum : unsigned long
50         {
51                 // We use extra bits so that no valid values are 0
52                 // and each set of flags is in a different hex digit
53                 // to make debugging easier
54                 // These can always be packed down in the future
55                 TEXTFLAGS=0x3FU, TEXT=0x1U, BIN=0x2U, BINSIDE1=0x4U, BINSIDE2=0x8U, BINSIDE3=0x10U, IMAGE=0x20U,
56                 TYPEFLAGS=0xC0U, FILE=0x40U, DIR=0x80U,
57                 COMPAREFLAGS=0x7000U, NOCMP=0x0000U, DIFF=0x1000U, SAME=0x2000U, CMPERR=0x3000U, CMPABORT=0x4000U,
58                 COMPAREFLAGS3WAY=0x18000U, DIFFALL=0x0000U, DIFF1STONLY=0x8000U, DIFF2NDONLY=0x10000U, DIFF3RDONLY=0x18000U,
59                 FILTERFLAGS=0x20000U, INCLUDED=0x00000U, SKIPPED=0x20000U,
60                 SCANFLAGS=0x100000U, NEEDSCAN=0x100000U,
61                 THREEWAYFLAGS=0x200000U, THREEWAY=0x200000U,
62                 SIDEFLAGS=0x70000000U, FIRST=0x10000000U, SECOND=0x20000000U, THIRD=0x40000000U, BOTH=0x30000000U, ALL=0x70000000U,
63         };
64
65         unsigned diffcode;
66
67         explicit DIFFCODE(unsigned diffcode = 0) : diffcode(diffcode) { }
68
69 protected:
70         /// Worker function, to check one area (mask) of code for a particular value (result)
71         static bool Check(unsigned code, unsigned mask, unsigned result) { return ((code & mask) == result); }
72         /// Convenience function to check the part of the code for comparison results
73         static bool CheckCompare(unsigned code, unsigned result) { return Check(code, DIFFCODE::COMPAREFLAGS, result); }
74         /// Convenience function to check the part of the code for filter status
75         static bool CheckFilter(unsigned code, unsigned result) { return Check(code, DIFFCODE::FILTERFLAGS, result); }
76         /// Convenience function to check the part of the code for side status (eg, left-only)
77         static bool CheckSide(unsigned code, unsigned result) { return Check(code, DIFFCODE::SIDEFLAGS, result); }
78
79         /// Worker function to set the area indicated by mask to specified result
80         void Set(int mask, unsigned result) { diffcode &= (~mask); diffcode |= result; }
81         /// Convenience function to set the side status, eg, SetSide(DIFFCODE::LEFT)
82         void SetSide(unsigned result) { Set(DIFFCODE::SIDEFLAGS, result); }
83 public:
84
85         // file/directory
86         bool isDirectory() const { return Check(diffcode, DIFFCODE::TYPEFLAGS, DIFFCODE::DIR); }
87         // left/right
88         bool isSideFirstOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST); }
89         bool isSideSecondOnly() const { return CheckSide(diffcode, DIFFCODE::SECOND); }
90         bool isSideThirdOnly() const { return CheckSide(diffcode, DIFFCODE::THIRD); }
91         bool isMissingFirstOnly() const { return CheckSide(diffcode, DIFFCODE::SECOND | DIFFCODE::THIRD); }
92         bool isMissingSecondOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST | DIFFCODE::THIRD); }
93         bool isMissingThirdOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST | DIFFCODE::SECOND); }
94         bool isSideBoth() const { return CheckSide(diffcode, DIFFCODE::BOTH); }
95         bool isSideAll() const { return CheckSide(diffcode, DIFFCODE::ALL); }
96         void setSideNone() { SetSide(0); }
97         void setSideFlag(int nIndex)
98         {
99                 switch (nIndex)
100                 {
101                 case 0: SetSide(diffcode | DIFFCODE::FIRST); return;
102                 case 1: SetSide(diffcode | DIFFCODE::SECOND); return;
103                 case 2: SetSide(diffcode | DIFFCODE::THIRD); return;
104                 }
105         }
106         void unsetSideFlag(int nIndex)
107         {
108                 switch (nIndex)
109                 {
110                 case 0: SetSide(diffcode & ~DIFFCODE::FIRST); return;
111                 case 1: SetSide(diffcode & ~DIFFCODE::SECOND); return;
112                 case 2: SetSide(diffcode & ~DIFFCODE::THIRD); return;
113                 }
114         }
115         bool isSideOnly(int nIndex) const
116         {
117                 switch (nIndex)
118                 {
119                 case 0: return isSideFirstOnly();
120                 case 1: return isSideSecondOnly();
121                 case 2: return isSideThirdOnly();
122                 default: return 0;
123                 }
124         }
125         bool existsFirst() const { return !!(diffcode & DIFFCODE::FIRST); }
126         bool existsSecond() const { return !!(diffcode & DIFFCODE::SECOND); }
127         bool existsThird() const { return !!(diffcode & DIFFCODE::THIRD); }
128         bool exists(int nIndex) const
129         {
130                 switch (nIndex)
131                 {
132                 case 0: return !!(diffcode & DIFFCODE::FIRST);
133                 case 1: return !!(diffcode & DIFFCODE::SECOND);
134                 case 2: return !!(diffcode & DIFFCODE::THIRD);
135                 default: return 0;
136                 }
137         }
138         bool existAll() const
139         {
140                 return ((diffcode & DIFFCODE::THREEWAY) ? DIFFCODE::ALL : DIFFCODE::BOTH) == (diffcode & DIFFCODE::ALL);
141         }
142
143         // compare result
144         bool isResultNone() const { return CheckCompare(diffcode, 0); }
145         bool isResultSame() const { return CheckCompare(diffcode, DIFFCODE::SAME); }
146         bool isResultDiff() const { return (CheckCompare(diffcode, DIFFCODE::DIFF) && !isResultFiltered() &&
147                         existAll()); }
148         static bool isResultError(unsigned code) { return CheckCompare(code, DIFFCODE::CMPERR); }
149         bool isResultError() const { return isResultError(diffcode); }
150         static bool isResultAbort(unsigned code) { return CheckCompare(code, DIFFCODE::CMPABORT); }
151         bool isResultAbort() const { return isResultAbort(diffcode); }
152         // filter status
153         bool isResultFiltered() const { return CheckFilter(diffcode, DIFFCODE::SKIPPED); }
154         // type
155         bool isText() const { return Check(diffcode, DIFFCODE::TEXTFLAGS, DIFFCODE::TEXT); }
156         bool isBin() const { return (diffcode & DIFFCODE::BIN) != 0; }
157         bool isImage() const { return (diffcode & DIFFCODE::IMAGE) != 0; }
158         // rescan
159         bool isScanNeeded() const { return ((diffcode & DIFFCODE::SCANFLAGS) == DIFFCODE::NEEDSCAN); }
160
161         void swap(int idx1, int idx2);
162 };
163
164 enum ViewCustomFlags
165 {
166         // No valid values are 0
167         INVALID_CODE = 0,
168         VISIBILITY = 0x3U, VISIBLE = 0x1U, HIDDEN = 0x2U, EXPANDED = 0x4U
169 };
170
171 /**
172  * @brief information about one file/folder item.
173  * This class holds information about one compared "item" in the folder comparison tree.
174  * The item can be a file item or folder item.  The item can have data from
175  * both (or all three) compare sides (file/folder exists in all sides) or from
176  * fewer sides (file/folder is missing in one/two sides).
177  *
178  * The basic structure consists of linkage to the `parent` folder and possible "sibling"
179  * file/folder items that share the identical `parent` folder item.  Folder items also
180  * have a `children` link indicating the head of another DIFFITEM list of contained 
181  * files/folders.  
182  *
183  * This class is for backend differences processing, representing physical
184  * files and folders. This class is not for GUI data like selection or
185  * visibility statuses. So do not include any GUI-dependent data here. 
186  */
187
188 // See http://web.eecs.utk.edu/~bvz/teaching/cs140Fa09/notes/Dllists for a discussion of
189 // "doubly linked lists".  The "sibling" linkage used here is basically a doubly linked
190 // list (similar to what is described in that article), with the notes ...
191 //              A. Items may be deleted anywhere within the tree (e.g. via "Refresh Selected (ctrl-F5)").
192 //                      This justifies the usage of a double link list.
193 //              B. Lists are only traversed in the forward direction (using `Flink`).  Using `nullptr`  
194 //                      to stop this traversal is both obvious and convenient.
195 //              C. Items are only inserted at the end, so storing the end's link (for a future insertion) 
196 //                      into the head's `Blink` serves the function of the "sentinel node" without consuming 
197 //                      extra space.
198 //              D. A "sentinel" structure is not necessary because ...
199 //                      D1. The head of a sibling list can always be found directly from the parent folder 
200 //                              item by `this->children`.  From an arbitrary sibling item, the head is at
201 //                              `this->parent->children` .
202 //                      D2. Similarily, the end of a sibling list is found (from the parent folder item) at
203 //                              `this->children->Blink` or (from a sibling item) at `this->parent->children->Blink`.
204 //              E. The "root" item for a DIFFITEM tree is `m_pRoot`, declared in and managed by the
205 //                      DiffItemList class.  There is one "root" for each active folder comparison 
206 //                      (i.e. each folder comparison window in the GUI).  The "root" item exists to anchor the 
207 //                      tree, as well as to guarantee that the `parent` and `children` linkage is correct at the 
208 //                      top folder comparison level.
209 //
210 //
211 // Sometimes a picture is worth many words...
212 //
213 // |------P1-------| 
214 // | "parent item" |  
215 // | parent -------|---> P0
216 // | Blink & Flink |
217 // | children -----|->\
218 // |---------------|  |
219 //                    |
220 //                    |  /--------------------------------------------------------------------------->\
221 //                    |  |                                                                            |
222 //                    |  |   |---P1.C0-----|           |---P1.S1-----|           |---P1.S2-----|      |
223 //                    |  |   | data values |           | data values |           | data values |      | 
224 //                    |  |   |-------------|           |-------------|           |-------------|      |    
225 //                    |  \<--|--Blink      |<----------|--Blink      |<----------|--Blink      |<-----/
226 //                    |      |  Flink -----|---------->|  Flink -----|---------->|  Flink -----|--------> `nullptr`
227 //                    \----->|  children---|->\        |  children---|->\        |  children---|->\
228 //            P1<------------|--parent     |  |   P1<--|--parent     |  |   P1<--|--parent     |  |
229 //                           |-------------|  |        |-------------|  |        |-------------|  | 
230 //                                            |                         |                         |  
231 //                                            \--> P1.C0.C0             \--> P1.S1.C0             \--> P1.S2.C0
232 //                                            \--> `nullptr`            \--> `nullptr`            \--> `nullptr`
233 //                                                                                      
234
235 class DIFFITEM 
236 {
237 //**** DIFFITEM data values
238
239 public:
240         DiffFileInfo diffFileInfo[3];   /**< Fileinfo for left/middle/right file. */
241         int     nsdiffs;                                        /**< Amount of non-ignored differences */
242         int nidiffs;                                    /**< Amount of ignored differences */
243                                                                         // Note: Keep `diffFileInfo[]`, `nsdiffs` and `nidiffs`
244                                                                         //               near front of class for small offsets.
245                                                                         // (see `DirColInfo` arrays in `DirViewColItems.cpp`) *>
246         DIFFCODE diffcode;                              /**< Compare result */
247         unsigned customFlags;                   /**< ViewCustomFlags flags */
248
249         String getFilepath(int nIndex, const String &sRoot) const;
250         String getItemRelativePath() const;
251
252         void Swap(int idx1, int idx2);
253         void ClearAllAdditionalProperties();
254
255 //**** Child, Parent, Sibling linkage
256 private:                                                        // Don't allow direct external manipulation of link values
257         DIFFITEM *parent;                               /**< Parent of current item */
258         DIFFITEM *children;                             /**< Link to first child of this item */
259         DIFFITEM *Flink;                                /**< Forward "sibling" link.  The forward linkage ends with
260                                                                                  a `nullptr` in the final (newest) item. */
261         DIFFITEM *Blink;                                /**< Backward "sibling" link.  The backward linkage is circular,
262                                                                                  with the first (oldest) item (pointed to by `this->parent->children`)
263                                                                                  pointing to the last (newest) item. This is for easy insertion. */
264         void AppendSibling(DIFFITEM *p);
265
266 public:
267         void DelinkFromSiblings();
268         void AddChildToParent(DIFFITEM *p);
269         void RemoveChildren();
270         int GetDepth() const;
271         bool IsAncestor(const DIFFITEM *pdi) const;
272         std::vector<const DIFFITEM*> GetAncestors() const;
273         inline DIFFITEM *GetFwdSiblingLink() const { return Flink; }
274         inline DIFFITEM *GetFirstChild() const { return children; }
275         inline DIFFITEM *GetParentLink() const { return parent; }
276 #ifdef SHOW_DIFFITEM_DEBUG_INFO
277         inline DIFFITEM *GetBackwardSiblingLink() const { return Blink; }
278 #endif // SHOW_DIFFITEM_DEBUG_INFO
279
280         /** @brief Return whether the current DIFFITEM has children */
281         inline bool HasChildren() const { return (children != nullptr); }
282         /** @brief Return whether the current DIFFITEM has children */
283         inline bool HasParent() const { return (parent != nullptr); }
284
285 //**** The `emptyitem` and its access procedures
286 private:
287         static DIFFITEM emptyitem;              /**< Singleton to represent a DIFFITEM that doesn't have any data */
288
289 public:
290         static DIFFITEM *GetEmptyItem();
291         inline bool isEmpty() const { return this == GetEmptyItem(); /* to invoke "emptiness" checking */ }
292
293 //**** CTOR, DTOR
294 public:
295         DIFFITEM() : parent(nullptr), children(nullptr), Flink(nullptr), Blink(nullptr), 
296                                         nidiffs(-1), nsdiffs(-1), customFlags(ViewCustomFlags::INVALID_CODE) 
297                                         // `DiffFileInfo` and `DIFFCODE` have their own initializers. 
298                                         {}
299         ~DIFFITEM();
300
301 };