// 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.

#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>

#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>

// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
// error cases, so it seems to be ok to use as a back door for errors.

namespace google {
namespace protobuf {
namespace compiler {
namespace objectivec {

namespace {

hash_set<string> gClassWhitelist;

// islower()/isupper()/tolower()/toupper() change based on locale.
//
// src/google/protobuf/stubs/strutil.h:150 has the same pattern. For the
// Objective C plugin, test failures were seen on TravisCI because isupper('A')
// was coming back false for some server's locale.  This approach avoids any
// such issues.

bool IsLower(const char c) {
  return ('a' <= c && c <= 'z');
}

bool IsUpper(const char c) {
  return ('A' <= c && c <= 'Z');
}

char ToLower(char c) {
  if ('A' <= c && c <= 'Z') {
    c += 'a' - 'A';
  }
  return c;
}

// toupper() changes based on locale.  We don't want this!
char ToUpper(char c) {
  if ('a' <= c && c <= 'z') {
    c += 'A' - 'a';
  }
  return c;
}

string TrimString(const string& s) {
  string::size_type start = s.find_first_not_of(" \n\r\t");
  if (start == string::npos) {
    return "";
  }
  string::size_type end = s.find_last_not_of(" \n\r\t") + 1;
  return s.substr(start, end - start);
}

hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
  hash_set<string> result;
  for (int i = 0; i < num_words; i++) {
    result.insert(words[i]);
  }
  return result;
}

const char* const kUpperSegmentsList[] = {"url", "http", "https"};

hash_set<string> kUpperSegments =
    MakeWordsMap(kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList));

// Internal helper for name handing.
// Do not expose this outside of helpers, stick to having functions for specific
// cases (ClassName(), FieldName()), so there is always consistent suffix rules.
string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
  vector<string> values;
  string current;

  bool last_char_was_number = false;
  bool last_char_was_lower = false;
  bool last_char_was_upper = false;
  for (int i = 0; i < input.size(); i++) {
    char c = input[i];
    if (c >= '0' && c <= '9') {
      if (!last_char_was_number) {
        values.push_back(current);
        current = "";
      }
      current += c;
      last_char_was_number = last_char_was_lower = last_char_was_upper = false;
      last_char_was_number = true;
    } else if (IsLower(c)) {
      // lowercase letter can follow a lowercase or uppercase letter
      if (!last_char_was_lower && !last_char_was_upper) {
        values.push_back(current);
        current = "";
      }
      current += c;  // already lower
      last_char_was_number = last_char_was_lower = last_char_was_upper = false;
      last_char_was_lower = true;
    } else if (IsUpper(c)) {
      if (!last_char_was_upper) {
        values.push_back(current);
        current = "";
      }
      current += ToLower(c);
      last_char_was_number = last_char_was_lower = last_char_was_upper = false;
      last_char_was_upper = true;
    } else {
      last_char_was_number = last_char_was_lower = last_char_was_upper = false;
    }
  }
  values.push_back(current);

  for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
    string value = *i;
    bool all_upper = (kUpperSegments.count(value) > 0);
    for (int j = 0; j < value.length(); j++) {
      if (j == 0 || all_upper) {
        value[j] = ToUpper(value[j]);
      } else {
        // Nothing, already in lower.
      }
    }
    *i = value;
  }
  string result;
  for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
    result += *i;
  }
  if ((result.length() != 0) && !first_capitalized) {
    result[0] = ToLower(result[0]);
  }
  return result;
}

const char* const kReservedWordList[] = {
    // Objective C "keywords" that aren't in C
    // From
    // http://stackoverflow.com/questions/1873630/reserved-keywords-in-objective-c
    "id", "_cmd", "super", "in", "out", "inout", "bycopy", "byref", "oneway",
    "self",

    // C/C++ keywords (Incl C++ 0x11)
    // From http://en.cppreference.com/w/cpp/keywords
    "and", "and_eq", "alignas", "alignof", "asm", "auto", "bitand", "bitor",
    "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
    "compl", "const", "constexpr", "const_cast", "continue", "decltype",
    "default", "delete", "double", "dynamic_cast", "else", "enum", "explicit",
    "export", "extern ", "false", "float", "for", "friend", "goto", "if",
    "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not",
    "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected",
    "public", "register", "reinterpret_cast", "return", "short", "signed",
    "sizeof", "static", "static_assert", "static_cast", "struct", "switch",
    "template", "this", "thread_local", "throw", "true", "try", "typedef",
    "typeid", "typename", "union", "unsigned", "using", "virtual", "void",
    "volatile", "wchar_t", "while", "xor", "xor_eq",

    // C99 keywords
    // From
    // http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fkeyw.htm
    "restrict",

    // Objective-C Runtime typedefs
    // From <obc/runtime.h>
    "Category", "Ivar", "Method", "Protocol",

    // NSObject Methods
    // new is covered by C++ keywords.
    "description", "debugDescription", "finalize", "hash", "dealloc", "init",
    "class", "superclass", "retain", "release", "autorelease", "retainCount",
    "zone", "isProxy", "copy", "mutableCopy", "classForCoder",

    // GPBMessage Methods
    // Only need to add instance methods that may conflict with
    // method declared in protos. The main cases are methods
    // that take no arguments, or setFoo:/hasFoo: type methods.
    "clear", "data", "delimitedData", "descriptor", "extensionRegistry",
    "extensionsCurrentlySet", "isInitialized", "serializedSize",
    "sortedExtensionsInUse", "unknownFields",

    // MacTypes.h names
    "Fixed", "Fract", "Size", "LogicalAddress", "PhysicalAddress", "ByteCount",
    "ByteOffset", "Duration", "AbsoluteTime", "OptionBits", "ItemCount",
    "PBVersion", "ScriptCode", "LangCode", "RegionCode", "OSType",
    "ProcessSerialNumber", "Point", "Rect", "FixedPoint", "FixedRect", "Style",
    "StyleParameter", "StyleField", "TimeScale", "TimeBase", "TimeRecord",
};

hash_set<string> kReservedWords =
    MakeWordsMap(kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList));

string SanitizeNameForObjC(const string& input, const string& extension) {
  if (kReservedWords.count(input) > 0) {
    return input + extension;
  }
  return input;
}

string NameFromFieldDescriptor(const FieldDescriptor* field) {
  if (field->type() == FieldDescriptor::TYPE_GROUP) {
    return field->message_type()->name();
  } else {
    return field->name();
  }
}

// Escape C++ trigraphs by escaping question marks to \?
string EscapeTrigraphs(const string& to_escape) {
  return StringReplace(to_escape, "?", "\\?", true);
}

void PathSplit(const string& path, string* directory, string* basename) {
  string::size_type last_slash = path.rfind('/');
  if (last_slash == string::npos) {
    if (directory) {
      *directory = "";
    }
    if (basename) {
      *basename = path;
    }
  } else {
    if (directory) {
      *directory = path.substr(0, last_slash);
    }
    if (basename) {
      *basename = path.substr(last_slash + 1);
    }
  }
}

bool IsSpecialName(const string& name, const string* special_names,
                   size_t count) {
  for (size_t i = 0; i < count; ++i) {
    size_t length = special_names[i].length();
    if (name.compare(0, length, special_names[i]) == 0) {
      if (name.length() > length) {
        // If name is longer than the retained_name[i] that it matches
        // the next character must be not lower case (newton vs newTon vs
        // new_ton).
        return !IsLower(name[length]);
      } else {
        return true;
      }
    }
  }
  return false;
}

}  // namespace

string StripProto(const string& filename) {
  if (HasSuffixString(filename, ".protodevel")) {
    return StripSuffixString(filename, ".protodevel");
  } else {
    return StripSuffixString(filename, ".proto");
  }
}

bool IsRetainedName(const string& name) {
  // List of prefixes from
  // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
  static const string retained_names[] = {"new", "alloc", "copy",
                                          "mutableCopy"};
  return IsSpecialName(name, retained_names,
                       sizeof(retained_names) / sizeof(retained_names[0]));
}

bool IsInitName(const string& name) {
  static const string init_names[] = {"init"};
  return IsSpecialName(name, init_names,
                       sizeof(init_names) / sizeof(init_names[0]));
}

string BaseFileName(const FileDescriptor* file) {
  string basename;
  PathSplit(file->name(), NULL, &basename);
  return basename;
}

string FileName(const FileDescriptor* file) {
  string path = FilePath(file);
  string basename;
  PathSplit(path, NULL, &basename);
  return basename;
}

string FilePath(const FileDescriptor* file) {
  string output;
  string basename;
  string directory;
  PathSplit(file->name(), &directory, &basename);
  if (directory.length() > 0) {
    output = directory + "/";
  }
  basename = StripProto(basename);

  // CamelCase to be more ObjC friendly.
  basename = UnderscoresToCamelCase(basename, true);

  output += basename;
  return output;
}

string FileClassPrefix(const FileDescriptor* file) {
  // Default is empty string, no need to check has_objc_class_prefix.
  string result = file->options().objc_class_prefix();
  return result;
}

void ValidateObjCClassPrefix(const FileDescriptor* file) {
  string prefix = file->options().objc_class_prefix();
  if (prefix.length() > 0) {
    // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
    // error cases, so it seems to be ok to use as a back door for errors.
    if (!IsUpper(prefix[0])) {
      cerr << endl
           << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
           << prefix << "\";' in '" << file->name() << "';"
           << " it should start with a capital letter."
           << endl;
      cerr.flush();
    }
    if (prefix.length() < 3) {
      cerr << endl
           << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
           << prefix << "\";' in '" << file->name() << "';"
           << " Apple recommends they should be at least 3 characters long."
           << endl;
      cerr.flush();
    }
  }
}

string FileClassName(const FileDescriptor* file) {
  string name = FileClassPrefix(file);
  name += UnderscoresToCamelCase(StripProto(BaseFileName(file)), true);
  name += "Root";
  // There aren't really any reserved words that end in "Root", but playing
  // it safe and checking.
  return SanitizeNameForObjC(name, "_RootClass");
}

string ClassNameWorker(const Descriptor* descriptor) {
  string name;
  if (descriptor->containing_type() != NULL) {
    name = ClassNameWorker(descriptor->containing_type());
    name += "_";
  }
  return name + descriptor->name();
}

string ClassNameWorker(const EnumDescriptor* descriptor) {
  string name;
  if (descriptor->containing_type() != NULL) {
    name = ClassNameWorker(descriptor->containing_type());
    name += "_";
  }
  return name + descriptor->name();
}

string ClassName(const Descriptor* descriptor) {
  // 1. Message names are used as is (style calls for CamelCase, trust it).
  // 2. Check for reserved word at the very end and then suffix things.
  string prefix = FileClassPrefix(descriptor->file());
  string name = ClassNameWorker(descriptor);
  return SanitizeNameForObjC(prefix + name, "_Class");
}

string EnumName(const EnumDescriptor* descriptor) {
  // 1. Enum names are used as is (style calls for CamelCase, trust it).
  // 2. Check for reserved word at the every end and then suffix things.
  //      message Fixed {
  //        message Size {...}
  //        enum Mumble {...}
  //      ...
  //      }
  //    yields Fixed_Class, Fixed_Size.
  string name = FileClassPrefix(descriptor->file());
  name += ClassNameWorker(descriptor);
  return SanitizeNameForObjC(name, "_Enum");
}

string EnumValueName(const EnumValueDescriptor* descriptor) {
  // Because of the Switch enum compatibility, the name on the enum has to have
  // the suffix handing, so it slightly diverges from how nested classes work.
  //   enum Fixed {
  //     FOO = 1
  //   }
  // yields Fixed_Enum and Fixed_Enum_Foo (not Fixed_Foo).
  const string& class_name = EnumName(descriptor->type());
  const string& value_str = UnderscoresToCamelCase(descriptor->name(), true);
  const string& name = class_name + "_" + value_str;
  // There aren't really any reserved words with an underscore and a leading
  // capital letter, but playing it safe and checking.
  return SanitizeNameForObjC(name, "_Value");
}

string EnumValueShortName(const EnumValueDescriptor* descriptor) {
  // Enum value names (EnumValueName above) are the enum name turned into
  // a class name and then the value name is CamelCased and concatenated; the
  // whole thing then gets sanitized for reserved words.
  // The "short name" is intended to be the final leaf, the value name; but
  // you can't simply send that off to sanitize as that could result in it
  // getting modified when the full name didn't.  For example enum
  // "StorageModes" has a value "retain".  So the full name is
  // "StorageModes_Retain", but if we sanitize "retain" it would become
  // "RetainValue".
  // So the right way to get the short name is to take the full enum name
  // and then strip off the enum name (leaving the value name and anything
  // done by sanitize).
  const string& class_name = EnumName(descriptor->type());
  const string& long_name_prefix = class_name + "_";
  const string& long_name = EnumValueName(descriptor);
  return StripPrefixString(long_name, long_name_prefix);
}

string UnCamelCaseEnumShortName(const string& name) {
  string result;
  for (int i = 0; i < name.size(); i++) {
    char c = name[i];
    if (i > 0 && c >= 'A' && c <= 'Z') {
      result += '_';
    }
    result += ToUpper(c);
  }
  return result;
}

string ExtensionMethodName(const FieldDescriptor* descriptor) {
  const string& name = NameFromFieldDescriptor(descriptor);
  const string& result = UnderscoresToCamelCase(name, false);
  return SanitizeNameForObjC(result, "_Extension");
}

string FieldName(const FieldDescriptor* field) {
  const string& name = NameFromFieldDescriptor(field);
  string result = UnderscoresToCamelCase(name, false);
  if (field->is_repeated() && !field->is_map()) {
    // Add "Array" before do check for reserved worlds.
    result += "Array";
  } else {
    // If it wasn't repeated, but ends in "Array", force on the _p suffix.
    if (HasSuffixString(result, "Array")) {
      result += "_p";
    }
  }
  return SanitizeNameForObjC(result, "_p");
}

string FieldNameCapitalized(const FieldDescriptor* field) {
  // Want the same suffix handling, so upcase the first letter of the other
  // name.
  string result = FieldName(field);
  if (result.length() > 0) {
    result[0] = ToUpper(result[0]);
  }
  return result;
}

string OneofEnumName(const OneofDescriptor* descriptor) {
  const Descriptor* fieldDescriptor = descriptor->containing_type();
  string name = ClassName(fieldDescriptor);
  name += "_" + UnderscoresToCamelCase(descriptor->name(), true) + "_OneOfCase";
  // No sanitize needed because the OS never has names that end in _OneOfCase.
  return name;
}

string OneofName(const OneofDescriptor* descriptor) {
  string name = UnderscoresToCamelCase(descriptor->name(), false);
  // No sanitize needed because it gets OneOfCase added and that shouldn't
  // ever conflict.
  return name;
}

string OneofNameCapitalized(const OneofDescriptor* descriptor) {
  // Use the common handling and then up-case the first letter.
  string result = OneofName(descriptor);
  if (result.length() > 0) {
    result[0] = ToUpper(result[0]);
  }
  return result;
}

string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) {
  string worker(name);
  if (HasSuffixString(worker, "_p")) {
    worker = StripSuffixString(worker, "_p");
  }
  if (field->is_repeated() && HasSuffixString(worker, "Array")) {
    worker = StripSuffixString(worker, "Array");
  }
  if (field->type() == FieldDescriptor::TYPE_GROUP) {
    if (worker.length() > 0) {
      if (worker[0] >= 'a' && worker[0] <= 'z') {
        worker[0] = ToUpper(worker[0]);
      }
    }
    return worker;
  } else {
    string result;
    for (int i = 0; i < worker.size(); i++) {
      char c = worker[i];
      if (c >= 'A' && c <= 'Z') {
        if (i > 0) {
          result += '_';
        }
        result += ToLower(c);
      } else {
        result += c;
      }
    }
    return result;
  }
}

string GetCapitalizedType(const FieldDescriptor* field) {
  switch (field->type()) {
    case FieldDescriptor::TYPE_INT32:
      return "Int32";
    case FieldDescriptor::TYPE_UINT32:
      return "UInt32";
    case FieldDescriptor::TYPE_SINT32:
      return "SInt32";
    case FieldDescriptor::TYPE_FIXED32:
      return "Fixed32";
    case FieldDescriptor::TYPE_SFIXED32:
      return "SFixed32";
    case FieldDescriptor::TYPE_INT64:
      return "Int64";
    case FieldDescriptor::TYPE_UINT64:
      return "UInt64";
    case FieldDescriptor::TYPE_SINT64:
      return "SInt64";
    case FieldDescriptor::TYPE_FIXED64:
      return "Fixed64";
    case FieldDescriptor::TYPE_SFIXED64:
      return "SFixed64";
    case FieldDescriptor::TYPE_FLOAT:
      return "Float";
    case FieldDescriptor::TYPE_DOUBLE:
      return "Double";
    case FieldDescriptor::TYPE_BOOL:
      return "Bool";
    case FieldDescriptor::TYPE_STRING:
      return "String";
    case FieldDescriptor::TYPE_BYTES:
      return "Data";
    case FieldDescriptor::TYPE_ENUM:
      return "Enum";
    case FieldDescriptor::TYPE_GROUP:
      return "Group";
    case FieldDescriptor::TYPE_MESSAGE:
      return "Message";
  }

  // Some compilers report reaching end of function even though all cases of
  // the enum are handed in the switch.
  GOOGLE_LOG(FATAL) << "Can't get here.";
  return NULL;
}

ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type) {
  switch (field_type) {
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_SFIXED32:
      return OBJECTIVECTYPE_INT32;

    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_FIXED32:
      return OBJECTIVECTYPE_UINT32;

    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_SFIXED64:
      return OBJECTIVECTYPE_INT64;

    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_FIXED64:
      return OBJECTIVECTYPE_UINT64;

    case FieldDescriptor::TYPE_FLOAT:
      return OBJECTIVECTYPE_FLOAT;

    case FieldDescriptor::TYPE_DOUBLE:
      return OBJECTIVECTYPE_DOUBLE;

    case FieldDescriptor::TYPE_BOOL:
      return OBJECTIVECTYPE_BOOLEAN;

    case FieldDescriptor::TYPE_STRING:
      return OBJECTIVECTYPE_STRING;

    case FieldDescriptor::TYPE_BYTES:
      return OBJECTIVECTYPE_DATA;

    case FieldDescriptor::TYPE_ENUM:
      return OBJECTIVECTYPE_ENUM;

    case FieldDescriptor::TYPE_GROUP:
    case FieldDescriptor::TYPE_MESSAGE:
      return OBJECTIVECTYPE_MESSAGE;
  }

  // Some compilers report reaching end of function even though all cases of
  // the enum are handed in the switch.
  GOOGLE_LOG(FATAL) << "Can't get here.";
  return OBJECTIVECTYPE_INT32;
}

bool IsPrimitiveType(const FieldDescriptor* field) {
  ObjectiveCType type = GetObjectiveCType(field);
  switch (type) {
    case OBJECTIVECTYPE_INT32:
    case OBJECTIVECTYPE_UINT32:
    case OBJECTIVECTYPE_INT64:
    case OBJECTIVECTYPE_UINT64:
    case OBJECTIVECTYPE_FLOAT:
    case OBJECTIVECTYPE_DOUBLE:
    case OBJECTIVECTYPE_BOOLEAN:
    case OBJECTIVECTYPE_ENUM:
      return true;
      break;
    default:
      return false;
  }
}

bool IsReferenceType(const FieldDescriptor* field) {
  return !IsPrimitiveType(field);
}

static string HandleExtremeFloatingPoint(string val, bool add_float_suffix) {
  if (val == "nan") {
    return "NAN";
  } else if (val == "inf") {
    return "INFINITY";
  } else if (val == "-inf") {
    return "-INFINITY";
  } else {
    // float strings with ., e or E need to have f appended
    if (add_float_suffix &&
        (val.find(".") != string::npos || val.find("e") != string::npos ||
         val.find("E") != string::npos)) {
      val += "f";
    }
    return val;
  }
}

string GPBValueFieldName(const FieldDescriptor* field) {
  // Returns the field within the GPBValue union to use for the given field.
  if (field->is_repeated()) {
      return "valueMessage";
  }
  switch (field->cpp_type()) {
    case FieldDescriptor::CPPTYPE_INT32:
      return "valueInt32";
    case FieldDescriptor::CPPTYPE_UINT32:
      return "valueUInt32";
    case FieldDescriptor::CPPTYPE_INT64:
      return "valueInt64";
    case FieldDescriptor::CPPTYPE_UINT64:
      return "valueUInt64";
    case FieldDescriptor::CPPTYPE_FLOAT:
      return "valueFloat";
    case FieldDescriptor::CPPTYPE_DOUBLE:
      return "valueDouble";
    case FieldDescriptor::CPPTYPE_BOOL:
      return "valueBool";
    case FieldDescriptor::CPPTYPE_STRING:
      if (field->type() == FieldDescriptor::TYPE_BYTES) {
        return "valueData";
      } else {
        return "valueString";
      }
    case FieldDescriptor::CPPTYPE_ENUM:
      return "valueEnum";
    case FieldDescriptor::CPPTYPE_MESSAGE:
      return "valueMessage";
  }

  // Some compilers report reaching end of function even though all cases of
  // the enum are handed in the switch.
  GOOGLE_LOG(FATAL) << "Can't get here.";
  return NULL;
}


string DefaultValue(const FieldDescriptor* field) {
  // Repeated fields don't have defaults.
  if (field->is_repeated()) {
    return "nil";
  }

  // Switch on cpp_type since we need to know which default_value_* method
  // of FieldDescriptor to call.
  switch (field->cpp_type()) {
    case FieldDescriptor::CPPTYPE_INT32:
      // gcc and llvm reject the decimal form of kint32min and kint64min.
      if (field->default_value_int32() == INT_MIN) {
        return "-0x80000000";
      }
      return SimpleItoa(field->default_value_int32());
    case FieldDescriptor::CPPTYPE_UINT32:
      return SimpleItoa(field->default_value_uint32()) + "U";
    case FieldDescriptor::CPPTYPE_INT64:
      // gcc and llvm reject the decimal form of kint32min and kint64min.
      if (field->default_value_int64() == LLONG_MIN) {
        return "-0x8000000000000000LL";
      }
      return SimpleItoa(field->default_value_int64()) + "LL";
    case FieldDescriptor::CPPTYPE_UINT64:
      return SimpleItoa(field->default_value_uint64()) + "ULL";
    case FieldDescriptor::CPPTYPE_DOUBLE:
      return HandleExtremeFloatingPoint(
          SimpleDtoa(field->default_value_double()), false);
    case FieldDescriptor::CPPTYPE_FLOAT:
      return HandleExtremeFloatingPoint(
          SimpleFtoa(field->default_value_float()), true);
    case FieldDescriptor::CPPTYPE_BOOL:
      return field->default_value_bool() ? "YES" : "NO";
    case FieldDescriptor::CPPTYPE_STRING: {
      const bool has_default_value = field->has_default_value();
      const string& default_string = field->default_value_string();
      if (!has_default_value || default_string.length() == 0) {
        // If the field is defined as being the empty string,
        // then we will just assign to nil, as the empty string is the
        // default for both strings and data.
        return "nil";
      }
      if (field->type() == FieldDescriptor::TYPE_BYTES) {
        // We want constant fields in our data structures so we can
        // declare them as static. To achieve this we cheat and stuff
        // a escaped c string (prefixed with a length) into the data
        // field, and cast it to an (NSData*) so it will compile.
        // The runtime library knows how to handle it.

        // Must convert to a standard byte order for packing length into
        // a cstring.
        uint32 length = ghtonl(default_string.length());
        string bytes((const char*)&length, sizeof(length));
        bytes.append(default_string);
        return "(NSData*)\"" + CEscape(bytes) + "\"";
      } else {
        return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\"";
      }
    }
    case FieldDescriptor::CPPTYPE_ENUM:
      return EnumValueName(field->default_value_enum());
    case FieldDescriptor::CPPTYPE_MESSAGE:
      return "nil";
  }

  // Some compilers report reaching end of function even though all cases of
  // the enum are handed in the switch.
  GOOGLE_LOG(FATAL) << "Can't get here.";
  return NULL;
}

string BuildFlagsString(const vector<string>& strings) {
  if (strings.size() == 0) {
    return "0";
  }
  string string;
  for (size_t i = 0; i != strings.size(); ++i) {
    if (i > 0) {
      string.append(" | ");
    }
    string.append(strings[i]);
  }
  return string;
}

string BuildCommentsString(const SourceLocation& location) {
  const string& comments = location.leading_comments.empty()
                               ? location.trailing_comments
                               : location.leading_comments;
  vector<string> lines;
  SplitStringAllowEmpty(comments, "\n", &lines);
  while (!lines.empty() && lines.back().empty()) {
    lines.pop_back();
  }
  string prefix("//");
  string suffix("\n");
  string final_comments;
  for (int i = 0; i < lines.size(); i++) {
    // We use $ for delimiters, so replace comments with dollars with
    // html escaped version.
    // None of the other compilers handle this (as of this writing) but we
    // ran into it once, so just to be safe.
    final_comments +=
        prefix + StringReplace(lines[i], "$", "&#36;", true) + suffix;
  }
  return final_comments;
}

bool InitializeClassWhitelist(string* error) {
  const char* env_var_value = getenv("GPB_OBJC_CLASS_WHITELIST_PATHS");
  if (env_var_value == NULL) {
    return true;
  }

  // The values are joined with ';' in case we ever want to make this a
  // generator parameter also (instead of env var), and generator parameter
  // parsing already has meaning for ',' and ':'.
  vector<string> file_paths = Split(env_var_value, ";", true);

  for (vector<string>::const_iterator i = file_paths.begin();
       i != file_paths.end(); ++i) {
    const string& file_path = *i;

    ifstream stream(file_path.c_str(), ifstream::in);
    if (!stream.good()) {
      if (error != NULL) {
        stringstream err_stream;
        err_stream << endl << file_path << ":0:0: error: Unable to open";
        *error = err_stream.str();
        return false;
      }
    }

    string input_line;
    while (stream.good()) {
      getline(stream, input_line);
      string trimmed_line(TrimString(input_line));
      if (trimmed_line.length() == 0) {
        // Skip empty lines
        continue;
      }
      if (trimmed_line[0] == '/' || trimmed_line[0] == '#') {
        // Skip comments and potential preprocessor symbols
        continue;
      }
      gClassWhitelist.insert(trimmed_line);
    }
  }
  return true;
}

bool FilterClass(const string& name) {
  if (gClassWhitelist.count(name) > 0) {
    // Whitelisted, don't filter.
    return false;
  }

  // If there was no list, default to everything in.
  // If there was a list, default to everything out.
  return gClassWhitelist.size() > 0;
}

void TextFormatDecodeData::AddString(int32 key,
                                     const string& input_for_decode,
                                     const string& desired_output) {
  for (vector<DataEntry>::const_iterator i = entries_.begin();
       i != entries_.end(); ++i) {
    if (i->first == key) {
      cerr << "error: duplicate key (" << key
           << ") making TextFormat data, input: \"" << input_for_decode
           << "\", desired: \"" << desired_output << "\"." << endl;
      cerr.flush();
      abort();
    }
  }

  const string& data = TextFormatDecodeData::DecodeDataForString(
      input_for_decode, desired_output);
  entries_.push_back(DataEntry(key, data));
}

string TextFormatDecodeData::Data() const {
  ostringstream data_stringstream;

  if (num_entries() > 0) {
    io::OstreamOutputStream data_outputstream(&data_stringstream);
    io::CodedOutputStream output_stream(&data_outputstream);

    output_stream.WriteVarint32(num_entries());
    for (vector<DataEntry>::const_iterator i = entries_.begin();
         i != entries_.end(); ++i) {
      output_stream.WriteVarint32(i->first);
      output_stream.WriteString(i->second);
    }
  }

  data_stringstream.flush();
  return data_stringstream.str();
}

namespace {

// Helper to build up the decode data for a string.
class DecodeDataBuilder {
 public:
  DecodeDataBuilder() { Reset(); }

  bool AddCharacter(const char desired, const char input);
  void AddUnderscore() {
    Push();
    need_underscore_ = true;
  }
  string Finish() {
    Push();
    return decode_data_;
  }

 private:
  static const uint8 kAddUnderscore = 0x80;

  static const uint8 kOpAsIs        = 0x00;
  static const uint8 kOpFirstUpper  = 0x40;
  static const uint8 kOpFirstLower  = 0x20;
  static const uint8 kOpAllUpper    = 0x60;

  static const int kMaxSegmentLen     = 0x1f;

  void AddChar(const char desired) {
    ++segment_len_;
    is_all_upper_ &= IsUpper(desired);
  }

  void Push() {
    uint8 op = (op_ | segment_len_);
    if (need_underscore_) op |= kAddUnderscore;
    if (op != 0) {
      decode_data_ += (char)op;
    }
    Reset();
  }

  bool AddFirst(const char desired, const char input) {
    if (desired == input) {
      op_ = kOpAsIs;
    } else if (desired == ToUpper(input)) {
      op_ = kOpFirstUpper;
    } else if (desired == ToLower(input)) {
      op_ = kOpFirstLower;
    } else {
      // Can't be transformed to match.
      return false;
    }
    AddChar(desired);
    return true;
  }

  void Reset() {
    need_underscore_ = false;
    op_ = 0;
    segment_len_ = 0;
    is_all_upper_ = true;
  }

  bool need_underscore_;
  bool is_all_upper_;
  uint8 op_;
  int segment_len_;

  string decode_data_;
};

bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
  // If we've hit the max size, push to start a new segment.
  if (segment_len_ == kMaxSegmentLen) {
    Push();
  }
  if (segment_len_ == 0) {
    return AddFirst(desired, input);
  }

  // Desired and input match...
  if (desired == input) {
    // If we aren't transforming it, or we're upper casing it and it is
    // supposed to be uppercase; just add it to the segment.
    if ((op_ != kOpAllUpper) || IsUpper(desired)) {
      AddChar(desired);
      return true;
    }

    // Add the current segment, and start the next one.
    Push();
    return AddFirst(desired, input);
  }

  // If we need to uppercase, and everything so far has been uppercase,
  // promote op to AllUpper.
  if ((desired == ToUpper(input)) && is_all_upper_) {
    op_ = kOpAllUpper;
    AddChar(desired);
    return true;
  }

  // Give up, push and start a new segment.
  Push();
  return AddFirst(desired, input);
}

// If decode data can't be generated, a directive for the raw string
// is used instead.
string DirectDecodeString(const string& str) {
  string result;
  result += (char)'\0';  // Marker for full string.
  result += str;
  result += (char)'\0';  // End of string.
  return result;
}

}  // namespace

// static
string TextFormatDecodeData::DecodeDataForString(const string& input_for_decode,
                                                 const string& desired_output) {
  if ((input_for_decode.size() == 0) || (desired_output.size() == 0)) {
    cerr << "error: got empty string for making TextFormat data, input: \""
         << input_for_decode << "\", desired: \"" << desired_output << "\"."
         << endl;
    cerr.flush();
    abort();
  }
  if ((input_for_decode.find('\0') != string::npos) ||
      (desired_output.find('\0') != string::npos)) {
    cerr << "error: got a null char in a string for making TextFormat data,"
         << " input: \"" << CEscape(input_for_decode) << "\", desired: \""
         << CEscape(desired_output) << "\"." << endl;
    cerr.flush();
    abort();
  }

  DecodeDataBuilder builder;

  // Walk the output building it from the input.
  int x = 0;
  for (int y = 0; y < desired_output.size(); y++) {
    const char d = desired_output[y];
    if (d == '_') {
      builder.AddUnderscore();
      continue;
    }

    if (x >= input_for_decode.size()) {
      // Out of input, no way to encode it, just return a full decode.
      return DirectDecodeString(desired_output);
    }
    if (builder.AddCharacter(d, input_for_decode[x])) {
      ++x;  // Consumed one input
    } else {
      // Couldn't transform for the next character, just return a full decode.
      return DirectDecodeString(desired_output);
    }
  }

  if (x != input_for_decode.size()) {
    // Extra input (suffix from name sanitizing?), just return a full decode.
    return DirectDecodeString(desired_output);
  }

  // Add the end marker.
  return builder.Finish() + (char)'\0';
}

}  // namespace objectivec
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
