1 // TextPairs.cpp
2
3 #include "StdAfx.h"
4
5 #include "TextPairs.h"
6
7 static const wchar_t kNewLineChar = '\n';
8 static const wchar_t kQuoteChar = '\"';
9
10 static const wchar_t kBOM = (wchar_t)0xFEFF;
11
IsSeparatorChar(wchar_t c)12 static bool IsSeparatorChar(wchar_t c)
13 {
14 return (c == ' ' || c == '\t');
15 }
16
RemoveCr(UString & s)17 static void RemoveCr(UString &s)
18 {
19 s.RemoveChar(L'\x0D');
20 }
21
GetIDString(const wchar_t * srcString,unsigned & finishPos)22 static UString GetIDString(const wchar_t *srcString, unsigned &finishPos)
23 {
24 UString result;
25 bool quotes = false;
26 for (finishPos = 0;;)
27 {
28 wchar_t c = srcString[finishPos];
29 if (c == 0)
30 break;
31 finishPos++;
32 bool isSeparatorChar = IsSeparatorChar(c);
33 if (c == kNewLineChar || (isSeparatorChar && !quotes)
34 || (c == kQuoteChar && quotes))
35 break;
36 else if (c == kQuoteChar)
37 quotes = true;
38 else
39 result += c;
40 }
41 result.Trim();
42 RemoveCr(result);
43 return result;
44 }
45
GetValueString(const wchar_t * srcString,unsigned & finishPos)46 static UString GetValueString(const wchar_t *srcString, unsigned &finishPos)
47 {
48 UString result;
49 for (finishPos = 0;;)
50 {
51 wchar_t c = srcString[finishPos];
52 if (c == 0)
53 break;
54 finishPos++;
55 if (c == kNewLineChar)
56 break;
57 result += c;
58 }
59 result.Trim();
60 RemoveCr(result);
61 return result;
62 }
63
GetTextPairs(const UString & srcString,CObjectVector<CTextPair> & pairs)64 static bool GetTextPairs(const UString &srcString, CObjectVector<CTextPair> &pairs)
65 {
66 pairs.Clear();
67 unsigned pos = 0;
68
69 if (srcString.Len() > 0)
70 {
71 if (srcString[0] == kBOM)
72 pos++;
73 }
74 while (pos < srcString.Len())
75 {
76 unsigned finishPos;
77 UString id = GetIDString((const wchar_t *)srcString + pos, finishPos);
78 pos += finishPos;
79 if (id.IsEmpty())
80 continue;
81 UString value = GetValueString((const wchar_t *)srcString + pos, finishPos);
82 pos += finishPos;
83 if (!id.IsEmpty())
84 {
85 CTextPair pair;
86 pair.ID = id;
87 pair.Value = value;
88 pairs.Add(pair);
89 }
90 }
91 return true;
92 }
93
ComparePairIDs(const UString & s1,const UString & s2)94 static int ComparePairIDs(const UString &s1, const UString &s2)
95 { return MyStringCompareNoCase(s1, s2); }
96
ComparePairItems(const CTextPair & p1,const CTextPair & p2)97 static int ComparePairItems(const CTextPair &p1, const CTextPair &p2)
98 { return ComparePairIDs(p1.ID, p2.ID); }
99
ComparePairItems(void * const * a1,void * const * a2,void *)100 static int ComparePairItems(void *const *a1, void *const *a2, void * /* param */)
101 { return ComparePairItems(**(const CTextPair *const *)a1, **(const CTextPair *const *)a2); }
102
Sort()103 void CPairsStorage::Sort() { Pairs.Sort(ComparePairItems, NULL); }
104
FindID(const UString & id,unsigned & insertPos) const105 int CPairsStorage::FindID(const UString &id, unsigned &insertPos) const
106 {
107 unsigned left = 0, right = Pairs.Size();
108 while (left != right)
109 {
110 const unsigned mid = (left + right) / 2;
111 const int compResult = ComparePairIDs(id, Pairs[mid].ID);
112 if (compResult == 0)
113 {
114 insertPos = mid; // to disable GCC warning
115 return (int)mid;
116 }
117 if (compResult < 0)
118 right = mid;
119 else
120 left = mid + 1;
121 }
122 insertPos = left;
123 return -1;
124 }
125
FindID(const UString & id) const126 int CPairsStorage::FindID(const UString &id) const
127 {
128 unsigned pos;
129 return FindID(id, pos);
130 }
131
AddPair(const CTextPair & pair)132 void CPairsStorage::AddPair(const CTextPair &pair)
133 {
134 unsigned insertPos;
135 const int pos = FindID(pair.ID, insertPos);
136 if (pos >= 0)
137 Pairs[pos] = pair;
138 else
139 Pairs.Insert(insertPos, pair);
140 }
141
DeletePair(const UString & id)142 void CPairsStorage::DeletePair(const UString &id)
143 {
144 const int pos = FindID(id);
145 if (pos >= 0)
146 Pairs.Delete((unsigned)pos);
147 }
148
GetValue(const UString & id,UString & value) const149 bool CPairsStorage::GetValue(const UString &id, UString &value) const
150 {
151 value.Empty();
152 const int pos = FindID(id);
153 if (pos < 0)
154 return false;
155 value = Pairs[pos].Value;
156 return true;
157 }
158
GetValue(const UString & id) const159 UString CPairsStorage::GetValue(const UString &id) const
160 {
161 const int pos = FindID(id);
162 if (pos < 0)
163 return UString();
164 return Pairs[pos].Value;
165 }
166
ReadFromString(const UString & text)167 bool CPairsStorage::ReadFromString(const UString &text)
168 {
169 bool result = ::GetTextPairs(text, Pairs);
170 if (result)
171 Sort();
172 else
173 Pairs.Clear();
174 return result;
175 }
176
SaveToString(UString & text) const177 void CPairsStorage::SaveToString(UString &text) const
178 {
179 FOR_VECTOR (i, Pairs)
180 {
181 const CTextPair &pair = Pairs[i];
182 bool multiWord = (pair.ID.Find(L' ') >= 0);
183 if (multiWord)
184 text.Add_Char('\"');
185 text += pair.ID;
186 if (multiWord)
187 text.Add_Char('\"');
188 text.Add_Space();
189 text += pair.Value;
190 text.Add_Char('\x0D');
191 text.Add_LF();
192 }
193 }
194