Use o3tl::convert in Math
[LibreOffice.git] / unoidl / source / sourcetreeprovider.cxx
blob404938c376cca03a301564b3d6d9c43c10e84fd5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
11 #include <sal/log.hxx>
13 #include <map>
14 #include <vector>
16 #include <osl/file.h>
17 #include <osl/file.hxx>
18 #include <rtl/character.hxx>
19 #include <rtl/ref.hxx>
20 #include <rtl/ustrbuf.hxx>
21 #include <rtl/ustring.hxx>
22 #include <unoidl/unoidl.hxx>
24 #include "sourceprovider-scanner.hxx"
25 #include "sourcetreeprovider.hxx"
27 #if defined MACOSX
28 #include <dirent.h>
29 #include <osl/thread.h>
30 #endif
32 namespace unoidl::detail {
34 namespace {
36 //TODO: Bad hack to work around osl::FileStatus::getFileName not determining the
37 // original spelling of a file name (not even with
38 // osl_FileStatus_Mask_Validate):
39 OUString getFileName(OUString const & uri, osl::FileStatus const & status) {
40 #if defined MACOSX
41 sal_Int32 i = uri.lastIndexOf('/') + 1;
42 OUString path;
43 if (osl::FileBase::getSystemPathFromFileURL(uri.copy(0, i), path)
44 != osl::FileBase::E_None)
46 SAL_WARN(
47 "unoidl",
48 "cannot getSystemPathFromFileURL(" << uri.copy(0, i) << ")");
49 return status.getFileName();
51 OString dir(OUStringToOString(path, osl_getThreadTextEncoding()));
52 OString name(OUStringToOString(uri.subView(i), osl_getThreadTextEncoding()));
53 DIR * d = opendir(dir.getStr());
54 if (d == nullptr) {
55 SAL_WARN("unoidl", "cannot opendir(" << dir << ")");
56 return status.getFileName();
58 for (;;) {
59 dirent ent;
60 dirent * p;
61 int e = readdir_r(d, &ent, &p);
62 if (e != 0) {
63 SAL_WARN("unoidl", "cannot readdir_r");
64 closedir(d);
65 return status.getFileName();
67 if (p == nullptr) {
68 SAL_WARN(
69 "unoidl", "cannot find " << name << " via readdir of " << dir);
70 closedir(d);
71 return status.getFileName();
73 if (name.equalsIgnoreAsciiCase(p->d_name)) {
74 closedir(d);
75 return OUString(
76 p->d_name, std::strlen(p->d_name), osl_getThreadTextEncoding());
79 #else
80 (void) uri;
81 return status.getFileName();
82 #endif
85 bool exists(OUString const & uri, bool directory) {
86 osl::DirectoryItem item;
87 osl::FileStatus status(
88 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName);
89 return osl::DirectoryItem::get(uri, item) == osl::FileBase::E_None
90 && item.getFileStatus(status) == osl::FileBase::E_None
91 && (status.getFileType() == osl::FileStatus::Directory) == directory
92 && getFileName(uri, status) == uri.subView(uri.lastIndexOf('/') + 1);
95 class Cursor: public MapCursor {
96 public:
97 Cursor(Manager& manager, OUString const & uri): manager_(manager), directory_(uri) {
98 auto const rc = directory_.open();
99 SAL_WARN_IF(
100 rc != osl::FileBase::E_None, "unoidl", "open(" << uri << ") failed with " << +rc);
103 private:
104 virtual ~Cursor() noexcept override {}
106 virtual rtl::Reference<Entity> getNext(OUString *) override;
108 Manager& manager_;
109 osl::Directory directory_;
112 class SourceModuleEntity: public ModuleEntity {
113 public:
114 SourceModuleEntity(Manager& manager, OUString const & uri): manager_(manager), uri_(uri) {}
116 private:
117 virtual ~SourceModuleEntity() noexcept override {}
119 virtual std::vector<OUString> getMemberNames() const override
120 { return std::vector<OUString>(); } //TODO
122 virtual rtl::Reference< MapCursor > createCursor() const override
123 { return new Cursor(manager_, uri_); }
125 Manager& manager_;
126 OUString uri_;
129 bool isValidFileName(OUString const & name, bool directory) {
130 for (sal_Int32 i = 0;; ++i) {
131 if (i == name.getLength()) {
132 if (i == 0) {
133 return false;
135 return directory;
137 auto const c = name[i];
138 if (c == '.') {
139 if (i == 0 || name[i - 1] == '_') {
140 return false;
142 return !directory && name.subView(i + 1) == u"idl";
143 } else if (c == '_') {
144 //TODO: Ignore case of name[0] only for case-insensitive file systems:
145 if (i == 0 || name[i - 1] == '_') {
146 return false;
148 } else if (rtl::isAsciiDigit(c)) {
149 if (i == 0) {
150 return false;
152 } else if (!rtl::isAsciiAlpha(c)) {
153 return false;
160 rtl::Reference<Entity> Cursor::getNext(OUString * name) {
161 assert(name != nullptr);
162 for (;;) {
163 osl::DirectoryItem i;
164 auto rc = directory_.getNextItem(i);
165 switch (rc) {
166 case osl::FileBase::E_None:
168 osl::FileStatus stat(
169 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
170 osl_FileStatus_Mask_FileURL);
171 rc = i.getFileStatus(stat);
172 if (rc != osl::FileBase::E_None) {
173 SAL_WARN(
174 "unoidl",
175 "getFileSatus in <" << directory_.getURL() << "> failed with " << +rc);
176 continue;
178 auto const dir = stat.getFileType() == osl::FileStatus::Directory;
179 if (!isValidFileName(stat.getFileName(), dir)) {
180 continue;
182 if (dir) {
183 //TODO: Using osl::FileStatus::getFileName can likely cause issues on case-
184 // insensitive/preserving file systems, see the free getFileName function above
185 // (which likely goes unnoticed if module identifiers follow the convention of
186 // being all-lowercase):
187 *name = stat.getFileName();
188 return new SourceModuleEntity(manager_, stat.getFileURL());
189 } else {
190 SourceProviderScannerData data(&manager_);
191 if (!parse(stat.getFileURL(), &data)) {
192 SAL_WARN("unoidl", "cannot parse <" << stat.getFileURL() << ">");
193 continue;
195 auto ent = data.entities.end();
196 for (auto j = data.entities.begin(); j != data.entities.end(); ++j) {
197 if (j->second.kind == SourceProviderEntity::KIND_EXTERNAL
198 || j->second.kind == SourceProviderEntity::KIND_MODULE)
200 continue;
202 if (ent != data.entities.end()) {
203 throw FileFormatException(
204 stat.getFileURL(), "source file defines more than one entity");
206 ent = j;
208 if (ent == data.entities.end()) {
209 throw FileFormatException(
210 stat.getFileURL(), "source file defines no entity");
212 //TODO: Check that the entity's name matches the suffix of stat.getFileURL():
213 *name = ent->first.copy(ent->first.lastIndexOf('.') + 1);
214 return ent->second.entity;
217 default:
218 SAL_WARN( "unoidl", "getNext from <" << directory_.getURL() << "> failed with " << +rc);
219 [[fallthrough]];
220 case osl::FileBase::E_NOENT:
221 return {};
226 SourceTreeProvider::SourceTreeProvider(Manager & manager, OUString const & uri):
227 manager_(manager), uri_(uri.endsWith("/") ? uri : uri + "/")
230 rtl::Reference<MapCursor> SourceTreeProvider::createRootCursor() const {
231 return new Cursor(manager_, uri_);
234 rtl::Reference<Entity> SourceTreeProvider::findEntity(OUString const & name)
235 const
237 std::map< OUString, rtl::Reference<Entity> >::iterator ci(
238 cache_.find(name));
239 if (ci != cache_.end()) {
240 return ci->second;
242 // Match name against
243 // name ::= identifier ("." identifier)*
244 // identifier ::= upper-blocks | lower-block
245 // upper-blocks ::= upper ("_"? alnum)*
246 // lower-block :== lower alnum*
247 // alnum ::= digit | upper | lower
248 // digit ::= "0"--"9"
249 // upper ::= "A"--"Z"
250 // lower ::= "a"--"z"
251 OUStringBuffer buf(name);
252 sal_Int32 start = 0;
253 sal_Int32 i = 0;
254 for (; i != name.getLength(); ++i) {
255 sal_Unicode c = name[i];
256 if (c == '.') {
257 assert(i == start || i != 0);
258 if (i == start || name[i - 1] == '_') {
259 throw FileFormatException( //TODO
260 "", "Illegal UNOIDL identifier \"" + name + "\"");
262 buf[i] = '/';
263 start = i + 1;
264 } else if (c == '_') {
265 assert(i == start || i != 0);
266 if (i == start || name[i - 1] == '_'
267 || !rtl::isAsciiUpperCase(name[start]))
269 throw FileFormatException( //TODO
270 "", "Illegal UNOIDL identifier \"" + name + "\"");
272 } else if (rtl::isAsciiDigit(c)) {
273 if (i == start) {
274 throw FileFormatException( //TODO
275 "", "Illegal UNOIDL identifier \"" + name + "\"");
277 } else if (!rtl::isAsciiAlpha(c)) {
278 throw FileFormatException( //TODO
279 "", "Illegal UNOIDL identifier \"" + name + "\"");
282 if (i == start) {
283 throw FileFormatException( //TODO
284 "", "Illegal UNOIDL identifier \"" + name + "\"");
286 OUString uri(uri_ + buf.makeStringAndClear());
287 rtl::Reference<Entity> ent;
288 // Prevent conflicts between foo/ and Foo.idl on case-preserving file
289 // systems:
290 if (exists(uri, true) && !exists(uri + ".idl", false)) {
291 ent = new SourceModuleEntity(manager_, uri);
292 } else {
293 uri += ".idl";
294 SourceProviderScannerData data(&manager_);
295 if (parse(uri, &data)) {
296 std::map<OUString, SourceProviderEntity>::const_iterator j(
297 data.entities.find(name));
298 if (j != data.entities.end()) {
299 ent = j->second.entity;
301 SAL_WARN_IF(
302 !ent.is(), "unoidl",
303 "<" << uri << "> does not define entity " << name);
306 cache_.emplace(name, ent);
307 return ent;
310 SourceTreeProvider::~SourceTreeProvider() noexcept {}
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */