// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Generates JavaScript code for a given .proto file.
//
#ifndef GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__

#include <set>
#include <string>

#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/scc.h>
#include <google/protobuf/compiler/code_generator.h>

#include <google/protobuf/port_def.inc>

namespace google {
namespace protobuf {

class Descriptor;
class EnumDescriptor;
class FieldDescriptor;
class OneofDescriptor;
class FileDescriptor;

namespace io {
class Printer;
}

namespace compiler {
namespace js {

struct GeneratorOptions {
  // Output path.
  std::string output_dir;
  // Namespace prefix.
  std::string namespace_prefix;
  // Enable binary-format support?
  bool binary;
  // What style of imports should be used.
  enum ImportStyle {
    kImportClosure,         // goog.require()
    kImportCommonJs,        // require()
    kImportCommonJsStrict,  // require() with no global export
    kImportBrowser,         // no import statements
    kImportEs6,             // import { member } from ''
  } import_style;

  GeneratorOptions()
      : output_dir("."),
        namespace_prefix(""),
        binary(false),
        import_style(kImportClosure),
        add_require_for_enums(false),
        testonly(false),
        library(""),
        extension(".js"),
        one_output_file_per_input_file(false),
        annotate_code(false) {}

  bool ParseFromOptions(
      const std::vector<std::pair<std::string, std::string> >& options,
      std::string* error);

  // Returns the file name extension to use for generated code.
  std::string GetFileNameExtension() const {
    return import_style == kImportClosure ? extension : "_pb.js";
  }

  enum OutputMode {
    // Create an output file for each input .proto file.
    kOneOutputFilePerInputFile,
    // Create an output file for each type.
    kOneOutputFilePerSCC,
    // Put everything in a single file named by the library option.
    kEverythingInOneFile,
  };

  // Indicates how to output the generated code based on the provided options.
  OutputMode output_mode() const;

  // The remaining options are only relevant when we are using kImportClosure.

  // Add a `goog.requires()` call for each enum type used. If not set, a
  // forward declaration with `goog.forwardDeclare` is produced instead.
  bool add_require_for_enums;
  // Set this as a test-only module via `goog.setTestOnly();`.
  bool testonly;
  // Create a library with name <name>_lib.js rather than a separate .js file
  // per type?
  std::string library;
  // The extension to use for output file names.
  std::string extension;
  // Create a separate output file for each input file?
  bool one_output_file_per_input_file;
  // If true, we should append annotations as comments on the last line for
  // generated .js file. Annotations used by tools like https://kythe.io
  // to provide cross-references between .js and .proto files. Annotations
  // are encoded as base64 proto of GeneratedCodeInfo message (see
  // descriptor.proto).
  bool annotate_code;
};

// CodeGenerator implementation which generates a JavaScript source file and
// header.  If you create your own protocol compiler binary and you want it to
// support JavaScript output, you can do so by registering an instance of this
// CodeGenerator with the CommandLineInterface in your main() function.
class PROTOC_EXPORT Generator : public CodeGenerator {
 public:
  Generator() {}
  virtual ~Generator() {}

  bool Generate(const FileDescriptor* file, const std::string& parameter,
                GeneratorContext* context, std::string* error) const override {
    *error = "Unimplemented Generate() method. Call GenerateAll() instead.";
    return false;
  }

  bool HasGenerateAll() const override { return true; }

  bool GenerateAll(const std::vector<const FileDescriptor*>& files,
                   const std::string& parameter, GeneratorContext* context,
                   std::string* error) const override;

  uint64 GetSupportedFeatures() const override {
    return FEATURE_PROTO3_OPTIONAL;
  }

 private:
  void GenerateHeader(const GeneratorOptions& options,
                      const FileDescriptor* file, io::Printer* printer) const;

  // Generate goog.provides() calls.
  void FindProvides(const GeneratorOptions& options, io::Printer* printer,
                    const std::vector<const FileDescriptor*>& file,
                    std::set<std::string>* provided) const;
  void FindProvidesForFile(const GeneratorOptions& options,
                           io::Printer* printer, const FileDescriptor* file,
                           std::set<std::string>* provided) const;
  void FindProvidesForMessage(const GeneratorOptions& options,
                              io::Printer* printer, const Descriptor* desc,
                              std::set<std::string>* provided) const;
  void FindProvidesForEnum(const GeneratorOptions& options,
                           io::Printer* printer, const EnumDescriptor* enumdesc,
                           std::set<std::string>* provided) const;
  // For extension fields at file scope.
  void FindProvidesForFields(const GeneratorOptions& options,
                             io::Printer* printer,
                             const std::vector<const FieldDescriptor*>& fields,
                             std::set<std::string>* provided) const;
  // Print the goog.provides() found by the methods above.
  void GenerateProvides(const GeneratorOptions& options, io::Printer* printer,
                        std::set<std::string>* provided) const;

  // Generate goog.setTestOnly() if indicated.
  void GenerateTestOnly(const GeneratorOptions& options,
                        io::Printer* printer) const;

  // Generate goog.requires() calls.
  void GenerateRequiresForLibrary(
      const GeneratorOptions& options, io::Printer* printer,
      const std::vector<const FileDescriptor*>& files,
      std::set<std::string>* provided) const;
  void GenerateRequiresForSCC(const GeneratorOptions& options,
                              io::Printer* printer, const SCC* scc,
                              std::set<std::string>* provided) const;
  // For extension fields at file scope.
  void GenerateRequiresForExtensions(
      const GeneratorOptions& options, io::Printer* printer,
      const std::vector<const FieldDescriptor*>& fields,
      std::set<std::string>* provided) const;
  void GenerateRequiresImpl(const GeneratorOptions& options,
                            io::Printer* printer,
                            std::set<std::string>* required,
                            std::set<std::string>* forwards,
                            std::set<std::string>* provided, bool require_jspb,
                            bool require_extension, bool require_map) const;
  void FindRequiresForMessage(const GeneratorOptions& options,
                              const Descriptor* desc,
                              std::set<std::string>* required,
                              std::set<std::string>* forwards,
                              bool* have_message) const;
  void FindRequiresForField(const GeneratorOptions& options,
                            const FieldDescriptor* field,
                            std::set<std::string>* required,
                            std::set<std::string>* forwards) const;
  void FindRequiresForExtension(const GeneratorOptions& options,
                                const FieldDescriptor* field,
                                std::set<std::string>* required,
                                std::set<std::string>* forwards) const;
  // Generate all things in a proto file into one file.
  // If use_short_name is true, the generated file's name will only be short
  // name that without directory, otherwise filename equals file->name()
  bool GenerateFile(const FileDescriptor* file, const GeneratorOptions& options,
                    GeneratorContext* context, bool use_short_name) const;
  void GenerateFile(const GeneratorOptions& options, io::Printer* printer,
                    const FileDescriptor* file) const;

  // Generate definitions for all message classes and enums in all files,
  // processing the files in dependence order.
  void GenerateFilesInDepOrder(
      const GeneratorOptions& options, io::Printer* printer,
      const std::vector<const FileDescriptor*>& file) const;
  // Helper for above.
  void GenerateFileAndDeps(const GeneratorOptions& options,
                           io::Printer* printer, const FileDescriptor* root,
                           std::set<const FileDescriptor*>* all_files,
                           std::set<const FileDescriptor*>* generated) const;

  // Generate definitions for all message classes and enums.
  void GenerateClassesAndEnums(const GeneratorOptions& options,
                               io::Printer* printer,
                               const FileDescriptor* file) const;

  void GenerateFieldValueExpression(io::Printer* printer,
                                    const char* obj_reference,
                                    const FieldDescriptor* field,
                                    bool use_default) const;

  // Generate definition for one class.
  void GenerateClass(const GeneratorOptions& options, io::Printer* printer,
                     const Descriptor* desc) const;
  void GenerateClassConstructor(const GeneratorOptions& options,
                                io::Printer* printer,
                                const Descriptor* desc) const;
  void GenerateClassFieldInfo(const GeneratorOptions& options,
                              io::Printer* printer,
                              const Descriptor* desc) const;
  void GenerateClassConstructorAndDeclareExtensionFieldInfo(
      const GeneratorOptions& options, io::Printer* printer,
      const Descriptor* desc) const;
  void GenerateClassXid(const GeneratorOptions& options, io::Printer* printer,
                        const Descriptor* desc) const;
  void GenerateOneofCaseDefinition(const GeneratorOptions& options,
                                   io::Printer* printer,
                                   const OneofDescriptor* oneof) const;
  void GenerateObjectTypedef(const GeneratorOptions& options,
                             io::Printer* printer,
                             const Descriptor* desc) const;
  void GenerateClassToObject(const GeneratorOptions& options,
                             io::Printer* printer,
                             const Descriptor* desc) const;
  void GenerateClassFieldToObject(const GeneratorOptions& options,
                                  io::Printer* printer,
                                  const FieldDescriptor* field) const;
  void GenerateClassFromObject(const GeneratorOptions& options,
                               io::Printer* printer,
                               const Descriptor* desc) const;
  void GenerateClassFieldFromObject(const GeneratorOptions& options,
                                    io::Printer* printer,
                                    const FieldDescriptor* field) const;
  void GenerateClassRegistration(const GeneratorOptions& options,
                                 io::Printer* printer,
                                 const Descriptor* desc) const;
  void GenerateClassFields(const GeneratorOptions& options,
                           io::Printer* printer, const Descriptor* desc) const;
  void GenerateClassField(const GeneratorOptions& options, io::Printer* printer,
                          const FieldDescriptor* desc) const;
  void GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
                                       io::Printer* printer,
                                       const Descriptor* desc) const;
  void GenerateClassDeserialize(const GeneratorOptions& options,
                                io::Printer* printer,
                                const Descriptor* desc) const;
  void GenerateClassDeserializeBinary(const GeneratorOptions& options,
                                      io::Printer* printer,
                                      const Descriptor* desc) const;
  void GenerateClassDeserializeBinaryField(const GeneratorOptions& options,
                                           io::Printer* printer,
                                           const FieldDescriptor* field) const;
  void GenerateClassSerializeBinary(const GeneratorOptions& options,
                                    io::Printer* printer,
                                    const Descriptor* desc) const;
  void GenerateClassSerializeBinaryField(const GeneratorOptions& options,
                                         io::Printer* printer,
                                         const FieldDescriptor* field) const;

  // Generate definition for one enum.
  void GenerateEnum(const GeneratorOptions& options, io::Printer* printer,
                    const EnumDescriptor* enumdesc) const;

  // Generate an extension definition.
  void GenerateExtension(const GeneratorOptions& options, io::Printer* printer,
                         const FieldDescriptor* field) const;

  // Generate addFoo() method for repeated primitive fields.
  void GenerateRepeatedPrimitiveHelperMethods(const GeneratorOptions& options,
                                              io::Printer* printer,
                                              const FieldDescriptor* field,
                                              bool untyped) const;

  // Generate addFoo() method for repeated message fields.
  void GenerateRepeatedMessageHelperMethods(const GeneratorOptions& options,
                                            io::Printer* printer,
                                            const FieldDescriptor* field) const;

  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator);
};

}  // namespace js
}  // namespace compiler
}  // namespace protobuf
}  // namespace google

#include <google/protobuf/port_undef.inc>

#endif  // GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__
