text:
// 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 <google/protobuf/compiler/php/php_generator.h>

#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>

#include <sstream>

using google::protobuf::internal::scoped_ptr;

const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
const std::string kEmptyFile = "google/protobuf/empty.proto";
const std::string kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php";
const std::string kDescriptorMetadataFile =
    "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
const std::string kDescriptorDirName = "Google/Protobuf/Internal";
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
const char* const kReservedNames[] = {"Empty", "ECHO"};
const int kReservedNamesSize = 2;

namespace google {
namespace protobuf {
namespace compiler {
namespace php {

// Forward decls.
std::string PhpName(const std::string& full_name, bool is_descriptor);
std::string DefaultForField(FieldDescriptor* field);
std::string IntToString(int32 value);
std::string FilenameToClassname(const string& filename);
std::string GeneratedMetadataFileName(const std::string& proto_file,
                                      bool is_descriptor);
std::string LabelForField(FieldDescriptor* field);
std::string TypeName(FieldDescriptor* field);
std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
std::string EscapeDollor(const string& to_escape);
std::string BinaryToHex(const string& binary);
void Indent(io::Printer* printer);
void Outdent(io::Printer* printer);
void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message);
void GenerateFieldDocComment(io::Printer* printer,
                             const FieldDescriptor* field);
void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_);
void GenerateEnumValueDocComment(io::Printer* printer,
                                 const EnumValueDescriptor* value);

std::string RenameEmpty(const std::string& name) {
  if (name == "Empty") {
    return "GPBEmpty";
  } else {
    return name;
  }
}

std::string MessageFullName(const Descriptor* message, bool is_descriptor) {
  if (is_descriptor) {
    return StringReplace(message->full_name(),
                         "google.protobuf",
                         "google.protobuf.internal", false);
  } else {
    return message->full_name();
  }
}

std::string EnumFullName(const EnumDescriptor* envm, bool is_descriptor) {
  if (is_descriptor) {
    return StringReplace(envm->full_name(),
                         "google.protobuf",
                         "google.protobuf.internal", false);
  } else {
    return envm->full_name();
  }
}

template <typename DescriptorType>
std::string ClassNamePrefix(const string& classname,
                            const DescriptorType* desc) {
  const string& prefix = (desc->file()->options()).php_class_prefix();
  if (prefix != "") {
    return prefix;
  }

  bool is_reserved = false;

  for (int i = 0; i < kReservedNamesSize; i++) {
    if (classname == kReservedNames[i]) {
      is_reserved = true;
      break;
    }
  }

  if (is_reserved) {
    if (desc->file()->package() == "google.protobuf") {
      return "GPB";
    } else {
      return "PB";
    }
  }

  return "";
}

template <typename DescriptorType>
std::string FullClassName(const DescriptorType* desc, bool is_descriptor) {
  string classname = desc->name();
  const Descriptor* containing = desc->containing_type();
  while (containing != NULL) {
    classname = containing->name() + '_' + classname;
    containing = containing->containing_type();
  }
  classname = ClassNamePrefix(classname, desc) + classname;

  if (desc->file()->package() == "") {
    return classname;
  } else {
    return PhpName(desc->file()->package(), is_descriptor) + '\\' +
           classname;
  }
}

std::string PhpName(const std::string& full_name, bool is_descriptor) {
  if (is_descriptor) {
    return kDescriptorPackageName;
  }

  std::string result;
  bool cap_next_letter = true;
  for (int i = 0; i < full_name.size(); i++) {
    if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
      result += full_name[i] + ('A' - 'a');
      cap_next_letter = false;
    } else if (full_name[i] == '.') {
      result += '\\';
      cap_next_letter = true;
    } else {
      result += full_name[i];
      cap_next_letter = false;
    }
  }
  return result;
}

std::string DefaultForField(const FieldDescriptor* field) {
  switch (field->type()) {
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_FIXED32:
    case FieldDescriptor::TYPE_FIXED64:
    case FieldDescriptor::TYPE_SFIXED32:
    case FieldDescriptor::TYPE_SFIXED64:
    case FieldDescriptor::TYPE_ENUM: return "0";
    case FieldDescriptor::TYPE_DOUBLE:
    case FieldDescriptor::TYPE_FLOAT: return "0.0";
    case FieldDescriptor::TYPE_BOOL: return "false";
    case FieldDescriptor::TYPE_STRING:
    case FieldDescriptor::TYPE_BYTES: return "''";
    case FieldDescriptor::TYPE_MESSAGE:
    case FieldDescriptor::TYPE_GROUP: return "null";
    default: assert(false); return "";
  }
}

std::string GeneratedMetadataFileName(const std::string& proto_file,
                                      bool is_descriptor) {
  int start_index = 0;
  int first_index = proto_file.find_first_of("/", start_index);
  std::string result = "GPBMetadata/";

  if (proto_file == kEmptyFile) {
    return kEmptyMetadataFile;
  }
  if (is_descriptor) {
    return kDescriptorMetadataFile;
  }

  // Append directory name.
  std::string file_no_suffix;
  int lastindex = proto_file.find_last_of(".");
  if (proto_file == kEmptyFile) {
    return kEmptyMetadataFile;
  } else {
    file_no_suffix = proto_file.substr(0, lastindex);
  }

  while (first_index != string::npos) {
    result += UnderscoresToCamelCase(
        file_no_suffix.substr(start_index, first_index - start_index), true);
    result += "/";
    start_index = first_index + 1;
    first_index = file_no_suffix.find_first_of("/", start_index);
  }

  // Append file name.
  result += RenameEmpty(UnderscoresToCamelCase(
      file_no_suffix.substr(start_index, first_index - start_index), true));

  return result += ".php";
}

std::string GeneratedMessageFileName(const Descriptor* message,
                                     bool is_descriptor) {
  std::string result = FullClassName(message, is_descriptor);
  for (int i = 0; i < result.size(); i++) {
    if (result[i] == '\\') {
      result[i] = '/';
    }
  }
  return result + ".php";
}

std::string GeneratedEnumFileName(const EnumDescriptor* en,
                                  bool is_descriptor) {
  std::string result = FullClassName(en, is_descriptor);
  for (int i = 0; i < result.size(); i++) {
    if (result[i] == '\\') {
      result[i] = '/';
    }
  }
  return result + ".php";
}

std::string IntToString(int32 value) {
  std::ostringstream os;
  os << value;
  return os.str();
}

std::string LabelForField(const FieldDescriptor* field) {
  switch (field->label()) {
    case FieldDescriptor::LABEL_OPTIONAL: return "optional";
    case FieldDescriptor::LABEL_REQUIRED: return "required";
    case FieldDescriptor::LABEL_REPEATED: return "repeated";
    default: assert(false); return "";
  }
}

std::string TypeName(const FieldDescriptor* field) {
  switch (field->type()) {
    case FieldDescriptor::TYPE_INT32: return "int32";
    case FieldDescriptor::TYPE_INT64: return "int64";
    case FieldDescriptor::TYPE_UINT32: return "uint32";
    case FieldDescriptor::TYPE_UINT64: return "uint64";
    case FieldDescriptor::TYPE_SINT32: return "sint32";
    case FieldDescriptor::TYPE_SINT64: return "sint64";
    case FieldDescriptor::TYPE_FIXED32: return "fixed32";
    case FieldDescriptor::TYPE_FIXED64: return "fixed64";
    case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
    case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
    case FieldDescriptor::TYPE_DOUBLE: return "double";
    case FieldDescriptor::TYPE_FLOAT: return "float";
    case FieldDescriptor::TYPE_BOOL: return "bool";
    case FieldDescriptor::TYPE_ENUM: return "enum";
    case FieldDescriptor::TYPE_STRING: return "string";
    case FieldDescriptor::TYPE_BYTES: return "bytes";
    case FieldDescriptor::TYPE_MESSAGE: return "message";
    case FieldDescriptor::TYPE_GROUP: return "group";
    default: assert(false); return "";
  }
}

std::string EnumOrMessageSuffix(
    const FieldDescriptor* field, bool is_descriptor) {
  if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
    return ", '" + MessageFullName(field->message_type(), is_descriptor) + "'";
  }
  if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
    return ", '" + EnumFullName(field->enum_type(), is_descriptor) + "'";
  }
  return "";
}

// Converts a name to camel-case. If cap_first_letter is true, capitalize the
// first letter.
std::string UnderscoresToCamelCase(const string& input, bool cap_first_letter) {
  std::string result;
  for (int i = 0; i < input.size(); i++) {
    if ('a' <= input[i] && input[i] <= 'z') {
      if (cap_first_letter) {
        result += input[i] + ('A' - 'a');
      } else {
        result += input[i];
      }
      cap_first_letter = false;
    } else if ('A' <= input[i] && input[i] <= 'Z') {
      if (i == 0 && !cap_first_letter) {
        // Force first letter to lower-case unless explicitly told to
        // capitalize it.
        result += input[i] + ('a' - 'A');
      } else {
        // Capital letters after the first are left as-is.
        result += input[i];
      }
      cap_first_letter = false;
    } else if ('0' <= input[i] && input[i] <= '9') {
      result += input[i];
      cap_first_letter = true;
    } else {
      cap_first_letter = true;
    }
  }
  // Add a trailing "_" if the name should be altered.
  if (input[input.size() - 1] == '#') {
    result += '_';
  }
  return result;
}

std::string EscapeDollor(const string& to_escape) {
  return StringReplace(to_escape, "$", "\\$", true);
}

std::string BinaryToHex(const string& src) {
  string dest;
  size_t i;
  unsigned char symbol[16] = {
    '0', '1', '2', '3',
    '4', '5', '6', '7',
    '8', '9', 'a', 'b',
    'c', 'd', 'e', 'f',
  };

  dest.resize(src.size() * 2);
  char* append_ptr = &dest[0];

  for (i = 0; i < src.size(); i++) {
    *append_ptr++ = symbol[(src[i] & 0xf0) >> 4];
    *append_ptr++ = symbol[src[i] & 0x0f];
  }

  return dest;
}

void Indent(io::Printer* printer) {
  printer->Indent();
  printer->Indent();
}
void Outdent(io::Printer* printer) {
  printer->Outdent();
  printer->Outdent();
}

void GenerateField(const FieldDescriptor* field, io::Printer* printer,
                   bool is_descriptor) {
  if (field->is_repeated()) {
    GenerateFieldDocComment(printer, field);
    printer->Print(
        "private $^name^;\n",
        "name", field->name());
  } else if (field->containing_oneof()) {
    // Oneof fields are handled by GenerateOneofField.
    return;
  } else {
    GenerateFieldDocComment(printer, field);
    printer->Print(
        "private $^name^ = ^default^;\n",
        "name", field->name(),
        "default", DefaultForField(field));
  }

  if (is_descriptor) {
    printer->Print(
        "private $has_^name^ = false;\n",
        "name", field->name());
  }
}

void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
  // Oneof property needs to be protected in order to be accessed by parent
  // class in implementation.
  printer->Print(
      "protected $^name^;\n",
      "name", oneof->name());
}

void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
                           io::Printer* printer) {
  const OneofDescriptor* oneof = field->containing_oneof();

  // Generate getter.
  if (oneof != NULL) {
    GenerateFieldDocComment(printer, field);
    printer->Print(
        "public function get^camel_name^()\n"
        "{\n"
        "    return $this->readOneof(^number^);\n"
        "}\n\n",
        "camel_name", UnderscoresToCamelCase(field->name(), true),
        "number", IntToString(field->number()));
  } else {
    GenerateFieldDocComment(printer, field);
    printer->Print(
        "public function get^camel_name^()\n"
        "{\n"
        "    return $this->^name^;\n"
        "}\n\n",
        "camel_name", UnderscoresToCamelCase(field->name(), true), "name",
        field->name());
  }

  // Generate setter.
  GenerateFieldDocComment(printer, field);
  printer->Print(
      "public function set^camel_name^(^var^)\n"
      "{\n",
      "camel_name", UnderscoresToCamelCase(field->name(), true),
      "var", (field->is_repeated() ||
              field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
             "&$var": "$var");

  Indent(printer);

  // Type check.
  if (field->is_map()) {
    const Descriptor* map_entry = field->message_type();
    const FieldDescriptor* key = map_entry->FindFieldByName("key");
    const FieldDescriptor* value = map_entry->FindFieldByName("value");
    printer->Print(
        "$arr = GPBUtil::checkMapField($var, "
        "\\Google\\Protobuf\\Internal\\GPBType::^key_type^, "
        "\\Google\\Protobuf\\Internal\\GPBType::^value_type^",
        "key_type", ToUpper(key->type_name()),
        "value_type", ToUpper(value->type_name()));
    if (value->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
      printer->Print(
          ", \\^class_name^);\n",
          "class_name",
          FullClassName(value->message_type(), is_descriptor) + "::class");
    } else if (value->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
      printer->Print(
          ", \\^class_name^);\n",
          "class_name",
          FullClassName(value->enum_type(), is_descriptor) + "::class");
    } else {
      printer->Print(");\n");
    }
  } else if (field->is_repeated()) {
    printer->Print(
        "$arr = GPBUtil::checkRepeatedField($var, "
        "\\Google\\Protobuf\\Internal\\GPBType::^type^",
        "type", ToUpper(field->type_name()));
    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
      printer->Print(
          ", \\^class_name^);\n",
          "class_name",
          FullClassName(field->message_type(), is_descriptor) + "::class");
    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
      printer->Print(
          ", \\^class_name^);\n",
          "class_name",
          FullClassName(field->enum_type(), is_descriptor) + "::class");
    } else {
      printer->Print(");\n");
    }
  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
    printer->Print(
        "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
        "class_name", FullClassName(field->message_type(), is_descriptor));
  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
    printer->Print(
        "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
        "class_name", FullClassName(field->enum_type(), is_descriptor));
  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
    printer->Print(
        "GPBUtil::checkString($var, ^utf8^);\n",
        "utf8",
        field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
  } else {
    printer->Print(
        "GPBUtil::check^type^($var);\n",
        "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
  }

  if (oneof != NULL) {
    printer->Print(
        "$this->writeOneof(^number^, $var);\n",
        "number", IntToString(field->number()));
  } else if (field->is_repeated()) {
    printer->Print(
        "$this->^name^ = $arr;\n",
        "name", field->name());
  } else {
    printer->Print(
        "$this->^name^ = $var;\n",
        "name", field->name());
  }

  // Set has bit for proto2 only.
  if (is_descriptor) {
    printer->Print(
        "$this->has_^field_name^ = true;\n",
        "field_name", field->name());
  }

  Outdent(printer);

  printer->Print(
      "}\n\n");

  // Generate has method for proto2 only.
  if (is_descriptor) {
    printer->Print(
        "public function has^camel_name^()\n"
        "{\n"
        "    return $this->has_^field_name^;\n"
        "}\n\n",
        "camel_name", UnderscoresToCamelCase(field->name(), true),
        "field_name", field->name());
  }
}

void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
  printer->Print(
      "$pool->addEnum('^name^', "
      "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
      "name", EnumFullName(en, true),
      "class_name", en->name());
  Indent(printer);

  for (int i = 0; i < en->value_count(); i++) {
    const EnumValueDescriptor* value = en->value(i);
    printer->Print(
        "->value(\"^name^\", ^number^)\n",
        "name", ClassNamePrefix(value->name(), en) + value->name(),
        "number", IntToString(value->number()));
  }
  printer->Print("->finalizeToPool();\n\n");
  Outdent(printer);
}

void GenerateMessageToPool(const string& name_prefix, const Descriptor* message,
                           io::Printer* printer) {
  // Don't generate MapEntry messages -- we use the PHP extension's native
  // support for map fields instead.
  if (message->options().map_entry()) {
    return;
  }
  string class_name = name_prefix.empty()?
      message->name() : name_prefix + "_" + message->name();

  printer->Print(
      "$pool->addMessage('^message^', "
      "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
      "message", MessageFullName(message, true),
      "class_name", class_name);

  Indent(printer);

  for (int i = 0; i < message->field_count(); i++) {
    const FieldDescriptor* field = message->field(i);
    if (field->is_map()) {
      const FieldDescriptor* key =
          field->message_type()->FindFieldByName("key");
      const FieldDescriptor* val =
          field->message_type()->FindFieldByName("value");
      printer->Print(
          "->map('^field^', \\Google\\Protobuf\\Internal\\GPBType::^key^, "
          "\\Google\\Protobuf\\Internal\\GPBType::^value^, ^number^^other^)\n",
          "field", field->name(),
          "key", ToUpper(key->type_name()),
          "value", ToUpper(val->type_name()),
          "number", SimpleItoa(field->number()),
          "other", EnumOrMessageSuffix(val, true));
    } else if (!field->containing_oneof()) {
      printer->Print(
          "->^label^('^field^', "
          "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
          "field", field->name(),
          "label", LabelForField(field),
          "type", ToUpper(field->type_name()),
          "number", SimpleItoa(field->number()),
          "other", EnumOrMessageSuffix(field, true));
    }
  }

  // oneofs.
  for (int i = 0; i < message->oneof_decl_count(); i++) {
    const OneofDescriptor* oneof = message->oneof_decl(i);
    printer->Print("->oneof(^name^)\n",
                   "name", oneof->name());
    Indent(printer);
    for (int index = 0; index < oneof->field_count(); index++) {
      const FieldDescriptor* field = oneof->field(index);
      printer->Print(
          "->value('^field^', "
          "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
          "field", field->name(),
          "type", ToUpper(field->type_name()),
          "number", SimpleItoa(field->number()),
          "other", EnumOrMessageSuffix(field, true));
    }
    printer->Print("->finish()\n");
    Outdent(printer);
  }

  printer->Print(
      "->finalizeToPool();\n");

  Outdent(printer);

  printer->Print(
      "\n");

  for (int i = 0; i < message->nested_type_count(); i++) {
    GenerateMessageToPool(class_name, message->nested_type(i), printer);
  }
  for (int i = 0; i < message->enum_type_count(); i++) {
    GenerateEnumToPool(message->enum_type(i), printer);
  }
}

void GenerateAddFileToPool(const FileDescriptor* file, bool is_descriptor,
                           io::Printer* printer) {
    printer->Print(
        "public static $is_initialized = false;\n\n"
        "public static function initOnce() {\n");
    Indent(printer);

    printer->Print(
        "$pool = \\Google\\Protobuf\\Internal\\"
        "DescriptorPool::getGeneratedPool();\n\n"
        "if (static::$is_initialized == true) {\n"
        "  return;\n"
        "}\n");

  if (is_descriptor) {
    for (int i = 0; i < file->message_type_count(); i++) {
      GenerateMessageToPool("", file->message_type(i), printer);
    }
    for (int i = 0; i < file->enum_type_count(); i++) {
      GenerateEnumToPool(file->enum_type(i), printer);
    }

    printer->Print(
        "$pool->finish();\n");
  } else {
    for (int i = 0; i < file->dependency_count(); i++) {
      const std::string& name = file->dependency(i)->name();
      // Currently, descriptor.proto is not ready for external usage. Skip to
      // import it for now, so that its dependencies can still work as long as
      // they don't use protos defined in descriptor.proto.
      if (name == kDescriptorFile) {
        continue;
      }
      std::string dependency_filename =
          GeneratedMetadataFileName(name, is_descriptor);
      printer->Print(
          "\\^name^::initOnce();\n",
          "name", FilenameToClassname(dependency_filename));
    }

    // Add messages and enums to descriptor pool.
    FileDescriptorSet files;
    FileDescriptorProto* file_proto = files.add_file();
    file->CopyTo(file_proto);

    // Filter out descriptor.proto as it cannot be depended on for now.
    RepeatedPtrField<string>* dependency = file_proto->mutable_dependency();
    for (RepeatedPtrField<string>::iterator it = dependency->begin();
         it != dependency->end(); ++it) {
      if (*it != kDescriptorFile) {
        dependency->erase(it);
        break;
      }
    }

    // Filter out all extensions, since we do not support extension yet.
    file_proto->clear_extension();
    RepeatedPtrField<DescriptorProto>* message_type =
        file_proto->mutable_message_type();
    for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
         it != message_type->end(); ++it) {
      it->clear_extension();
    }

    string files_data;
    files.SerializeToString(&files_data);

    printer->Print("$pool->internalAddGeneratedFile(hex2bin(\n");
    Indent(printer);

    // Only write 30 bytes per line.
    static const int kBytesPerLine = 30;
    for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
      printer->Print(
          "\"^data^\"^dot^\n",
          "data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
          "dot", i + kBytesPerLine < files_data.size() ? " ." : "");
    }

    Outdent(printer);
    printer->Print(
        "));\n\n");
  }
  printer->Print(
      "static::$is_initialized = true;\n");
  Outdent(printer);
  printer->Print("}\n");
}

void GenerateUseDeclaration(bool is_descriptor, io::Printer* printer) {
  if (!is_descriptor) {
    printer->Print(
        "use Google\\Protobuf\\Internal\\GPBType;\n"
        "use Google\\Protobuf\\Internal\\RepeatedField;\n"
        "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
  } else {
    printer->Print(
        "use Google\\Protobuf\\Internal\\GPBType;\n"
        "use Google\\Protobuf\\Internal\\GPBWire;\n"
        "use Google\\Protobuf\\Internal\\RepeatedField;\n"
        "use Google\\Protobuf\\Internal\\InputStream;\n\n"
        "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
  }
}

void GenerateHead(const FileDescriptor* file, io::Printer* printer) {
  printer->Print(
    "<?php\n"
    "# Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
    "# source: ^filename^\n"
    "\n",
    "filename", file->name());
}

std::string FilenameToClassname(const string& filename) {
  int lastindex = filename.find_last_of(".");
  std::string result = filename.substr(0, lastindex);
  for (int i = 0; i < result.size(); i++) {
    if (result[i] == '/') {
      result[i] = '\\';
    }
  }
  return result;
}

void GenerateMetadataFile(const FileDescriptor* file,
                          bool is_descriptor,
                          GeneratorContext* generator_context) {
  std::string filename = GeneratedMetadataFileName(file->name(), is_descriptor);
  scoped_ptr<io::ZeroCopyOutputStream> output(
      generator_context->Open(filename));
  io::Printer printer(output.get(), '^');

  GenerateHead(file, &printer);

  std::string fullname = FilenameToClassname(filename);
  int lastindex = fullname.find_last_of("\\");

  printer.Print(
      "namespace ^name^;\n\n",
      "name", fullname.substr(0, lastindex));

  if (lastindex != string::npos) {
    printer.Print(
        "class ^name^\n"
        "{\n",
        "name", fullname.substr(lastindex + 1));
  } else {
    printer.Print(
        "class ^name^\n"
        "{\n",
        "name", fullname);
  }
  Indent(&printer);

  GenerateAddFileToPool(file, is_descriptor, &printer);

  Outdent(&printer);
  printer.Print("}\n\n");
}

void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
                      bool is_descriptor, GeneratorContext* generator_context) {
  std::string filename = GeneratedEnumFileName(en, is_descriptor);
  scoped_ptr<io::ZeroCopyOutputStream> output(
      generator_context->Open(filename));
  io::Printer printer(output.get(), '^');

  GenerateHead(file, &printer);

  std::string fullname = FilenameToClassname(filename);
  int lastindex = fullname.find_last_of("\\");

  if (!file->package().empty()) {
    printer.Print(
        "namespace ^name^;\n\n",
        "name", fullname.substr(0, lastindex));
  }

  GenerateEnumDocComment(&printer, en);

  if (lastindex != string::npos) {
    printer.Print(
        "class ^name^\n"
        "{\n",
        "name", fullname.substr(lastindex + 1));
  } else {
    printer.Print(
        "class ^name^\n"
        "{\n",
        "name", fullname);
  }
  Indent(&printer);

  for (int i = 0; i < en->value_count(); i++) {
    const EnumValueDescriptor* value = en->value(i);
    GenerateEnumValueDocComment(&printer, value);
    printer.Print("const ^name^ = ^number^;\n",
                  "name", ClassNamePrefix(value->name(), en) + value->name(),
                  "number", IntToString(value->number()));
  }

  Outdent(&printer);
  printer.Print("}\n\n");
}

void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
                         bool is_descriptor,
                         GeneratorContext* generator_context) {
  // Don't generate MapEntry messages -- we use the PHP extension's native
  // support for map fields instead.
  if (message->options().map_entry()) {
    return;
  }

  std::string filename = GeneratedMessageFileName(message, is_descriptor);
  scoped_ptr<io::ZeroCopyOutputStream> output(
      generator_context->Open(filename));
  io::Printer printer(output.get(), '^');

  GenerateHead(file, &printer);

  std::string fullname = FilenameToClassname(filename);
  int lastindex = fullname.find_last_of("\\");

  if (!file->package().empty()) {
    printer.Print(
        "namespace ^name^;\n\n",
        "name", fullname.substr(0, lastindex));
  }

  GenerateUseDeclaration(is_descriptor, &printer);

  GenerateMessageDocComment(&printer, message);
  if (lastindex != string::npos) {
    printer.Print(
        "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
        "{\n",
        "name", fullname.substr(lastindex + 1));
  } else {
    printer.Print(
        "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
        "{\n",
        "name", fullname);
  }
  Indent(&printer);

  // Field and oneof definitions.
  for (int i = 0; i < message->field_count(); i++) {
    const FieldDescriptor* field = message->field(i);
    GenerateField(field, &printer, is_descriptor);
  }
  for (int i = 0; i < message->oneof_decl_count(); i++) {
    const OneofDescriptor* oneof = message->oneof_decl(i);
    GenerateOneofField(oneof, &printer);
  }
  printer.Print("\n");

  printer.Print(
      "public function __construct() {\n");
  Indent(&printer);

  std::string metadata_filename =
      GeneratedMetadataFileName(file->name(), is_descriptor);
  std::string metadata_fullname = FilenameToClassname(metadata_filename);
  printer.Print(
      "\\^fullname^::initOnce();\n"
      "parent::__construct();\n",
      "fullname", metadata_fullname);

  Outdent(&printer);
  printer.Print("}\n\n");

  // Field and oneof accessors.
  for (int i = 0; i < message->field_count(); i++) {
    const FieldDescriptor* field = message->field(i);
    GenerateFieldAccessor(field, is_descriptor, &printer);
  }
  for (int i = 0; i < message->oneof_decl_count(); i++) {
    const OneofDescriptor* oneof = message->oneof_decl(i);
    printer.Print(
      "public function get^camel_name^()\n"
      "{\n"
      "    return $this->whichOneof(\"^name^\");\n"
      "}\n\n",
      "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
      oneof->name());
  }

  Outdent(&printer);
  printer.Print("}\n\n");

  // Nested messages and enums.
  for (int i = 0; i < message->nested_type_count(); i++) {
    GenerateMessageFile(file, message->nested_type(i), is_descriptor,
                        generator_context);
  }
  for (int i = 0; i < message->enum_type_count(); i++) {
    GenerateEnumFile(file, message->enum_type(i), is_descriptor,
                     generator_context);
  }
}

void GenerateFile(const FileDescriptor* file, bool is_descriptor,
                  GeneratorContext* generator_context) {
  GenerateMetadataFile(file, is_descriptor, generator_context);
  for (int i = 0; i < file->message_type_count(); i++) {
    GenerateMessageFile(file, file->message_type(i), is_descriptor,
                        generator_context);
  }
  for (int i = 0; i < file->enum_type_count(); i++) {
    GenerateEnumFile(file, file->enum_type(i), is_descriptor,
                     generator_context);
  }
}

static string EscapePhpdoc(const string& input) {
  string result;
  result.reserve(input.size() * 2);

  char prev = '*';

  for (string::size_type i = 0; i < input.size(); i++) {
    char c = input[i];
    switch (c) {
      case '*':
        // Avoid "/*".
        if (prev == '/') {
          result.append("*");
        } else {
          result.push_back(c);
        }
        break;
      case '/':
        // Avoid "*/".
        if (prev == '*') {
          result.append("/");
        } else {
          result.push_back(c);
        }
        break;
      case '@':
        // '@' starts phpdoc tags including the @deprecated tag, which will
        // cause a compile-time error if inserted before a declaration that
        // does not have a corresponding @Deprecated annotation.
        result.append("@");
        break;
      case '<':
        // Avoid interpretation as HTML.
        result.append("<");
        break;
      case '>':
        // Avoid interpretation as HTML.
        result.append(">");
        break;
      case '&':
        // Avoid interpretation as HTML.
        result.append("&");
        break;
      case '\\':
        // Java interprets Unicode escape sequences anywhere!
        result.append("\");
        break;
      default:
        result.push_back(c);
        break;
    }

    prev = c;
  }

  return result;
}

static void GenerateDocCommentBodyForLocation(
    io::Printer* printer, const SourceLocation& location) {
  string comments = location.leading_comments.empty() ?
      location.trailing_comments : location.leading_comments;
  if (!comments.empty()) {
    // TODO(teboring):  Ideally we should parse the comment text as Markdown and
    //   write it back as HTML, but this requires a Markdown parser.  For now
    //   we just use <pre> to get fixed-width text formatting.

    // If the comment itself contains block comment start or end markers,
    // HTML-escape them so that they don't accidentally close the doc comment.
    comments = EscapePhpdoc(comments);

    vector<string> lines = Split(comments, "\n");
    while (!lines.empty() && lines.back().empty()) {
      lines.pop_back();
    }

    printer->Print(" * <pre>\n");
    for (int i = 0; i < lines.size(); i++) {
      // Most lines should start with a space.  Watch out for lines that start
      // with a /, since putting that right after the leading asterisk will
      // close the comment.
      if (!lines[i].empty() && lines[i][0] == '/') {
        printer->Print(" * ^line^\n", "line", lines[i]);
      } else {
        printer->Print(" *^line^\n", "line", lines[i]);
      }
    }
    printer->Print(
        " * </pre>\n"
        " *\n");
  }
}

template <typename DescriptorType>
static void GenerateDocCommentBody(
    io::Printer* printer, const DescriptorType* descriptor) {
  SourceLocation location;
  if (descriptor->GetSourceLocation(&location)) {
    GenerateDocCommentBodyForLocation(printer, location);
  }
}

static string FirstLineOf(const string& value) {
  string result = value;

  string::size_type pos = result.find_first_of('\n');
  if (pos != string::npos) {
    result.erase(pos);
  }

  return result;
}

void GenerateMessageDocComment(io::Printer* printer,
                               const Descriptor* message) {
  printer->Print("/**\n");
  GenerateDocCommentBody(printer, message);
  printer->Print(
    " * Protobuf type <code>^fullname^</code>\n"
    " */\n",
    "fullname", EscapePhpdoc(message->full_name()));
}

void GenerateFieldDocComment(io::Printer* printer,
                             const FieldDescriptor* field) {
  // In theory we should have slightly different comments for setters, getters,
  // etc., but in practice everyone already knows the difference between these
  // so it's redundant information.

  // We start the comment with the main body based on the comments from the
  // .proto file (if present). We then end with the field declaration, e.g.:
  //   optional string foo = 5;
  // If the field is a group, the debug string might end with {.
  printer->Print("/**\n");
  GenerateDocCommentBody(printer, field);
  printer->Print(
    " * <code>^def^</code>\n",
    "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
  printer->Print(" */\n");
}

void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) {
  printer->Print("/**\n");
  GenerateDocCommentBody(printer, enum_);
  printer->Print(
    " * Protobuf enum <code>^fullname^</code>\n"
    " */\n",
    "fullname", EscapePhpdoc(enum_->full_name()));
}

void GenerateEnumValueDocComment(io::Printer* printer,
                                 const EnumValueDescriptor* value) {
  printer->Print("/**\n");
  GenerateDocCommentBody(printer, value);
  printer->Print(
    " * <code>^def^</code>\n"
    " */\n",
    "def", EscapePhpdoc(FirstLineOf(value->DebugString())));
}

bool Generator::Generate(const FileDescriptor* file, const string& parameter,
                         GeneratorContext* generator_context,
                         string* error) const {
  bool is_descriptor = parameter == "internal";

  if (is_descriptor && file->name() != kDescriptorFile) {
    *error =
        "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
    return false;
  }

  if (!is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
    *error =
        "Can only generate PHP code for proto3 .proto files.\n"
        "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
    return false;
  }

  GenerateFile(file, is_descriptor, generator_context);

  return true;
}

}  // namespace php
}  // namespace compiler
}  // namespace protobuf
}  // namespace google


text:
package com.beyondar.android.opengl.renderer;

import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.view.Surface;

import com.beyondar.android.opengl.renderable.Renderable;
import com.beyondar.android.opengl.texture.Texture;
import com.beyondar.android.opengl.util.MatrixGrabber;
import com.beyondar.android.plugin.GLPlugin;
import com.beyondar.android.plugin.Plugable;
import com.beyondar.android.sensor.BeyondarSensorListener;
import com.beyondar.android.util.Logger;
import com.beyondar.android.util.PendingBitmapsToBeLoaded;
import com.beyondar.android.util.Utils;
import com.beyondar.android.util.cache.BitmapCache;
import com.beyondar.android.util.math.Distance;
import com.beyondar.android.util.math.MathUtils;
import com.beyondar.android.util.math.geom.Point3;
import com.beyondar.android.util.math.geom.Ray;
import com.beyondar.android.world.BeyondarObject;
import com.beyondar.android.world.BeyondarObjectList;
import com.beyondar.android.world.GeoObject;
import com.beyondar.android.world.World;

// Some references:
// http://ovcharov.me/2011/01/14/android-opengl-es-ray-picking/
// http://magicscrollsofcode.blogspot.com/2010/10/3d-picking-in-android.html

/**
 * Renderer for drawing the {@link com.beyondar.android.world.World World} with
 * OpenGL.
 */
public class ARRenderer implements GLSurfaceView.Renderer, BeyondarSensorListener,
		BitmapCache.OnExternalBitmapLoadedCacheListener, Plugable<GLPlugin> {

	/**
	 * Callback to get notified when the snapshot of the OpenGL view is taken.
	 */
	public static interface GLSnapshotCallback {
		/**
		 * This method is called when the snapshot of the GL Surface is ready.
		 * If there is an error, the image will be null
		 *
		 * @param snapshot
		 */
		void onSnapshotTaken(Bitmap snapshot);
	}

	private class UriAndBitmap {
		String uri;
		Bitmap btm;
	}

	/**
	 * The default maximum distance that the object will be displayed (meters)
	 * in the AR view.
	 */
	public static final float DEFAULT_MAX_AR_VIEW_DISTANCE = 100;

	public static final float DEFAULT_DISTANCE_FACTOR = 2;

	/**
	 * Specifies the distance from the viewer to the far clipping plane. Used
	 * for the GLU.gluPerspective
	 */
	public static float Z_FAR = 400.0f;
	public static final double TIMEOUT_LOAD_TEXTURE = 1500;

	private static final String TAG = "ARRenderer";

	private float mAccelerometerValues[] = new float[3];
	private float mMagneticValues[] = new float[3];

	private float mRotationMatrix[] = new float[16];
	private float mRemappedRotationMatrix[] = new float[16];

	private MatrixGrabber mMatrixGrabber = new MatrixGrabber();
	private int mWidth, mHeight;

	private static HashMap<String, Texture> sTextureHolder = new HashMap<String, Texture>();
	private static PendingBitmapsToBeLoaded<BeyondarObject> sPendingTextureObjects = new PendingBitmapsToBeLoaded<BeyondarObject>();
	private static ArrayList<UriAndBitmap> sNewBitmapsLoaded = new ArrayList<UriAndBitmap>();
	private static final float[] sInclination = new float[16];

	private float mArViewDistance;
	private float mDistanceFactor;

	private World mWorld;

	/** List of loaded plugins. */
	protected List<GLPlugin> plugins;
	/**
	 * Lock to synchronize the access to the plugins. Use it to modify the
	 * loaded plugins.
	 */
	protected Object lockPlugins = new Object();

	private boolean mScreenshot;
	private GLSnapshotCallback mSnapshotCallback;

	private int mSurfaceRotation;

	private Point3 mCameraPosition;

	private boolean mReloadWorldTextures;

	private boolean mRender;

	private boolean mGetFps = false;

	private long mCurrentTime = System.currentTimeMillis();
	private float mFrames = 0;
	private FpsUpdatable mFpsUpdatable;

	private float mMaxDistanceSizePoints;
	private float mMinDistanceSizePoints;

	private Queue<float[]> mFloat4ArrayPool;
	private OnBeyondarObjectRenderedListener mOnBeyondarObjectRenderedListener;

	/** This list keep track of the object that have been rendered */
	private List<BeyondarObject> mRenderedObjects;

	private boolean mFillPositions;

	// This GL extension allow us to load non square textures.
	private boolean isGL_OES_texture_npot;

	/**
	 * {@link ARRenderer} constructor.
	 */
	public ARRenderer() {
		mArViewDistance = DEFAULT_MAX_AR_VIEW_DISTANCE;
		mDistanceFactor = DEFAULT_DISTANCE_FACTOR;
		mReloadWorldTextures = false;
		setRendering(true);
		mCameraPosition = new Point3(0, 0, 0);
		mFloat4ArrayPool = new ConcurrentLinkedQueue<float[]>();

		mRenderedObjects = new ArrayList<BeyondarObject>();

		mFillPositions = false;

		plugins = new ArrayList<GLPlugin>();
	}

	/**
	 * Set the rotation of the device:

	 * Surface.ROTATION_0

	 * Surface.ROTATION_90

	 * Surface.ROTATION_180

	 * Surface.ROTATION_270

	 *
	 * @param surfaceRotation
	 */
	public void rotateView(int surfaceRotation) {
		mSurfaceRotation = surfaceRotation;
	}

	/**
	 * Define the world where the objects are stored.
	 *
	 * @param world
	 */
	public void setWorld(World world) {
		mWorld = world;
		mWorld.getBitmapCache().addOnExternalBitmapLoadedCahceListener(this);
		mReloadWorldTextures = true;
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.setup(mWorld, this);
			}
		}
	}

	/**
	 * Get the {@link com.beyondar.android.world.World World} used for by the
	 * {@link ARRenderer}.
	 *
	 * @return
	 */
	public World getWorld() {
		return mWorld;
	}

	/**
	 * Change the camera position. It is independent to the GPS position.
	 *
	 * @param newCameraPos
	 *            The new camera position
	 */
	public void setCameraPosition(Point3 newCameraPos) {
		mCameraPosition = newCameraPos;
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.onCameraPositionChanged(newCameraPos);
			}
		}
	}

	/**
	 * Restore the camera position
	 */
	public void restoreCameraPosition() {
		mCameraPosition.x = 0;
		mCameraPosition.y = 0;
		mCameraPosition.z = 0;
		setCameraPosition(mCameraPosition);
	}

	/**
	 * Get the camera position
	 *
	 * @return
	 */
	public Point3 getCameraPosition() {
		return mCameraPosition;
	}

	public void onDrawFrame(GL10 gl) {
		if (!mRender) {
			return;
		}
		long time = System.currentTimeMillis();

		SensorManager.getInclination(sInclination);
		SensorManager.getRotationMatrix(mRotationMatrix, sInclination, mAccelerometerValues, mMagneticValues);

		float rotation = 0;
		switch (mSurfaceRotation) {
		case Surface.ROTATION_0:
			rotation = 270;
			break;
		case Surface.ROTATION_90:
			break;
		case Surface.ROTATION_180:
			rotation = 90;
			break;
		case Surface.ROTATION_270:
			rotation = 180;
			break;
		}

		gl.glRotatef(rotation, 0, 0, 1);

		SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y,
				SensorManager.AXIS_MINUS_X, mRemappedRotationMatrix);

		// Clear color buffer
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		// Load remapped matrix
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
		gl.glLoadMatrixf(mRemappedRotationMatrix, 0);

		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		// gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

		gl.glDisableClientState(GL10.GL_COLOR_ARRAY);

		// gl.glEnable(GL10.GL_BLEND); //Turn Blending On
		// gl.glDisable(GL10.GL_DEPTH_TEST); //Turn Depth Testing Off

		// getCurrentProjection(gl);
		// getCurrentModelView(gl);

		if (sNewBitmapsLoaded.size() > 0) {
			for (int i = 0; i < sNewBitmapsLoaded.size(); i++) {
				setAllTextures(gl, sNewBitmapsLoaded.get(i).uri, sNewBitmapsLoaded.get(i).btm,
						sPendingTextureObjects);
			}
			sNewBitmapsLoaded.clear();
		}

		// Store projection and modelview matrices. This is used for the
		// collision detections
		mMatrixGrabber.getCurrentState(gl);

		if (mWorld != null) {
			if (mReloadWorldTextures) {
				loadWorldTextures(gl);
				mReloadWorldTextures = false;
			}
			mRenderedObjects.clear();
			renderWorld(gl, time);

			OnBeyondarObjectRenderedListener tmpTraker = mOnBeyondarObjectRenderedListener;
			if (tmpTraker != null) {
				tmpTraker.onBeyondarObjectsRendered(mRenderedObjects);
			}
		}

		try {
			for (GLPlugin plugin : plugins) {
				plugin.onFrameRendered(gl);
			}
		} catch (ConcurrentModificationException e) {
			Logger.w("Some plugins where changed while drawing a frame");
		}

		if (mScreenshot) {
			mScreenshot = false;
			if (mSnapshotCallback != null) {
				mSnapshotCallback.onSnapshotTaken(savePixels(gl));
			}
		}

	}

	/**
	 * Generate a new bitmap from the gl.
	 *
	 * @param gl
	 * @return A new Bitmap of the last frame.
	 */
	protected Bitmap savePixels(GL10 gl) {
		// http://stackoverflow.com/questions/3310990/taking-screenshot-of-android-opengl
		// http://www.anddev.org/how_to_get_opengl_screenshot__useful_programing_hint-t829.html
		int b[] = new int[mWidth * mHeight];
		IntBuffer ib = IntBuffer.wrap(b);
		ib.position(0);
		gl.glReadPixels(0, 0, mWidth, mHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);

		// The bytes within the ints are in the wrong order for android, but
		// convert into a
		// bitmap anyway. They're also bottom-to-top rather than top-to-bottom.
		// We'll fix
		// this up soon using some fast API calls.
		Bitmap glbitmap = Bitmap.createBitmap(b, mWidth, mHeight, Bitmap.Config.ARGB_4444);
		ib = null; // we're done with ib
		b = null; // we're done with b, so allow the memory to be freed

		// To swap the color channels, we'll use a
		// ColorMatrix/ColorMatrixFilter. From the Android docs:
		//
		// This is a 5x4 matrix: [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o,
		// p, q, r, s, t ]
		// When applied to a color [r, g, b, a] the resulting color is computed
		// as (after clamping):
		//
		// R' = a*R + b*G + c*B + d*A + e;
		// G' = f*R + g*G + h*B + i*A + j;
		// B' = k*R + l*G + m*B + n*A + o;
		// A' = p*R + q*G + r*B + s*A + t;
		//
		// We want to swap R and B, so the coefficients will be:
		// R' = B => 0,0,1,0,0
		// G' = G => 0,1,0,0,0
		// B' = R => 1,0,0,0,0
		// A' = A => 0,0,0,1,0

		final float[] cmVals = { 0, 0, 1, 0, 0, //
				0, 1, 0, 0, 0, //
				1, 0, 0, 0, 0, //
				0, 0, 0, 1, 0 };

		Paint paint = new Paint();

		// our R<->B swapping paint
		paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(cmVals)));

		// the bitmap we're going to draw onto
		Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_4444);

		// we draw to the bitmap through a canvas
		Canvas canvas = new Canvas(bitmap);
		// draw the opengl bitmap onto the canvas, using the color swapping
		// paint
		canvas.drawBitmap(glbitmap, 0, 0, paint);

		glbitmap.recycle();
		glbitmap = null; // we're done with glbitmap, let go of its memory

		// the image is still upside-down, so vertically flip it
		Matrix matrix = new Matrix();
		matrix.preScale(1.0f, -1.0f); // scaling: x = x, y = -y, i.e. vertically
										// flip

		// new bitmap, using the flipping matrix
		Bitmap result = Bitmap
				.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

		bitmap.recycle();
		System.gc();
		return result;
	}

	private static final double[] sOut = new double[3];

	/**
	 * Convert the geolocation to gl points representation.
	 *
	 * @param geoObject
	 *            The {@link com.beyondar.android.world.GeoObject GeoObject} to
	 *            be processed.
	 * @param out
	 *            Where the results be stored.
	 */
	protected void convertGPStoPoint3(GeoObject geoObject, Point3 out) {
		float x, z, y;
		x = (float) (Distance.fastConversionGeopointsToMeters(geoObject.getLongitude()
				- mWorld.getLongitude()) / mDistanceFactor);
		z = (float) (Distance.fastConversionGeopointsToMeters(geoObject.getAltitude()
				- mWorld.getAltitude()) / mDistanceFactor);
		y = (float) (Distance.fastConversionGeopointsToMeters(geoObject.getLatitude()
				- mWorld.getLatitude()) / mDistanceFactor);

		if (mMaxDistanceSizePoints > 0 || mMinDistanceSizePoints > 0) {
			double totalDst = Distance.calculateDistance(x, y, 0, 0);

			if (mMaxDistanceSizePoints > 0 && totalDst > mMaxDistanceSizePoints) {
				MathUtils.linearInterpolate(0, 0, 0, x, y, 0, mMaxDistanceSizePoints, totalDst, sOut);
				x = (float) sOut[0];
				y = (float) sOut[1];
				if (mMinDistanceSizePoints > 0) {
					totalDst = Distance.calculateDistance(x, y, 0, 0);
				}
			}
			if (mMinDistanceSizePoints > 0 && totalDst < mMinDistanceSizePoints) {
				MathUtils.linearInterpolate(0, 0, 0, x, y, 0, mMinDistanceSizePoints, totalDst, sOut);
				x = (float) sOut[0];
				y = (float) sOut[1];
			}
		}

		out.x = x;
		out.y = y;
		out.z = z;
	}

	/**
	 * When a {@link com.beyondar.android.world.GeoObject GeoObject} is rendered
	 * according to its position it could look very small if it is far away. Use
	 * this method to render far objects as if there were closer.

	 * For instance if there is an object at 100 meters and we want to have
	 * everything at least at 25 meters, we could use this method for that
	 * purpose.

	 * To set it to the default behavior just set it to 0
	 *
	 * @param maxDistanceSize
	 *            The top far distance (in meters) which we want to draw a
	 *            {@link com.beyondar.android.world.GeoObject GeoObject} , 0 to
	 *            set again the default behavior
	 */
	public void setPullCloserDistance(float maxDistanceSize) {
		mMaxDistanceSizePoints = (float) (maxDistanceSize / 2);
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.onMaxDistanceSizeChanged(mMaxDistanceSizePoints);
			}
		}
	}

	/**
	 * Get the max distance which a {@link com.beyondar.android.world.GeoObject
	 * GeoObject} will be rendered.
	 *
	 * @return The current max distance. 0 is the default behavior
	 */
	public float getPullCloserDistance() {
		return (float) (mMaxDistanceSizePoints * 2);
	}

	/**
	 * When a {@link com.beyondar.android.world.GeoObject GeoObject} is rendered
	 * according to its position it could look very big if it is too close. Use
	 * this method to render near objects as if there were farther.

	 * For instance if there is an object at 1 meters and we want to have
	 * everything at least at 10 meters, we could use this method for that
	 * purpose.

	 * To set it to the default behavior just set it to 0
	 *
	 * @param minDistanceSize
	 *            The top near distance (in meters) which we want to draw a
	 *            {@link com.beyondar.android.world.GeoObject GeoObject} , 0 to
	 *            set again the default behavior
	 */
	public void setPushAwayDistance(float minDistanceSize) {
		mMinDistanceSizePoints = (float) (minDistanceSize / 2);
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.onMaxDistanceSizeChanged(mMinDistanceSizePoints);
			}
		}
	}

	/**
	 * Get the minimum distance which a
	 * {@link com.beyondar.android.world.GeoObject GeoObject} will be rendered.
	 *
	 * @return The current minimum distance. 0 is the default behavior
	 */
	public float getPushAwayDistance() {
		return (float) (mMinDistanceSizePoints * 2);
	}

	/**
	 * Set the
	 * {@link com.beyondar.android.opengl.renderer.ARRenderer.FpsUpdatable
	 * FpsUpdatable} to get notified about the frames per seconds.
	 *
	 * @param fpsUpdatable
	 *            The event listener. Use null to remove the
	 *            {@link com.beyondar.android.opengl.renderer.ARRenderer.FpsUpdatable
	 *            FpsUpdatable}
	 */
	public void setFpsUpdatable(FpsUpdatable fpsUpdatable) {
		mCurrentTime = System.currentTimeMillis();
		mFpsUpdatable = fpsUpdatable;
		mGetFps = mFpsUpdatable != null;
	}

	public void setOnBeyondarObjectRenderedListener(OnBeyondarObjectRenderedListener rendererTracker) {
		mOnBeyondarObjectRenderedListener = rendererTracker;
	}

	/**
	 * Render the {@link com.beyondar.android.world.World World}.
	 *
	 * @param gl
	 * @param time
	 *            Time mark to be used for drawing the frame.
	 */
	protected void renderWorld(GL10 gl, long time) {
		BeyondarObjectList list = null;
		try {
			for (int i = 0; i < mWorld.getBeyondarObjectLists().size(); i++) {
				list = mWorld.getBeyondarObjectLists().get(i);
				if (list != null) {
					renderList(gl, list, time);
				}
			}
		} catch (ConcurrentModificationException e) {
		}
		mWorld.forceProcessRemoveQueue();

		if (mGetFps) {
			mFrames++;
			final long timeInterval = System.currentTimeMillis() - mCurrentTime;
			if (timeInterval > 1000) {
				if (Logger.DEBUG_OPENGL) {
					Logger.d("Frames/second:  " + mFrames / (timeInterval / 1000F));
				}
				if (mFpsUpdatable != null) {
					mFpsUpdatable.onFpsUpdate(mFrames / (timeInterval / 1000F));
				}
				mFrames = 0;
				mCurrentTime = System.currentTimeMillis();
			}
		}
	}

	/**
	 * Render a specific list.
	 *
	 * @param gl
	 * @param list
	 *            List to render.
	 * @param time
	 *            Time mark to be used for drawing the frame.
	 */
	protected void renderList(GL10 gl, BeyondarObjectList list, long time) {

		Texture listTexture = list.getDefaultTexture();

		if (!listTexture.isLoaded()) {
			Texture defaultTexture = sTextureHolder.get(list.getDefaultImageUri());
			if (defaultTexture == null || !defaultTexture.isLoaded()) {
				Logger.w("Warning!! The default texture for the list \"" + list.getType()
						+ "\" has not been loaded. Trying to load it now...");
				Bitmap defaultBtm = mWorld.getBitmapCache().getBitmap(list.getDefaultImageUri());
				defaultTexture = load2DTexture(gl, defaultBtm);
			}
			list.setDefaultTexture(defaultTexture == null ? null : defaultTexture.clone());
		}

		for (int j = 0; j < list.size(); j++) {
			BeyondarObject beyondarObject = list.get(j);
			if (beyondarObject == null) {
				continue;
			}
			renderBeyondarObject(gl, beyondarObject, listTexture, time);
		}
	}

	private float[] tmpEyeForRendering = new float[4];

	/**
	 * Render a specific {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject}.
	 *
	 * @param gl
	 * @param beyondarObject
	 *            {@link com.beyondar.android.world.BeyondarObject
	 *            BeyondarObject} to render.
	 * @param defaultTexture
	 *            If the texture from the
	 *            {@link com.beyondar.android.world.BeyondarObject
	 *            BeyondarObject} is not available the defaultTexure will be
	 *            used.
	 */
	protected void renderBeyondarObject(GL10 gl, BeyondarObject beyondarObject, Texture defaultTexture,
			long time) {
		boolean renderObject = false;
		Renderable renderable = beyondarObject.getOpenGLObject();

		if (renderable == null || !beyondarObject.isVisible()) {
			return;
		}
		double dst = 0;
		if (beyondarObject instanceof GeoObject) {
			dst = ((GeoObject) beyondarObject).calculateDistanceMeters(mWorld.getLongitude(),
					mWorld.getLatitude());
			convertGPStoPoint3((GeoObject) beyondarObject, beyondarObject.getPosition());
		} else {
			Point3 position = beyondarObject.getPosition();
			dst = MathUtils.GLUnitsToMeters((float) Distance.calculateDistanceCoordinates(0, 0, 0,
					position.x, position.y, position.z));
		}

		beyondarObject.setDistanceFromUser(dst);

		if (dst < mArViewDistance) {
			renderObject = true;
		}

		boolean forceDraw = renderable.update(time, (float) dst, beyondarObject);

		if (forceDraw || renderObject) {
			if (beyondarObject.isFacingToCamera()) {
				MathUtils.calcAngleFaceToCamera(beyondarObject.getPosition(), mCameraPosition,
						beyondarObject.getAngle());
			}

			if (!beyondarObject.getTexture().isLoaded() && beyondarObject.getImageUri() != null) {
				int counter = beyondarObject.getTexture().getLoadTryCounter();
				double timeOut = TIMEOUT_LOAD_TEXTURE * (counter + 1);
				if (beyondarObject.getTexture().getTimeStamp() == 0
						|| time - beyondarObject.getTexture().getTimeStamp() > timeOut) {
					Logger.d("Loading new textures...");
					loadBeyondarObjectTexture(gl, beyondarObject);
					if (!beyondarObject.getTexture().isLoaded()) {
						beyondarObject.getTexture().setTimeStamp(time);
						beyondarObject.getTexture().setLoadTryCounter(counter + 1);
					}
				}
			}

			getScreenCoordinates(beyondarObject.getPosition(), beyondarObject.getScreenPositionCenter(),
					tmpEyeForRendering);

			try {
				for (GLPlugin plugin : plugins) {
					plugin.onDrawBeyondaarObject(gl, beyondarObject, defaultTexture);
				}
			} catch (ConcurrentModificationException e) {
				Logger.w("Some plugins where changed while drawing a frame");
			}

			renderable.draw(gl, defaultTexture);

			if (mFillPositions) {
				fillBeyondarObjectScreenPositions(beyondarObject);
			}
			mRenderedObjects.add(beyondarObject);
		} else {
			renderable.onNotRendered(dst);
		}
	}

	/**
	 * Use this method to fill all the screen positions of the
	 * {@link com.beyondar.android.world.BeyondarObject BeyondarObject}. After
	 * this method is called you can use the following:

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionBottomLeft()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionBottomRight()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionTopLeft()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionTopRight()}
	 *
	 * @param beyondarObject
	 *            The {@link com.beyondar.android.world.BeyondarObject
	 *            BeyondarObject} to compute
	 */
	public void fillBeyondarObjectScreenPositions(BeyondarObject beyondarObject) {
		getScreenCoordinates(beyondarObject.getBottomLeft(), beyondarObject.getScreenPositionBottomLeft());
		getScreenCoordinates(beyondarObject.getBottomRight(), beyondarObject.getScreenPositionBottomRight());
		getScreenCoordinates(beyondarObject.getTopLeft(), beyondarObject.getScreenPositionTopLeft());
		getScreenCoordinates(beyondarObject.getTopRight(), beyondarObject.getScreenPositionTopRight());
	}

	/**
	 * Take an snapshot of the view. The callback will be notified when the
	 * picture is ready.
	 *
	 * @param callBack
	 */
	public void tackePicture(GLSnapshotCallback callBack) {
		mSnapshotCallback = callBack;
		mScreenshot = true;
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		gl.glViewport(0, 0, width, height);

		/*
		 * Set our projection matrix. This doesn't have to be done each time we
		 * draw, but usually a new projection needs to be set when the viewport
		 * is resized.
		 */
		float screenRatio = (float) width / height;
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		// gl.glFrustumf(-ratio, ratio, -1, 1, 1f, 100);

		GLU.gluPerspective(gl, 45.0f, screenRatio, 0.1f, Z_FAR);
		mWidth = width;
		mHeight = height;
		setupViewPort();
	}

	private void checkGlExtensions(GL10 gl) {

		String extensions = gl.glGetString(GL10.GL_EXTENSIONS);

		if (extensions.contains("GL_OES_texture_npot")) {
			isGL_OES_texture_npot = true;
		}
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {

		// Let's check the available OpenGL Extensions:
		checkGlExtensions(gl);

		/*
		 * By default, OpenGL enables features that improve quality but reduce
		 * performance. One might want to tweak that especially on software
		 * renderer.
		 */
		gl.glDisable(GL10.GL_DITHER);

		gl.glEnable(GL10.GL_TEXTURE_2D); // Enable Texture Mapping ( NEW )
		gl.glShadeModel(GL10.GL_SMOOTH); // Enable Smooth Shading
		gl.glClearDepthf(1.0f); // Depth Buffer Setup
		gl.glEnable(GL10.GL_DEPTH_TEST); // Enables Depth Testing
		// gl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing To Do

		gl.glEnable(GL10.GL_BLEND); // Enable blending
		// GL10.GL_REPLACE;
		// gl.glDisable(GL10.GL_DEPTH_TEST); // Disable depth test
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		// gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE); // Set The Blending
		// Function For
		// Translucency

		gl.glEnable(GL10.GL_ALPHA_TEST);
		gl.glAlphaFunc(GL10.GL_NOTEQUAL, 0);

		/*
		 * Some one-time OpenGL initialization can be made here probably based
		 * on features of this particular context
		 */
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
		gl.glEnable(GL10.GL_CULL_FACE);

		// TODO What is the best choice?
		// Really Nice Perspective Calculations
		// gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

		// gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
		// GL10.GL_REPLACE);

		gl.glClearColor(0, 0, 0, 0);

		sTextureHolder.clear();
		Logger.d(TAG, "Loading textures...");
		loadWorldTextures(gl);
		loadAdditionalTextures(gl);
		Logger.d(TAG, "TEXTURES LOADED");

	}

	/**
	 * Override this method to load additional textures. Use the following code
	 * to help you:

	 * loadTexture(gl, yourBitmap)

	 * This method is called when the surface is created.
	 *
	 * @param gl
	 */
	protected void loadAdditionalTextures(GL10 gl) {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.loadAdditionalTextures(gl);
			}
		}
	}

	/**
	 * Load the world textures.
	 *
	 * @param gl
	 */
	private void loadWorldTextures(GL10 gl) {
		if (null != mWorld) {
			try {
				BeyondarObjectList list = null;
				for (int i = 0; i < mWorld.getBeyondarObjectLists().size(); i++) {
					list = mWorld.getBeyondarObjectLists().get(i);
					if (null != list) {
						Bitmap defaultBtm = mWorld.getBitmapCache().getBitmap(list.getDefaultImageUri());
						Texture texture = load2DTexture(gl, defaultBtm);
						list.setDefaultTexture(texture);

						for (int j = 0; j < list.size(); j++) {
							// loading texture
							loadBeyondarObjectTexture(gl, list.get(j));

						}
					}
				}
			} catch (ConcurrentModificationException e) {
				loadWorldTextures(gl);
			}
			mWorld.getBitmapCache().cleanRecylcedBitmaps();
		}

	}

	/**
	 * Load the textures of the specified geoObject.
	 *
	 * @param gl
	 * @param geoObject
	 *            The object to load the textures.
	 */
	public void loadBeyondarObjectTexture(GL10 gl, BeyondarObject geoObject) {

		Texture texture = getTexture(geoObject.getImageUri());

		if (texture == null) {
			Bitmap btm = mWorld.getBitmapCache().getBitmap(geoObject.getImageUri());

			texture = loadBitmapTexture(gl, btm, geoObject.getImageUri());

			if (texture == null || !texture.isLoaded()) {
				sPendingTextureObjects.addObject(geoObject.getImageUri(), geoObject);
			}
			if (btm == null) {
				if (Logger.DEBUG_OPENGL) {
					Logger.e(TAG, "ERROR: the resource " + geoObject.getImageUri()
							+ " has not been loaded. Object Name: " + geoObject.getName());
				}
			}
		}

		geoObject.setTexture(texture);
	}

	/**
	 * Load the {@link com.beyondar.android.opengl.texture.Texture Texture} from
	 * a Bitmap.
	 *
	 * @param gl
	 * @param btm
	 *            The Bitmap to load.
	 * @param uri
	 *            The unique id of the bitmap.
	 * @return The {@link com.beyondar.android.opengl.texture.Texture Texture}
	 *         object.
	 */
	public Texture loadBitmapTexture(GL10 gl, Bitmap btm, String uri) {

		if (null == btm) {
			return null;
		}
		Texture texture = null;
		// Check if the texture already exist
		texture = sTextureHolder.get(uri);
		if (texture == null) {
			texture = load2DTexture(gl, btm);

			sTextureHolder.put(uri, texture);
		}
		return texture.clone();
	}

	/**
	 * Check if the bitmap texture object is loaded. To check if the
	 * {@link com.beyondar.android.opengl.texture.Texture Texture} has been
	 * loaded with a pointer, use
	 * {@link com.beyondar.android.opengl.texture.Texture Texture.isLoaded()}
	 *
	 * @param bitmap
	 * @return true if it is already loaded, false otherwise.
	 */
	public boolean isTextureLoaded(Bitmap bitmap) {
		return sTextureHolder.containsValue(bitmap);
	}

	/**
	 * Check if the image URI has been loaded as a texture. To check if the
	 * {@link com.beyondar.android.opengl.texture.Texture Texture} has any GL
	 * texture pointer use {@link com.beyondar.android.opengl.texture.Texture
	 * Texture.isLoaded()}
	 *
	 * @param uri
	 * @return true if it is already loaded, false otherwise.
	 */
	public boolean isTextureObjectLoaded(String uri) {
		return sTextureHolder.get(uri) != null;
	}

	/**
	 * Get a copy of the texture pointer for the specified URI.
	 *
	 * @param uri
	 * @return
	 */
	public Texture getTexture(String uri) {
		if (uri == null) {
			return null;
		}
		Texture texture = sTextureHolder.get(uri);
		if (texture != null) {
			texture = texture.clone();
		}
		return texture;
	}

	/**
	 * Create the texture for the specified Bitmap.

	 * __Important__ The method will recycle the bitmap after being used.
	 *
	 * @param gl
	 * @param bitmap
	 * @return A new texture for the bitmap.
	 */
	public Texture load2DTexture(GL10 gl, Bitmap bitmap) {
		// see
		// http://stackoverflow.com/questions/3921685/issues-with-glutils-teximage2d-and-alpha-in-textures
		int[] tmpTexture = new int[1];
		if (null == bitmap) {
			return null;
		}
		int imageWidth = bitmap.getWidth();
		int imageHeight = bitmap.getHeight();

		if (!isGL_OES_texture_npot && !Utils.isCompatibleWithOpenGL(bitmap)) {
			Bitmap tmp = Utils.resizeImageToPowerOfTwo(bitmap);
			bitmap.recycle();
			bitmap = tmp;
		}

		// generate one texture pointer
		gl.glGenTextures(1, tmpTexture, 0);
		// ...and bind it to our array
		gl.glBindTexture(GL10.GL_TEXTURE_2D, tmpTexture[0]);

		// create nearest filtered texture
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

		// Different possible texture parameters, e.g.
		// GL10.GL_CLAMP_TO_EDGE
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
		//
		// Use Android GLUtils to specify a two-dimensional
		// texture image from our bitmap
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
		//
		// Clean up
		bitmap.recycle();
		return new Texture(tmpTexture[0]).setImageSize(imageWidth, imageHeight);

	}

	@Override
	public void onSensorChanged(float[] filteredValues, SensorEvent event) {
		if (!mRender) {
			return;
		}
		switch (event.sensor.getType()) {
		case Sensor.TYPE_ACCELEROMETER:
			mAccelerometerValues = filteredValues;
			break;
		case Sensor.TYPE_MAGNETIC_FIELD:
			mMagneticValues = filteredValues;
			break;
		default:
			break;
		}

	}

	// view port
	private final int[] viewport = new int[4];

	private void setupViewPort() {
		viewport[2] = mWidth;
		viewport[3] = mHeight;
	}

	/**
	 * Get a view {@link com.beyondar.android.util.math.geom.Ray Ray} for the
	 * screen position (x,y). Use this to object to check if there are any
	 * {@link com.beyondar.android.world.GeoObject GeoObject} that collide.
	 *
	 * @param x
	 * @param y
	 * @param ray
	 */
	public void getViewRay(float x, float y, Ray ray) {

		// far eye point
		float[] eye = mFloat4ArrayPool.poll();
		if (eye == null) {
			eye = new float[4];
		} else {
			eye[0] = eye[1] = eye[2] = eye[3] = 0;
		}
		GLU.gluUnProject(x, mHeight - y, 0.9f, mMatrixGrabber.mModelView, 0, mMatrixGrabber.mProjection, 0,
				viewport, 0, eye, 0);

		// fix
		if (eye[3] != 0) {
			eye[0] = eye[0] / eye[3];
			eye[1] = eye[1] / eye[3];
			eye[2] = eye[2] / eye[3];
		}

		// ray vector
		ray.setVector((eye[0] - mCameraPosition.x), (eye[1] - mCameraPosition.y),
				(eye[2] - mCameraPosition.z));
		mFloat4ArrayPool.add(eye);
	}

	/**
	 * Get the screen position of a given
	 * {@link com.beyondar.android.util.math.geom.Point3 Point3}.
	 *
	 * @param position
	 *            The 3D position to transform to a screen coordinates.
	 * @param outPoint
	 *            The object where the result will be stored.
	 */
	public void getScreenCoordinates(Point3 position, Point3 outPoint) {
		float[] eye = mFloat4ArrayPool.poll();
		if (eye == null) {
			eye = new float[4];
		}
		getScreenCoordinates(position.x, position.y, position.z, outPoint, mFloat4ArrayPool.poll());
		mFloat4ArrayPool.add(eye);
	}

	/**
	 * Get the screen position of a given
	 * {@link com.beyondar.android.util.math.geom.Point3 Point3}.
	 *
	 * @param position
	 *            The 3D position to transform to a screen coordinates.
	 * @param outPoint
	 *            The object where the result will be stored.
	 * @param eye
	 *            Array where the eye position will be stored to do the
	 *            calculations.
	 */
	public void getScreenCoordinates(Point3 position, Point3 outPoint, float[] eye) {
		getScreenCoordinates(position.x, position.y, position.z, outPoint, eye);
	}

	/**
	 * Get the screen position of a given
	 * {@link com.beyondar.android.util.math.geom.Point3 Point3}.
	 *
	 * @param x
	 *            The 3D position (x) to transform to a screen coordinates.
	 * @param y
	 *            The 3D position (y) to transform to a screen coordinates.
	 * @param z
	 *            The 3D position (z) to transform to a screen coordinates.
	 * @param outPoint
	 *            The object where the result will be stored.
	 * @param eye
	 *            Array where the eye position will be stored to do the
	 *            calculations.
	 */
	public void getScreenCoordinates(float x, float y, float z, Point3 outPoint, float[] eye) {
		// far eye point
		if (eye == null) {
			eye = new float[4];
		} else {
			eye[0] = eye[1] = eye[2] = eye[3] = 0;
		}

		GLU.gluProject(x, y, z, mMatrixGrabber.mModelView, 0, mMatrixGrabber.mProjection, 0, viewport, 0,
				eye, 0);

		// fix
		if (eye[3] != 0) {
			eye[0] = eye[0] / eye[3];
			eye[1] = eye[1] / eye[3];
			eye[2] = eye[2] / eye[3];
		}

		// Screen coordinates
		outPoint.x = eye[0];
		outPoint.y = mHeight - eye[1];
		outPoint.z = eye[2];
	}

	/**
	 * Specify if the {@link ARRenderer} should render the world.
	 *
	 * @param render
	 */
	public void setRendering(boolean render) {
		mRender = render;
	}

	/**
	 * Get known if the {@link ARRenderer} is rendering the world
	 *
	 * @return
	 */
	public boolean isRendering() {
		return mRender;
	}

	@Override
	public void onExternalBitmapLoaded(BitmapCache cache, String url, Bitmap btm) {
		UriAndBitmap uriAndBitmap = new UriAndBitmap();
		uriAndBitmap.uri = url;
		uriAndBitmap.btm = btm;
		sNewBitmapsLoaded.add(uriAndBitmap);
	}

	private synchronized void setAllTextures(GL10 gl, String uri, Bitmap btm,
			PendingBitmapsToBeLoaded<BeyondarObject> pendingList) {
		if (uri == null) {
			return;
		}
		ArrayList<BeyondarObject> list = pendingList.getPendingList(uri);
		if (list == null) {
			return;
		}

		Texture texture = loadBitmapTexture(gl, btm, uri);

		for (int i = 0; i < list.size() && texture.isLoaded(); i++) {
			BeyondarObject object = list.get(i);
			object.setTexture(texture);
		}
		pendingList.removePendingList(uri);
	}

	/**
	 * Use this method to fill all the screen positions of the
	 * {@link com.beyondar.android.world.BeyondarObject BeyondarObject} when a
	 * object is rendered. Remember that the information is filled when the
	 * object is rendered, so it is asynchronous.

	 *
	 * After this method is called you can use the following:

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionBottomLeft()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionBottomRight()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionTopLeft()}

	 * {@link com.beyondar.android.world.BeyondarObject
	 * BeyondarObject.getScreenPositionTopRight()}
	 *
	 * __Important__ Enabling this feature will reduce the FPS, use only when is
	 * needed.
	 *
	 * @param fill
	 *            Enable or disable this feature.
	 */
	public void forceFillBeyondarObjectPositions(boolean fill) {
		mFillPositions = fill;
	}

	public static interface FpsUpdatable {

		/**
		 * This method will get the frames per second rendered by the
		 * {@link ARRenderer}
		 *
		 * @param fps
		 *            The Frames per second rendered
		 */
		public void onFpsUpdate(float fps);
	}

	@Override
	public void addPlugin(GLPlugin plugin) {
		synchronized (lockPlugins) {
			if (!plugins.contains(plugin)) {
				plugins.add(plugin);
			}
		}
		plugin.setup(mWorld, this);
	}

	@Override
	public boolean removePlugin(GLPlugin plugin) {
		boolean removed = false;
		synchronized (lockPlugins) {
			removed = plugins.remove(plugin);
		}
		if (removed) {
			plugin.onDetached();
		}
		return removed;
	}

	@Override
	public void removeAllPlugins() {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				removePlugin(plugin);
			}
		}
	}

	@Override
	public GLPlugin getFirstPlugin(Class<? extends GLPlugin> pluginClass) {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				if (pluginClass.isInstance(plugin)) {
					return plugin;
				}
			}
		}
		return null;
	}

	@Override
	public boolean containsAnyPlugin(Class<? extends GLPlugin> pluginClass) {
		return getFirstPlugin(pluginClass) != null;
	}

	@Override
	public boolean containsPlugin(GLPlugin plugin) {
		synchronized (lockPlugins) {
			return plugins.contains(plugin);
		}
	}

	@Override
	public List<GLPlugin> getAllPlugins(Class<? extends GLPlugin> pluginClass, List<GLPlugin> result) {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				if (pluginClass.isInstance(plugin)) {
					result.add(plugin);
				}
			}
		}
		return result;
	}

	@Override
	public List<GLPlugin> getAllPugins(Class<? extends GLPlugin> pluginClass) {
		ArrayList<GLPlugin> result = new ArrayList<GLPlugin>(5);
		return getAllPlugins(pluginClass, result);
	}

	@Override
	public List<GLPlugin> getAllPlugins() {
		synchronized (lockPlugins) {
			return new ArrayList<GLPlugin>(plugins);
		}
	}

	/**
	 * Called when the application is paused.
	 */
	public void onPause() {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.onPause();
			}
		}
	}

	/**
	 * Called when the application is resumed.
	 */
	public void onResume() {
		synchronized (lockPlugins) {
			for (GLPlugin plugin : plugins) {
				plugin.onResume();
			}
		}
	}

	/**
	 * Set the distance (in meters) which the app will draw the objects.
	 *
	 * @param meters
	 */
	public void setMaxDistanceToRender(float meters) {
		mArViewDistance = meters;
	}

	/**
	 * Get the distance (in meters) which the AR view will draw the objects.
	 *
	 * @return meters
	 */
	public float getMaxDistanceToRender() {
		return mArViewDistance;
	}

	/**
	 * Set the distance factor for rendering all the objects. As bigger the
	 * factor the closer the objects.
	 *
	 * @param factor
	 *            number bigger than 0.
	 */
	public void setDistanceFactor(float factor) {
		if (factor <= 0)
			return;
		mDistanceFactor = factor;
	}

	/**
	 * Get the distance factor.
	 *
	 * @return Distance factor
	 */
	public float getDistanceFactor() {
		return mDistanceFactor;
	}
}

redpajama_set_name: RedPajamaGithub


text:
class Browser;
class PanelManager;
class PanelOverflowIndicator;
namespace ui {
class SlideAnimation;
}

// Manipulates all the panels that are placed on the left-most overflow area.
class OverflowPanelStrip : public PanelMouseWatcherObserver,
                           public ui::AnimationDelegate {
 public:
  typedef std::vector<Panel*> Panels;

  explicit OverflowPanelStrip(PanelManager* panel_manager);
  virtual ~OverflowPanelStrip();

  // Sets the display area of the overflow strip.
  // |display_area| is in screen coordinates.
  void SetDisplayArea(const gfx::Rect& display_area);

  // Adds a panel to the strip.
  void AddPanel(Panel* panel);

  // Returns |false| if the panel is not in the strip.
  bool Remove(Panel* panel);
  void RemoveAll();

  // Called when a panel's expansion state changes.
  void OnPanelExpansionStateChanged(Panel* panel);

  // Called when a panel is starting/stopping drawing an attention.
  void OnPanelAttentionStateChanged(Panel* panel);

  // Refreshes the layouts for all panels to reflect any possible changes.
  void Refresh();

  void OnFullScreenModeChanged(bool is_full_screen);

  int num_panels() const { return static_cast<int>(panels_.size()); }
  Panel* first_panel() const {
    return panels_.empty() ? NULL : panels_.front();
  }
  const Panels& panels() const { return panels_; }

#ifdef UNIT_TEST
  int current_display_width() const { return current_display_width_; }

  gfx::Rect display_area() const { return display_area_; }

  // This might return NULL.
  PanelOverflowIndicator* overflow_indicator() const {
    return overflow_indicator_.get();
  }

  void set_max_visible_panels(int max_visible_panels) {
    max_visible_panels_ = max_visible_panels;
  }

  void set_max_visible_panels_on_hover(int max_visible_panels_on_hover) {
    max_visible_panels_on_hover_ = max_visible_panels_on_hover;
  }
#endif

 private:
  // Overridden from PanelMouseWatcherObserver:
  virtual void OnMouseMove(const gfx::Point& mouse_position) OVERRIDE;

  // Overridden from AnimationDelegate:
  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
  virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;

  void UpdateCurrentWidth();

  void DoRefresh(size_t start_index, size_t end_index);

  // Used to update the overflow indicator.
  void UpdateMaxVisiblePanelsOnHover();
  void UpdateOverflowIndicatorCount();
  void UpdateOverflowIndicatorAttention();

  // Computes the layout of the |index| panel to fit into the area.
  // Empty bounds will be returned if this is not possible due to not enough
  // space.
  gfx::Rect ComputeLayout(size_t index,
                          const gfx::Size& iconified_size) const;

  // Used to pop up the titles for overflow panels when the mouse hovers over
  // the overflow area.
  bool ShouldShowOverflowTitles(const gfx::Point& mouse_position) const;
  void ShowOverflowTitles(bool show_overflow_titles);

  int max_visible_panels() const {
    return are_overflow_titles_shown_ ? max_visible_panels_on_hover_
                                      : max_visible_panels_;
  }

  // Weak pointer since PanelManager owns OverflowPanelStrip instance.
  PanelManager* panel_manager_;

  // The queue for storing all panels.
  Panels panels_;

  // The overflow area where panels are iconified due to insufficient space
 // in the panel strip.
  gfx::Rect display_area_;

  // Current width of the overflow area. It is the width of the panel in the
  // iconified state when the mouse does not hover over it, or the width of
  // the panel showing more info when the mouse hovers over it.
  int current_display_width_;

  // Maximium number of overflow panels allowed to be shown when the mouse
  // does not hover over the overflow area.
  int max_visible_panels_;

  // Maximium number of overflow panels allowed to be shown when the mouse
  // hovers over the overflow area and causes it to be expanded.
  int max_visible_panels_on_hover_;

  // Used to show "+N" indicator when number of overflow panels exceed
  // |max_visible_panels_|.
  scoped_ptr<PanelOverflowIndicator> overflow_indicator_;

  // For mouse hover-over effect.
  bool are_overflow_titles_shown_;
  scoped_ptr<ui::SlideAnimation> overflow_hover_animator_;
  int overflow_hover_animator_start_width_;
  int overflow_hover_animator_end_width_;

  // Invalid panel index.
  static const size_t kInvalidPanelIndex = static_cast<size_t>(-1);

  DISALLOW_COPY_AND_ASSIGN(OverflowPanelStrip);
};

#endif  // CHROME_BROWSER_UI_PANELS_OVERFLOW_PANEL_STRIP_H_


text:
package me.expdev.adminmode.events;

import me.expdev.adminmode.AdminMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;

/**
 * AdminMode - Created by Marius on 08.03.2016.
 */

public class InteractEntityEvent implements Listener {

    AdminMode plugin;

    public InteractEntityEvent(AdminMode instance){
        this.plugin = instance;
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onLogout(PlayerInteractAtEntityEvent event){
        Player p = event.getPlayer();
        if(!this.plugin.getAdminModeManager().containsPlayer(p.getUniqueId().toString()))return;
        if(event.getRightClicked() instanceof Player){
            if(p.getItemInHand().equals(this.plugin.getToolsManager().getViewInv())){
                this.plugin.getGuiManager().openInventoryTo(p, (Player)event.getRightClicked());
                return;
            }
        }
    }
}


text:
import { browser, element, by } from 'protractor';

export class AngularMylearningPage {
  navigateTo() {
    return browser.get('/');
  }

  getParagraphText() {
    return element(by.css('app-root h1')).getText();
  }
}


text:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using Census.Interfaces;

namespace Census.UmbracoObject
{
    public class Property : IUmbracoObject
    {
        public string Name { get { return "Document Type"; } }

        public List<string> BackofficePages
        {
            get { return new List<string>() {"/settings/editNodeTypeNew.aspx"}; }
        }

        DataTable IUmbracoObject.ToDataTable(object usages)
        {
            return ToDataTable(usages);
        }

        public static DataTable ToDataTable(object usages, int propertyId = 0)
        {
            var documentTypes = (IEnumerable<global::umbraco.cms.businesslogic.web.DocumentType>) usages;

            var dt = new DataTable();
            dt.Columns.Add("Name");
            dt.Columns.Add("Alias");
            dt.Columns.Add("Property");

            foreach (var documentType in documentTypes)
            {
                var row = dt.NewRow();
                row["Name"] = Helper.GenerateLink(documentType.Text, "settings", "/settings/editNodeTypeNew.aspx?id=" + documentType.Id, "settingMasterDataType.gif");
                row["Alias"] = documentType.Alias;
                if (propertyId != 0)
                {
                    var properties = documentType.PropertyTypes.Where(x => x.DataTypeDefinition.Id == propertyId);
                    if (properties.Any())
                        row["Property"] = string.Join("<br/>", properties.Select(p => p.Name));
                }
                dt.Rows.Add(row);
                row.AcceptChanges();
            }

            return dt;
        }

    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="io.github.mayunfei.draw_board.MainActivity">

    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <io.github.mayunfei.draw_board.PPTView
            android:id="@+id/ppt_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <io.github.mayunfei.draw_board.Whiteboard
            android:id="@+id/whiteboard"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </FrameLayout>

    <Button
        android:id="@+id/btn_change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换小屏" />

    <Button
        android:id="@+id/btn_drawline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="画线" />

    <Button
        android:id="@+id/btn_clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="橡皮" />
    <Button
        android:id="@+id/btn_change_ppt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换ppt" />

</LinearLayout>


text:
package edu.softserve.zoo.service;
import edu.softserve.zoo.exceptions.NotFoundException;
import edu.softserve.zoo.model.BaseEntity;

import java.util.List;

public interface Service<T extends BaseEntity> {

    /**
     * Returns domain object by id from persistent storage
     *
     * @param id identifier of required entity
     * @return entity with specified identifier.
     * @throws NotFoundException if entity not found with given id
     */
    T findOne(Long id);

    Long count();

    List<T> findAll();

    T save(T entity);

    T update(T entity);

    void delete(Long id);

}


text:
Angular jQuery Knob
===================

A angular directive of [jQuery Knob](https://github.com/aterrien/jQuery-Knob/blob/master/js/jquery.knob.js)

jQuery Knob is a

* canvas based ; no png or jpg sprites.
* touch, mouse and mousewheel, keyboard events implemented.
* downward compatible ; overloads an input element.

## Example

Check /app folder for examples.

## Options

Behaviors :

* min : min value | default=0.
* max : max value | default=100.
*  step : step size | default=1.
*  angleOffset : starting angle in degrees | default=0.
* angleArc : arc size in degrees | default=360.
* stopper : stop at min & max on keydown/mousewheel | default=true.
* readOnly : disable input and events | default=false.

UI :

* cursor : display mode "cursor", cursor size could be changed passing a numeric value to the option, * default width is used when passing boolean value "true" | default=gauge.
* thickness : gauge thickness.
* lineCap : gauge stroke endings. | default=butt, round=rounded line endings
* width : dial width.
* displayInput : default=true | false=hide input.
* displayPrevious : default=false | true=displays the previous value with transparency.
* fgColor : foreground color.
* inputColor : input value (number) color.
* font : font family.
* fontWeight : font weight.
* bgColor : background color.


text:
package com.asakusafw.runtime.io.text;

import java.io.Closeable;
import java.io.IOException;

import com.asakusafw.runtime.io.text.driver.FieldOutput;

/**
 * An abstract super interface which writing records and their fields.
 * @since 0.9.1
 */
public interface FieldWriter extends Closeable {

    /**
     * Puts a next field into the current writing record.
     * @param output the field contents
     * @throws IOException if I/O error was occurred while writing the next field
     */
    void putField(FieldOutput output) throws IOException;

    /**
     * Puts an end-of-record mark and starts writing the next record.
     * @throws IOException if I/O error was occurred while writing the end-of-record mark
     * @throws UnmappableOutputException if the current record contained some unmappable sequences
     */
    void putEndOfRecord() throws IOException;
}


text:
namespace {

// Create a basic mode for a 6x4 screen.
const drmModeModeInfo kDefaultMode =
    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};

class MockScreenManager : public ui::ScreenManager {
 public:
  MockScreenManager(ui::DriWrapper* dri,
                    ui::ScanoutSurfaceGenerator* surface_generator)
      : ScreenManager(dri, surface_generator), dri_(dri) {}

  virtual void ForceInitializationOfPrimaryDisplay() OVERRIDE {}

 private:
  ui::DriWrapper* dri_;

  DISALLOW_COPY_AND_ASSIGN(MockScreenManager);
};

}  // namespace

class ScreenManagerTest : public testing::Test {
 public:
  ScreenManagerTest() {}
  virtual ~ScreenManagerTest() {}

  virtual void SetUp() OVERRIDE {
    dri_.reset(new ui::MockDriWrapper(3));
    surface_generator_.reset(new ui::MockSurfaceGenerator(dri_.get()));
    screen_manager_.reset(new MockScreenManager(
        dri_.get(), surface_generator_.get()));
  }
  virtual void TearDown() OVERRIDE {
    screen_manager_.reset();
    dri_.reset();
  }

 protected:
  scoped_ptr<ui::MockDriWrapper> dri_;
  scoped_ptr<ui::MockSurfaceGenerator> surface_generator_;
  scoped_ptr<MockScreenManager> screen_manager_;

 private:
  DISALLOW_COPY_AND_ASSIGN(ScreenManagerTest);
};

TEST_F(ScreenManagerTest, CheckWithNoControllers) {
  EXPECT_FALSE(screen_manager_->GetDisplayController(1));
}

TEST_F(ScreenManagerTest, CheckWithValidController) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);
  base::WeakPtr<ui::HardwareDisplayController> controller =
      screen_manager_->GetDisplayController(1);

  EXPECT_TRUE(controller);
  EXPECT_EQ(1u, controller->crtc_id());
  EXPECT_EQ(2u, controller->connector_id());
}

TEST_F(ScreenManagerTest, CheckWithInvalidId) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);

  EXPECT_TRUE(screen_manager_->GetDisplayController(1));
  EXPECT_FALSE(screen_manager_->GetDisplayController(2));
}

TEST_F(ScreenManagerTest, CheckForSecondValidController) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);
  screen_manager_->ConfigureDisplayController(3, 4, kDefaultMode);

  EXPECT_TRUE(screen_manager_->GetDisplayController(1));
  EXPECT_TRUE(screen_manager_->GetDisplayController(2));
}

TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);
  base::WeakPtr<ui::HardwareDisplayController> controller =
      screen_manager_->GetDisplayController(1);

  EXPECT_TRUE(controller);
  screen_manager_->RemoveDisplayController(1, 2);
  EXPECT_FALSE(controller);
}

TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);

  EXPECT_TRUE(screen_manager_->GetDisplayController(1));
  EXPECT_FALSE(screen_manager_->GetDisplayController(2));
}

TEST_F(ScreenManagerTest, CheckChangingMode) {
  screen_manager_->ConfigureDisplayController(1, 2, kDefaultMode);
  drmModeModeInfo new_mode = kDefaultMode;
  new_mode.vdisplay = 10;
  screen_manager_->ConfigureDisplayController(1, 2, new_mode);

  EXPECT_TRUE(screen_manager_->GetDisplayController(1));
  EXPECT_FALSE(screen_manager_->GetDisplayController(2));
  drmModeModeInfo mode = screen_manager_->GetDisplayController(1)->get_mode();
  EXPECT_EQ(new_mode.vdisplay, mode.vdisplay);
  EXPECT_EQ(new_mode.hdisplay, mode.hdisplay);
}


text:
<link rel="import" href="../polymer/polymer.html">

<polymer-element name="lp-homepage-links" attributes="notitle">

  <template>

    <link rel="stylesheet" href="lp-homepage-links.css" />

Home

About

FAQ

Privacy

GitHub

  </template>

</polymer-element>