1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  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(1c);
 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(1c);
 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(lineItinsertPatch->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(posmodifyPatch->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>&leftstd::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 } // namespace cpp2cm