loader.h 5.08 KB
Newer Older
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * ProfaneDB - A Protocol Buffers database.
 * Copyright (C) 2017  "Giorgio Azzinnaro" <giorgio.azzinnaro@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef PROFANEDB_FORMAT_PROTOBUF_LOADER_H
#define PROFANEDB_FORMAT_PROTOBUF_LOADER_H

#include <profanedb/protobuf/options.pb.h>
#include <profanedb/protobuf/storage.pb.h>

26
#include <google/protobuf/io/zero_copy_stream.h>
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
27
#include <google/protobuf/compiler/importer.h>
28 29
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
30 31

#include <boost/filesystem.hpp>
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
32
#include <boost/log/trivial.hpp>
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
33 34 35 36 37

namespace profanedb {
namespace format {
namespace protobuf {

38 39
// Loader is a utility class to populate a schemaPool and normalizedPool
// which profanedb::format::protobuf::Marshaller requires
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
40 41 42 43 44 45 46
class Loader
{
public:
    // RootSourceTree is a utility class that creates a DiskSourceTree,
    // mapping all the paths provided to the root ("/") path for easier import.
    // Paths are available for Loader to populate its Pool;
    class RootSourceTree : public google::protobuf::compiler::DiskSourceTree {
47
        friend class Loader;
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
48 49
        
    public:
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
50 51 52 53 54 55 56 57 58 59 60 61
        RootSourceTree(std::initializer_list<boost::filesystem::path> paths);
        
    private:
        std::vector<boost::filesystem::path> paths;
    };
    
    // include has the import directories
    // ("/usr/include/[google/protobuf/*.proto]", "src/[profanedb/protobuf/*.proto]")
    // schema the user proto files, with ProfaneDB options set
    Loader(
        std::unique_ptr<RootSourceTree> include,
        std::unique_ptr<RootSourceTree> schema);
62 63 64 65 66 67

    enum PoolType {
      SCHEMA,
      NORMALIZED
    };

Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
68 69 70
    // Get either Schema or Normalized pool
    const google::protobuf::DescriptorPool & GetPool(PoolType poolType) const;

71 72 73 74 75 76 77 78
    // Retrieve a Descriptor either from Schema or Normalized pool
    const google::protobuf::Descriptor * GetDescriptor(
        PoolType poolType,
        std::string typeName) const;

    // Create a prototype Message from defined pool
    // (Should use `CreateMessage()->New() to get an empty Message from the prototype)
    const google::protobuf::Message * CreateMessage(PoolType poolType, std::string typeName);
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
79 80 81 82 83
    
private:
    // Given a Protobuf FileDescriptor from the pool, parse all of its messages,
    // find the keyable messages, and return a FileDescriptorProto,
    // ready to be put in the normalizedDescriptorDb
84
    google::protobuf::FileDescriptorProto * ParseFile(
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
85 86 87
        const google::protobuf::FileDescriptor * fileDescriptor);
    
    // Parse a Descriptor and its nested messages
88
    google::protobuf::DescriptorProto * ParseAndNormalizeDescriptor(
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
89 90 91 92 93
        const google::protobuf::Descriptor * descriptor);
    
    // Check whether a Descriptor has a field with key option set
    bool IsKeyable(const google::protobuf::Descriptor * descriptor) const;
    
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    class BoostLogErrorCollector
        : public google::protobuf::DescriptorPool::ErrorCollector {
      virtual void AddError(const std::string & filename,
                            const std::string & element_name,
                            const google::protobuf::Message * descriptor,
                            google::protobuf::DescriptorPool::ErrorCollector::ErrorLocation location,
                            const std::string & message) override;

      virtual void AddWarning(const std::string & filename,
                              const std::string & element_name,
                              const google::protobuf::Message * descriptor,
                              google::protobuf::DescriptorPool::ErrorCollector::ErrorLocation location,
                              const std::string & message) override;
    };
    BoostLogErrorCollector errorCollector;

Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
110 111 112 113 114 115 116 117
    std::unique_ptr<RootSourceTree> includeSourceTree;
    std::unique_ptr<RootSourceTree> schemaSourceTree;
    
    google::protobuf::compiler::SourceTreeDescriptorDatabase includeDb;
    google::protobuf::compiler::SourceTreeDescriptorDatabase schemaDb;
    
    google::protobuf::SimpleDescriptorDatabase normalizedDescriptorDb;
    
118
    // schemaPool keeps track of the original messages
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
119
    google::protobuf::DescriptorPool schemaPool;
120 121 122
    
    // For each keyable message in schema, there is a normalized version
    // which has Key in place of nested keyable messages
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
123
    google::protobuf::DescriptorPool normalizedPool;
124 125 126

    // The message factory is used to create new messages from the pools
    google::protobuf::DynamicMessageFactory messageFactory;
Giorgio Azzinnaro's avatar
Giorgio Azzinnaro committed
127 128 129 130 131 132
};
}
}
}

#endif // PROFANEDB_FORMAT_PROTOBUF_LOADER_H