1
2
3
4
5
6 #include <cpp2cm/cpp2cm/SourceFile.hpp>
7 #include <soulng/util/CodeFormatter.hpp>
8 #include <soulng/util/MappedInputFile.hpp>
9 #include <soulng/util/Unicode.hpp>
10 #include <algorithm>
11 #include <stdexcept>
12 #include <fstream>
13
14 namespace cpp2cm {
15
16 SourceFile::SourceFile(const std::u32string& name_, const std::string& path_) : name(name_), path(path_), inMemory(false)
17 {
18 }
19
20 void SourceFile::Read()
21 {
22 inMemory = true;
23 std::string content = soulng::util::ReadFile(path);
24 std::u32string text = soulng::unicode::ToUtf32(content);
25 std::u32string line;
26 int state = 0;
27 int lineNumber = 1;
28 for (char32_t c : text)
29 {
30 switch (state)
31 {
32 case 0:
33 {
34 if (c == '\n')
35 {
36 std::list<std::u32string>::iterator it = lines.insert(lines.cend(), line);
37 lineIteratorMap[lineNumber++] = it;
38 line.clear();
39 state = 1;
40 }
41 else if (c != '\r')
42 {
43 line.append(1, c);
44 }
45 break;
46 }
47 case 1:
48 {
49 if (c == '\n')
50 {
51 std::list<std::u32string>::iterator it = lines.insert(lines.cend(), line);
52 lineIteratorMap[lineNumber++] = it;
53 line.clear();
54 }
55 else if (c != '\r')
56 {
57 line.append(1, c);
58 state = 0;
59 }
60 break;
61 }
62 }
63 }
64 if (state == 0)
65 {
66 std::list<std::u32string>::iterator it = lines.insert(lines.cend(), line);
67 lineIteratorMap[lineNumber++] = it;
68 }
69 }
70
71 void SourceFile::Write()
72 {
73 inMemory = false;
74 std::ofstream file(path);
75 soulng::util::CodeFormatter formatter(file);
76 for (const auto& line : lines)
77 {
78 formatter.WriteLine(soulng::unicode::ToUtf8(line));
79 }
80 }
81
82 void SourceFile::Apply(Patch* patch)
83 {
84 std::list<std::u32string>::iterator lineIt = std::list<std::u32string>::iterator();
85 int lineNumber = patch->LineNumber();
86 auto it = lineIteratorMap.find(lineNumber);
87 if (it != lineIteratorMap.cend())
88 {
89 lineIt = it->second;
90 }
91 else
92 {
93 switch (patch->GetKind())
94 {
95 case Patch::Kind::insert_:
96 {
97 throw std::runtime_error("cannot insert: line number " + std::to_string(lineNumber) + " not found from file '" + path + "'");
98 }
99 case Patch::Kind::delete_:
100 {
101 throw std::runtime_error("cannot delete: line number " + std::to_string(lineNumber) + " not found from file '" + path + "'");
102 }
103 case Patch::Kind::modify_:
104 {
105 throw std::runtime_error("cannot modify: line number " + std::to_string(lineNumber) + " not found from file '" + path + "'");
106 }
107 }
108 }
109 switch (patch->GetKind())
110 {
111 case Patch::Kind::insert_:
112 {
113 InsertPatch* insertPatch = static_cast<InsertPatch*>(patch);
114 lines.insert(lineIt, insertPatch->Text());
115 break;
116 }
117 case Patch::Kind::delete_:
118 {
119 lines.erase(lineIt);
120 break;
121 }
122 case Patch::Kind::modify_:
123 {
124 ModifyPatch* modifyPatch = static_cast<ModifyPatch*>(patch);
125 std::u32string line = *lineIt;
126 std::u32string::size_type pos = line.find(modifyPatch->OldText());
127 if (pos != std::u32string::npos)
128 {
129 line.replace(pos, modifyPatch->OldText().size(), modifyPatch->NewText());
130 *lineIt = line;
131 }
132 else
133 {
134 throw std::runtime_error("cannot modify: text '" + soulng::unicode::ToUtf8(modifyPatch->OldText()) + "' not found from file '" + path + "' line " + std::to_string(lineNumber));
135 }
136 break;
137 }
138 }
139 }
140
141 struct ByName
142 {
143 bool operator()(std::std::unique_ptr<SourceFile>&left, std::std::unique_ptr<SourceFile>&right) const
144 {
145 return left->Name() < right->Name();
146 }
147 };
148
149 SourceFiles::SourceFiles()
150 {
151 }
152
153 void SourceFiles::Sort()
154 {
155 std::sort(sourceFiles.begin(), sourceFiles.end(), ByName());
156 }
157
158 void SourceFiles::Add(SourceFile* sourceFile)
159 {
160 sourceFileMap[sourceFile->Name()] = sourceFile;
161 sourceFiles.push_back(std::unique_ptr<SourceFile>(sourceFile));
162 }
163
164 void SourceFiles::Apply(PatchFile* patches)
165 {
166 for (const auto& patch : patches->Patches())
167 {
168 const std::u32string& fileName = patch->FileName();
169 auto it = sourceFileMap.find(fileName);
170 if (it != sourceFileMap.cend())
171 {
172 SourceFile* sourceFile = it->second;
173 if (!sourceFile->InMemory())
174 {
175 sourceFile->Read();
176 }
177 sourceFile->Apply(patch.get());
178 }
179 else
180 {
181 throw std::runtime_error("source file '" + soulng::unicode::ToUtf8(fileName) + "' not found");
182 }
183 }
184 }
185
186 void SourceFiles::Write()
187 {
188 for (const auto& sourceFile : sourceFiles)
189 {
190 if (sourceFile->InMemory())
191 {
192 sourceFile->Write();
193 }
194 }
195 }
196
197 }