Brian Silverman | 9c614bc | 2016-02-15 20:20:02 -0500 | [diff] [blame^] | 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 <string> |
| 41 | #include <vector> |
| 42 | #include <set> |
| 43 | #include <utility> |
| 44 | #include <google/protobuf/descriptor.h> |
| 45 | #include <google/protobuf/descriptor_database.h> |
| 46 | #include <google/protobuf/compiler/parser.h> |
| 47 | |
| 48 | namespace google { |
| 49 | namespace protobuf { |
| 50 | |
| 51 | namespace io { class ZeroCopyInputStream; } |
| 52 | |
| 53 | namespace compiler { |
| 54 | |
| 55 | // Defined in this file. |
| 56 | class Importer; |
| 57 | class MultiFileErrorCollector; |
| 58 | class SourceTree; |
| 59 | class DiskSourceTree; |
| 60 | |
| 61 | // TODO(kenton): Move all SourceTree stuff to a separate file? |
| 62 | |
| 63 | // An implementation of DescriptorDatabase which loads files from a SourceTree |
| 64 | // and parses them. |
| 65 | // |
| 66 | // Note: This class is not thread-safe since it maintains a table of source |
| 67 | // code locations for error reporting. However, when a DescriptorPool wraps |
| 68 | // a DescriptorDatabase, it uses mutex locking to make sure only one method |
| 69 | // of the database is called at a time, even if the DescriptorPool is used |
| 70 | // from multiple threads. Therefore, there is only a problem if you create |
| 71 | // multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase |
| 72 | // and use them from multiple threads. |
| 73 | // |
| 74 | // Note: This class does not implement FindFileContainingSymbol() or |
| 75 | // FindFileContainingExtension(); these will always return false. |
| 76 | class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase { |
| 77 | public: |
| 78 | SourceTreeDescriptorDatabase(SourceTree* source_tree); |
| 79 | ~SourceTreeDescriptorDatabase(); |
| 80 | |
| 81 | // Instructs the SourceTreeDescriptorDatabase to report any parse errors |
| 82 | // to the given MultiFileErrorCollector. This should be called before |
| 83 | // parsing. error_collector must remain valid until either this method |
| 84 | // is called again or the SourceTreeDescriptorDatabase is destroyed. |
| 85 | void RecordErrorsTo(MultiFileErrorCollector* error_collector) { |
| 86 | error_collector_ = error_collector; |
| 87 | } |
| 88 | |
| 89 | // Gets a DescriptorPool::ErrorCollector which records errors to the |
| 90 | // MultiFileErrorCollector specified with RecordErrorsTo(). This collector |
| 91 | // has the ability to determine exact line and column numbers of errors |
| 92 | // from the information given to it by the DescriptorPool. |
| 93 | DescriptorPool::ErrorCollector* GetValidationErrorCollector() { |
| 94 | using_validation_error_collector_ = true; |
| 95 | return &validation_error_collector_; |
| 96 | } |
| 97 | |
| 98 | // implements DescriptorDatabase ----------------------------------- |
| 99 | bool FindFileByName(const string& filename, FileDescriptorProto* output); |
| 100 | bool FindFileContainingSymbol(const string& symbol_name, |
| 101 | FileDescriptorProto* output); |
| 102 | bool FindFileContainingExtension(const string& containing_type, |
| 103 | int field_number, |
| 104 | FileDescriptorProto* output); |
| 105 | |
| 106 | private: |
| 107 | class SingleFileErrorCollector; |
| 108 | |
| 109 | SourceTree* source_tree_; |
| 110 | MultiFileErrorCollector* error_collector_; |
| 111 | |
| 112 | class LIBPROTOBUF_EXPORT ValidationErrorCollector : public DescriptorPool::ErrorCollector { |
| 113 | public: |
| 114 | ValidationErrorCollector(SourceTreeDescriptorDatabase* owner); |
| 115 | ~ValidationErrorCollector(); |
| 116 | |
| 117 | // implements ErrorCollector --------------------------------------- |
| 118 | void AddError(const string& filename, |
| 119 | const string& element_name, |
| 120 | const Message* descriptor, |
| 121 | ErrorLocation location, |
| 122 | const string& message); |
| 123 | |
| 124 | virtual void AddWarning(const string& filename, |
| 125 | const string& element_name, |
| 126 | const Message* descriptor, |
| 127 | ErrorLocation location, |
| 128 | const string& message); |
| 129 | |
| 130 | private: |
| 131 | SourceTreeDescriptorDatabase* owner_; |
| 132 | }; |
| 133 | friend class ValidationErrorCollector; |
| 134 | |
| 135 | bool using_validation_error_collector_; |
| 136 | SourceLocationTable source_locations_; |
| 137 | ValidationErrorCollector validation_error_collector_; |
| 138 | }; |
| 139 | |
| 140 | // Simple interface for parsing .proto files. This wraps the process |
| 141 | // of opening the file, parsing it with a Parser, recursively parsing all its |
| 142 | // imports, and then cross-linking the results to produce a FileDescriptor. |
| 143 | // |
| 144 | // This is really just a thin wrapper around SourceTreeDescriptorDatabase. |
| 145 | // You may find that SourceTreeDescriptorDatabase is more flexible. |
| 146 | // |
| 147 | // TODO(kenton): I feel like this class is not well-named. |
| 148 | class LIBPROTOBUF_EXPORT Importer { |
| 149 | public: |
| 150 | Importer(SourceTree* source_tree, |
| 151 | MultiFileErrorCollector* error_collector); |
| 152 | ~Importer(); |
| 153 | |
| 154 | // Import the given file and build a FileDescriptor representing it. If |
| 155 | // the file is already in the DescriptorPool, the existing FileDescriptor |
| 156 | // will be returned. The FileDescriptor is property of the DescriptorPool, |
| 157 | // and will remain valid until it is destroyed. If any errors occur, they |
| 158 | // will be reported using the error collector and Import() will return NULL. |
| 159 | // |
| 160 | // A particular Importer object will only report errors for a particular |
| 161 | // file once. All future attempts to import the same file will return NULL |
| 162 | // without reporting any errors. The idea is that you might want to import |
| 163 | // a lot of files without seeing the same errors over and over again. If |
| 164 | // you want to see errors for the same files repeatedly, you can use a |
| 165 | // separate Importer object to import each one (but use the same |
| 166 | // DescriptorPool so that they can be cross-linked). |
| 167 | const FileDescriptor* Import(const string& filename); |
| 168 | |
| 169 | // The DescriptorPool in which all imported FileDescriptors and their |
| 170 | // contents are stored. |
| 171 | inline const DescriptorPool* pool() const { |
| 172 | return &pool_; |
| 173 | } |
| 174 | |
| 175 | void AddUnusedImportTrackFile(const string& file_name); |
| 176 | void ClearUnusedImportTrackFiles(); |
| 177 | |
| 178 | private: |
| 179 | SourceTreeDescriptorDatabase database_; |
| 180 | DescriptorPool pool_; |
| 181 | |
| 182 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer); |
| 183 | }; |
| 184 | |
| 185 | // If the importer encounters problems while trying to import the proto files, |
| 186 | // it reports them to a MultiFileErrorCollector. |
| 187 | class LIBPROTOBUF_EXPORT MultiFileErrorCollector { |
| 188 | public: |
| 189 | inline MultiFileErrorCollector() {} |
| 190 | virtual ~MultiFileErrorCollector(); |
| 191 | |
| 192 | // Line and column numbers are zero-based. A line number of -1 indicates |
| 193 | // an error with the entire file (e.g. "not found"). |
| 194 | virtual void AddError(const string& filename, int line, int column, |
| 195 | const string& message) = 0; |
| 196 | |
| 197 | virtual void AddWarning(const string& filename, int line, int column, |
| 198 | const string& message) {} |
| 199 | |
| 200 | private: |
| 201 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector); |
| 202 | }; |
| 203 | |
| 204 | // Abstract interface which represents a directory tree containing proto files. |
| 205 | // Used by the default implementation of Importer to resolve import statements |
| 206 | // Most users will probably want to use the DiskSourceTree implementation, |
| 207 | // below. |
| 208 | class LIBPROTOBUF_EXPORT SourceTree { |
| 209 | public: |
| 210 | inline SourceTree() {} |
| 211 | virtual ~SourceTree(); |
| 212 | |
| 213 | // Open the given file and return a stream that reads it, or NULL if not |
| 214 | // found. The caller takes ownership of the returned object. The filename |
| 215 | // must be a path relative to the root of the source tree and must not |
| 216 | // contain "." or ".." components. |
| 217 | virtual io::ZeroCopyInputStream* Open(const string& filename) = 0; |
| 218 | |
| 219 | // If Open() returns NULL, calling this method immediately will return an |
| 220 | // description of the error. |
| 221 | // Subclasses should implement this method and return a meaningful value for |
| 222 | // better error reporting. |
| 223 | // TODO(xiaofeng): change this to a pure virtual function. |
| 224 | virtual string GetLastErrorMessage(); |
| 225 | |
| 226 | private: |
| 227 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree); |
| 228 | }; |
| 229 | |
| 230 | // An implementation of SourceTree which loads files from locations on disk. |
| 231 | // Multiple mappings can be set up to map locations in the DiskSourceTree to |
| 232 | // locations in the physical filesystem. |
| 233 | class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { |
| 234 | public: |
| 235 | DiskSourceTree(); |
| 236 | ~DiskSourceTree(); |
| 237 | |
| 238 | // Map a path on disk to a location in the SourceTree. The path may be |
| 239 | // either a file or a directory. If it is a directory, the entire tree |
| 240 | // under it will be mapped to the given virtual location. To map a directory |
| 241 | // to the root of the source tree, pass an empty string for virtual_path. |
| 242 | // |
| 243 | // If multiple mapped paths apply when opening a file, they will be searched |
| 244 | // in order. For example, if you do: |
| 245 | // MapPath("bar", "foo/bar"); |
| 246 | // MapPath("", "baz"); |
| 247 | // and then you do: |
| 248 | // Open("bar/qux"); |
| 249 | // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux, |
| 250 | // returning the first one that opens successfuly. |
| 251 | // |
| 252 | // disk_path may be an absolute path or relative to the current directory, |
| 253 | // just like a path you'd pass to open(). |
| 254 | void MapPath(const string& virtual_path, const string& disk_path); |
| 255 | |
| 256 | // Return type for DiskFileToVirtualFile(). |
| 257 | enum DiskFileToVirtualFileResult { |
| 258 | SUCCESS, |
| 259 | SHADOWED, |
| 260 | CANNOT_OPEN, |
| 261 | NO_MAPPING |
| 262 | }; |
| 263 | |
| 264 | // Given a path to a file on disk, find a virtual path mapping to that |
| 265 | // file. The first mapping created with MapPath() whose disk_path contains |
| 266 | // the filename is used. However, that virtual path may not actually be |
| 267 | // usable to open the given file. Possible return values are: |
| 268 | // * SUCCESS: The mapping was found. *virtual_file is filled in so that |
| 269 | // calling Open(*virtual_file) will open the file named by disk_file. |
| 270 | // * SHADOWED: A mapping was found, but using Open() to open this virtual |
| 271 | // path will end up returning some different file. This is because some |
| 272 | // other mapping with a higher precedence also matches this virtual path |
| 273 | // and maps it to a different file that exists on disk. *virtual_file |
| 274 | // is filled in as it would be in the SUCCESS case. *shadowing_disk_file |
| 275 | // is filled in with the disk path of the file which would be opened if |
| 276 | // you were to call Open(*virtual_file). |
| 277 | // * CANNOT_OPEN: The mapping was found and was not shadowed, but the |
| 278 | // file specified cannot be opened. When this value is returned, |
| 279 | // errno will indicate the reason the file cannot be opened. *virtual_file |
| 280 | // will be set to the virtual path as in the SUCCESS case, even though |
| 281 | // it is not useful. |
| 282 | // * NO_MAPPING: Indicates that no mapping was found which contains this |
| 283 | // file. |
| 284 | DiskFileToVirtualFileResult |
| 285 | DiskFileToVirtualFile(const string& disk_file, |
| 286 | string* virtual_file, |
| 287 | string* shadowing_disk_file); |
| 288 | |
| 289 | // Given a virtual path, find the path to the file on disk. |
| 290 | // Return true and update disk_file with the on-disk path if the file exists. |
| 291 | // Return false and leave disk_file untouched if the file doesn't exist. |
| 292 | bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file); |
| 293 | |
| 294 | // implements SourceTree ------------------------------------------- |
| 295 | virtual io::ZeroCopyInputStream* Open(const string& filename); |
| 296 | |
| 297 | virtual string GetLastErrorMessage(); |
| 298 | |
| 299 | private: |
| 300 | struct Mapping { |
| 301 | string virtual_path; |
| 302 | string disk_path; |
| 303 | |
| 304 | inline Mapping(const string& virtual_path_param, |
| 305 | const string& disk_path_param) |
| 306 | : virtual_path(virtual_path_param), disk_path(disk_path_param) {} |
| 307 | }; |
| 308 | vector<Mapping> mappings_; |
| 309 | string last_error_message_; |
| 310 | |
| 311 | // Like Open(), but returns the on-disk path in disk_file if disk_file is |
| 312 | // non-NULL and the file could be successfully opened. |
| 313 | io::ZeroCopyInputStream* OpenVirtualFile(const string& virtual_file, |
| 314 | string* disk_file); |
| 315 | |
| 316 | // Like Open() but given the actual on-disk path. |
| 317 | io::ZeroCopyInputStream* OpenDiskFile(const string& filename); |
| 318 | |
| 319 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree); |
| 320 | }; |
| 321 | |
| 322 | } // namespace compiler |
| 323 | } // namespace protobuf |
| 324 | |
| 325 | } // namespace google |
| 326 | #endif // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ |