1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | // Author: kenton@google.com (Kenton Varda) |
32 | // Based on original Protocol Buffers design by |
33 | // Sanjay Ghemawat, Jeff Dean, and others. |
34 | // |
35 | // This file is the public interface to the .proto file parser. |
36 | |
37 | #ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ |
38 | #define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ |
39 | |
40 | #include <set> |
41 | #include <string> |
42 | #include <utility> |
43 | #include <vector> |
44 | |
45 | #include <google/protobuf/compiler/parser.h> |
46 | #include <google/protobuf/descriptor.h> |
47 | #include <google/protobuf/descriptor_database.h> |
48 | |
49 | // Must be included last. |
50 | #include <google/protobuf/port_def.inc> |
51 | |
52 | namespace google { |
53 | namespace protobuf { |
54 | |
55 | namespace io { |
56 | class ZeroCopyInputStream; |
57 | } |
58 | |
59 | namespace compiler { |
60 | |
61 | // Defined in this file. |
62 | class Importer; |
63 | class MultiFileErrorCollector; |
64 | class SourceTree; |
65 | class DiskSourceTree; |
66 | |
67 | // TODO(kenton): Move all SourceTree stuff to a separate file? |
68 | |
69 | // An implementation of DescriptorDatabase which loads files from a SourceTree |
70 | // and parses them. |
71 | // |
72 | // Note: This class is not thread-safe since it maintains a table of source |
73 | // code locations for error reporting. However, when a DescriptorPool wraps |
74 | // a DescriptorDatabase, it uses mutex locking to make sure only one method |
75 | // of the database is called at a time, even if the DescriptorPool is used |
76 | // from multiple threads. Therefore, there is only a problem if you create |
77 | // multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase |
78 | // and use them from multiple threads. |
79 | // |
80 | // Note: This class does not implement FindFileContainingSymbol() or |
81 | // FindFileContainingExtension(); these will always return false. |
82 | class PROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase { |
83 | public: |
84 | SourceTreeDescriptorDatabase(SourceTree* source_tree); |
85 | |
86 | // If non-NULL, fallback_database will be checked if a file doesn't exist in |
87 | // the specified source_tree. |
88 | SourceTreeDescriptorDatabase(SourceTree* source_tree, |
89 | DescriptorDatabase* fallback_database); |
90 | ~SourceTreeDescriptorDatabase() override; |
91 | |
92 | // Instructs the SourceTreeDescriptorDatabase to report any parse errors |
93 | // to the given MultiFileErrorCollector. This should be called before |
94 | // parsing. error_collector must remain valid until either this method |
95 | // is called again or the SourceTreeDescriptorDatabase is destroyed. |
96 | void RecordErrorsTo(MultiFileErrorCollector* error_collector) { |
97 | error_collector_ = error_collector; |
98 | } |
99 | |
100 | // Gets a DescriptorPool::ErrorCollector which records errors to the |
101 | // MultiFileErrorCollector specified with RecordErrorsTo(). This collector |
102 | // has the ability to determine exact line and column numbers of errors |
103 | // from the information given to it by the DescriptorPool. |
104 | DescriptorPool::ErrorCollector* GetValidationErrorCollector() { |
105 | using_validation_error_collector_ = true; |
106 | return &validation_error_collector_; |
107 | } |
108 | |
109 | // implements DescriptorDatabase ----------------------------------- |
110 | bool FindFileByName(const std::string& filename, |
111 | FileDescriptorProto* output) override; |
112 | bool FindFileContainingSymbol(const std::string& symbol_name, |
113 | FileDescriptorProto* output) override; |
114 | bool FindFileContainingExtension(const std::string& containing_type, |
115 | int field_number, |
116 | FileDescriptorProto* output) override; |
117 | |
118 | private: |
119 | class SingleFileErrorCollector; |
120 | |
121 | SourceTree* source_tree_; |
122 | DescriptorDatabase* fallback_database_; |
123 | MultiFileErrorCollector* error_collector_; |
124 | |
125 | class PROTOBUF_EXPORT ValidationErrorCollector |
126 | : public DescriptorPool::ErrorCollector { |
127 | public: |
128 | ValidationErrorCollector(SourceTreeDescriptorDatabase* owner); |
129 | ~ValidationErrorCollector() override; |
130 | |
131 | // implements ErrorCollector --------------------------------------- |
132 | void AddError(const std::string& filename, const std::string& element_name, |
133 | const Message* descriptor, ErrorLocation location, |
134 | const std::string& message) override; |
135 | |
136 | void AddWarning(const std::string& filename, |
137 | const std::string& element_name, const Message* descriptor, |
138 | ErrorLocation location, |
139 | const std::string& message) override; |
140 | |
141 | private: |
142 | SourceTreeDescriptorDatabase* owner_; |
143 | }; |
144 | friend class ValidationErrorCollector; |
145 | |
146 | bool using_validation_error_collector_; |
147 | SourceLocationTable source_locations_; |
148 | ValidationErrorCollector validation_error_collector_; |
149 | }; |
150 | |
151 | // Simple interface for parsing .proto files. This wraps the process |
152 | // of opening the file, parsing it with a Parser, recursively parsing all its |
153 | // imports, and then cross-linking the results to produce a FileDescriptor. |
154 | // |
155 | // This is really just a thin wrapper around SourceTreeDescriptorDatabase. |
156 | // You may find that SourceTreeDescriptorDatabase is more flexible. |
157 | // |
158 | // TODO(kenton): I feel like this class is not well-named. |
159 | class PROTOBUF_EXPORT Importer { |
160 | public: |
161 | Importer(SourceTree* source_tree, MultiFileErrorCollector* error_collector); |
162 | ~Importer(); |
163 | |
164 | // Import the given file and build a FileDescriptor representing it. If |
165 | // the file is already in the DescriptorPool, the existing FileDescriptor |
166 | // will be returned. The FileDescriptor is property of the DescriptorPool, |
167 | // and will remain valid until it is destroyed. If any errors occur, they |
168 | // will be reported using the error collector and Import() will return NULL. |
169 | // |
170 | // A particular Importer object will only report errors for a particular |
171 | // file once. All future attempts to import the same file will return NULL |
172 | // without reporting any errors. The idea is that you might want to import |
173 | // a lot of files without seeing the same errors over and over again. If |
174 | // you want to see errors for the same files repeatedly, you can use a |
175 | // separate Importer object to import each one (but use the same |
176 | // DescriptorPool so that they can be cross-linked). |
177 | const FileDescriptor* Import(const std::string& filename); |
178 | |
179 | // The DescriptorPool in which all imported FileDescriptors and their |
180 | // contents are stored. |
181 | inline const DescriptorPool* pool() const { return &pool_; } |
182 | |
183 | void AddUnusedImportTrackFile(const std::string& file_name, |
184 | bool is_error = false); |
185 | void ClearUnusedImportTrackFiles(); |
186 | |
187 | |
188 | private: |
189 | SourceTreeDescriptorDatabase database_; |
190 | DescriptorPool pool_; |
191 | |
192 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer); |
193 | }; |
194 | |
195 | // If the importer encounters problems while trying to import the proto files, |
196 | // it reports them to a MultiFileErrorCollector. |
197 | class PROTOBUF_EXPORT MultiFileErrorCollector { |
198 | public: |
199 | inline MultiFileErrorCollector() {} |
200 | virtual ~MultiFileErrorCollector(); |
201 | |
202 | // Line and column numbers are zero-based. A line number of -1 indicates |
203 | // an error with the entire file (e.g. "not found"). |
204 | virtual void AddError(const std::string& filename, int line, int column, |
205 | const std::string& message) = 0; |
206 | |
207 | virtual void AddWarning(const std::string& /* filename */, int /* line */, |
208 | int /* column */, const std::string& /* message */) {} |
209 | |
210 | private: |
211 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector); |
212 | }; |
213 | |
214 | // Abstract interface which represents a directory tree containing proto files. |
215 | // Used by the default implementation of Importer to resolve import statements |
216 | // Most users will probably want to use the DiskSourceTree implementation, |
217 | // below. |
218 | class PROTOBUF_EXPORT SourceTree { |
219 | public: |
220 | inline SourceTree() {} |
221 | virtual ~SourceTree(); |
222 | |
223 | // Open the given file and return a stream that reads it, or NULL if not |
224 | // found. The caller takes ownership of the returned object. The filename |
225 | // must be a path relative to the root of the source tree and must not |
226 | // contain "." or ".." components. |
227 | virtual io::ZeroCopyInputStream* Open(const std::string& filename) = 0; |
228 | |
229 | // If Open() returns NULL, calling this method immediately will return an |
230 | // description of the error. |
231 | // Subclasses should implement this method and return a meaningful value for |
232 | // better error reporting. |
233 | // TODO(xiaofeng): change this to a pure virtual function. |
234 | virtual std::string GetLastErrorMessage(); |
235 | |
236 | private: |
237 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree); |
238 | }; |
239 | |
240 | // An implementation of SourceTree which loads files from locations on disk. |
241 | // Multiple mappings can be set up to map locations in the DiskSourceTree to |
242 | // locations in the physical filesystem. |
243 | class PROTOBUF_EXPORT DiskSourceTree : public SourceTree { |
244 | public: |
245 | DiskSourceTree(); |
246 | ~DiskSourceTree() override; |
247 | |
248 | // Map a path on disk to a location in the SourceTree. The path may be |
249 | // either a file or a directory. If it is a directory, the entire tree |
250 | // under it will be mapped to the given virtual location. To map a directory |
251 | // to the root of the source tree, pass an empty string for virtual_path. |
252 | // |
253 | // If multiple mapped paths apply when opening a file, they will be searched |
254 | // in order. For example, if you do: |
255 | // MapPath("bar", "foo/bar"); |
256 | // MapPath("", "baz"); |
257 | // and then you do: |
258 | // Open("bar/qux"); |
259 | // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux, |
260 | // returning the first one that opens successfully. |
261 | // |
262 | // disk_path may be an absolute path or relative to the current directory, |
263 | // just like a path you'd pass to open(). |
264 | void MapPath(const std::string& virtual_path, const std::string& disk_path); |
265 | |
266 | // Return type for DiskFileToVirtualFile(). |
267 | enum DiskFileToVirtualFileResult { |
268 | SUCCESS, |
269 | SHADOWED, |
270 | CANNOT_OPEN, |
271 | NO_MAPPING |
272 | }; |
273 | |
274 | // Given a path to a file on disk, find a virtual path mapping to that |
275 | // file. The first mapping created with MapPath() whose disk_path contains |
276 | // the filename is used. However, that virtual path may not actually be |
277 | // usable to open the given file. Possible return values are: |
278 | // * SUCCESS: The mapping was found. *virtual_file is filled in so that |
279 | // calling Open(*virtual_file) will open the file named by disk_file. |
280 | // * SHADOWED: A mapping was found, but using Open() to open this virtual |
281 | // path will end up returning some different file. This is because some |
282 | // other mapping with a higher precedence also matches this virtual path |
283 | // and maps it to a different file that exists on disk. *virtual_file |
284 | // is filled in as it would be in the SUCCESS case. *shadowing_disk_file |
285 | // is filled in with the disk path of the file which would be opened if |
286 | // you were to call Open(*virtual_file). |
287 | // * CANNOT_OPEN: The mapping was found and was not shadowed, but the |
288 | // file specified cannot be opened. When this value is returned, |
289 | // errno will indicate the reason the file cannot be opened. *virtual_file |
290 | // will be set to the virtual path as in the SUCCESS case, even though |
291 | // it is not useful. |
292 | // * NO_MAPPING: Indicates that no mapping was found which contains this |
293 | // file. |
294 | DiskFileToVirtualFileResult DiskFileToVirtualFile( |
295 | const std::string& disk_file, std::string* virtual_file, |
296 | std::string* shadowing_disk_file); |
297 | |
298 | // Given a virtual path, find the path to the file on disk. |
299 | // Return true and update disk_file with the on-disk path if the file exists. |
300 | // Return false and leave disk_file untouched if the file doesn't exist. |
301 | bool VirtualFileToDiskFile(const std::string& virtual_file, |
302 | std::string* disk_file); |
303 | |
304 | // implements SourceTree ------------------------------------------- |
305 | io::ZeroCopyInputStream* Open(const std::string& filename) override; |
306 | |
307 | std::string GetLastErrorMessage() override; |
308 | |
309 | private: |
310 | struct Mapping { |
311 | std::string virtual_path; |
312 | std::string disk_path; |
313 | |
314 | inline Mapping(const std::string& virtual_path_param, |
315 | const std::string& disk_path_param) |
316 | : virtual_path(virtual_path_param), disk_path(disk_path_param) {} |
317 | }; |
318 | std::vector<Mapping> mappings_; |
319 | std::string last_error_message_; |
320 | |
321 | // Like Open(), but returns the on-disk path in disk_file if disk_file is |
322 | // non-NULL and the file could be successfully opened. |
323 | io::ZeroCopyInputStream* OpenVirtualFile(const std::string& virtual_file, |
324 | std::string* disk_file); |
325 | |
326 | // Like Open() but given the actual on-disk path. |
327 | io::ZeroCopyInputStream* OpenDiskFile(const std::string& filename); |
328 | |
329 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree); |
330 | }; |
331 | |
332 | } // namespace compiler |
333 | } // namespace protobuf |
334 | } // namespace google |
335 | |
336 | #include <google/protobuf/port_undef.inc> |
337 | |
338 | #endif // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ |
339 | |