text: ////////////////////////////////////////////////////////////////////////////////
  /// DISCLAIMER
  ///
  /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
  /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
  ///
  /// Licensed under the Apache License, Version 2.0 (the "License");
  /// you may not use this file except in compliance with the License.
  /// You may obtain a copy of the License at
  ///
  ///     http://www.apache.org/licenses/LICENSE-2.0
  ///
  /// Unless required by applicable law or agreed to in writing, software
  /// distributed under the License is distributed on an "AS IS" BASIS,
  /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  /// See the License for the specific language governing permissions and
  /// limitations under the License.
  ///
  /// Copyright holder is ArangoDB GmbH, Cologne, Germany
  ///
  /// @author Dr. Frank Celler
  ////////////////////////////////////////////////////////////////////////////////
  
  #include "Basics/StaticStrings.h"
  #include "Basics/conversions.h"
  #include "V8/v8-conv.h"
  #include "VocBase/KeyGenerator.h"
  #include "v8-vocbaseprivate.h"
  
  using namespace arangodb;
  using namespace arangodb::basics;
  
  ////////////////////////////////////////////////////////////////////////////////
  /// @brief get the vocbase pointer from the current V8 context
  ////////////////////////////////////////////////////////////////////////////////
  
  TRI_vocbase_t& GetContextVocBase(v8::Isolate* isolate) {
    TRI_GET_GLOBALS();
  
    TRI_ASSERT(v8g->_vocbase != nullptr);
    TRI_ASSERT(!v8g->_vocbase->isDangling());
  
    return *static_cast<TRI_vocbase_t*>(v8g->_vocbase);
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  /// @brief checks if argument is a document identifier
  ////////////////////////////////////////////////////////////////////////////////
  
  static bool ParseDocumentHandle(v8::Handle<v8::Value> const arg, std::string& collectionName,
                                  std::unique_ptr<char[]>& key) {
    TRI_ASSERT(collectionName.empty());
  
    if (!arg->IsString()) {
      return false;
    }
  
    // the handle must always be an ASCII string. These is no need to normalize it
    // first
    v8::String::Utf8Value str(arg);
  
    if (*str == nullptr) {
      return false;
    }
  
    // collection name / document key
    size_t split;
    if (KeyGenerator::validateId(*str, str.length(), &split)) {
      collectionName = std::string(*str, split);
      auto const length = str.length() - split - 1;
      auto buffer = new char[length + 1];
      memcpy(buffer, *str + split + 1, length);
      buffer[length] = '\0';
      key.reset(buffer);
      return true;
    }
  
    // document key only
    if (KeyGenerator::validateKey(*str, str.length())) {
      auto const length = str.length();
      auto buffer = new char[length + 1];
      memcpy(buffer, *str, length);
      buffer[length] = '\0';
      key.reset(buffer);
  
      return true;
    }
  
    return false;
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  /// @brief parse document or document handle from a v8 value (string | object)
  /// Note that the builder must already be open with an object and that it
  /// will remain open afterwards!
  ////////////////////////////////////////////////////////////////////////////////
  
  bool ExtractDocumentHandle(v8::Isolate* isolate, v8::Handle<v8::Value> const val,
                             std::string& collectionName, VPackBuilder& builder,
                             bool includeRev) {
    // reset the collection identifier and the revision
    TRI_ASSERT(collectionName.empty());
  
    std::unique_ptr<char[]> key;
  
    // extract the document identifier and revision from a string
    if (val->IsString()) {
      bool res = ParseDocumentHandle(val, collectionName, key);
      if (res) {
        if (key.get() == nullptr) {
          return false;
        }
        builder.add(StaticStrings::KeyString,
                    VPackValue(reinterpret_cast<char*>(key.get())));
      }
      return res;
    }
  
    // extract the document identifier and revision from a document object
    if (val->IsObject()) {
      TRI_GET_GLOBALS();
  
      v8::Handle<v8::Object> obj = val->ToObject();
      TRI_GET_GLOBAL_STRING(_IdKey);
      TRI_GET_GLOBAL_STRING(_KeyKey);
      if (obj->HasRealNamedProperty(_IdKey)) {
        v8::Handle<v8::Value> didVal = obj->Get(_IdKey);
  
        if (!ParseDocumentHandle(didVal, collectionName, key)) {
          return false;
        }
      } else if (obj->HasRealNamedProperty(_KeyKey)) {
        v8::Handle<v8::Value> didVal = obj->Get(_KeyKey);
  
        if (!ParseDocumentHandle(didVal, collectionName, key)) {
          return false;
        }
      } else {
        return false;
      }
  
      if (key.get() == nullptr) {
        return false;
      }
      // If we get here we have a valid key
      builder.add(StaticStrings::KeyString, VPackValue(reinterpret_cast<char*>(key.get())));
  
      if (!includeRev) {
        return true;
      }
  
      TRI_GET_GLOBAL_STRING(_RevKey);
      if (!obj->HasRealNamedProperty(_RevKey)) {
        return true;
      }
      v8::Handle<v8::Value> revObj = obj->Get(_RevKey);
      if (!revObj->IsString()) {
        return true;
      }
      v8::String::Utf8Value str(revObj);
      bool isOld;
      uint64_t rid = TRI_StringToRid(*str, str.length(), isOld, false);
  
      if (rid == 0) {
        return false;
      }
      builder.add(StaticStrings::RevString, VPackValue(TRI_RidToString(rid)));
      return true;
    }
  
    // unknown value type. give up
    return false;
  }
  