<commit_before>/*
 * ZoteroCollectionsLocal.cpp
 *
 * Copyright (C) 2009-20 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ZoteroCollectionsLocal.hpp"

#include <boost/bind.hpp>
#include <boost/algorithm/algorithm.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/FileSerializer.hpp>
#include <core/Database.hpp>

#include <core/system/Process.hpp>

#include <r/RExec.hpp>

#include <session/prefs/UserState.hpp>
#include <session/SessionModuleContext.hpp>

#include "session-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace zotero {
namespace collections {

namespace {

SEXP createCacheSpecSEXP( ZoteroCollectionSpec cacheSpec, r::sexp::Protect* pProtect)
{
   std::vector<std::string> names;
   names.push_back(kName);
   names.push_back(kVersion);
   SEXP cacheSpecSEXP = r::sexp::createList(names, pProtect);
   r::sexp::setNamedListElement(cacheSpecSEXP, kName, cacheSpec.name);
   r::sexp::setNamedListElement(cacheSpecSEXP, kVersion, cacheSpec.version);
   return cacheSpecSEXP;
}

void testZoteroSQLite(std::string dataDir)
{
   // connect to sqlite
   std::string db = dataDir + "/zotero.sqlite";
   database::SqliteConnectionOptions options = { db };
   boost::shared_ptr<database::IConnection> pConnection;
   Error error = database::connect(options, &pConnection);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   database::Rowset rows;
   database::Query query = pConnection->query("select collectionName, version from collections");
   error = pConnection->execute(query, rows);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   for (database::RowsetIterator it = rows.begin(); it != rows.end(); ++it)
   {
      database::Row& row = *it;
      std::string name = row.get<std::string>("collectionName");
      int version = row.get<int>("version");
      std::cerr << name << " - " << version << std::endl;
   }
}

void getLocalLibrary(std::string key,
                     ZoteroCollectionSpec cacheSpec,
                     ZoteroCollectionsHandler handler)
{
   r::sexp::Protect protect;
   std::string libraryJsonStr;
   Error error = r::exec::RFunction(".rs.zoteroGetLibrary", key,
                                    createCacheSpecSEXP(cacheSpec, &protect)).call(&libraryJsonStr);
   if (error)
   {
      handler(error, std::vector<ZoteroCollection>());
   }
   else
   {
      json::Object libraryJson;
      error = libraryJson.parse(libraryJsonStr);
      if (error)
      {
         handler(error, std::vector<ZoteroCollection>());
      }
      else
      {
         ZoteroCollection collection;
         collection.name = libraryJson[kName].getString();
         collection.version = libraryJson[kVersion].getInt();
         collection.items = libraryJson[kItems].getArray();
         handler(Success(), std::vector<ZoteroCollection>{ collection });
      }
   }
}

void getLocalCollections(std::string key,
                         std::vector<std::string> collections,
                         ZoteroCollectionSpecs cacheSpecs,
                         ZoteroCollectionsHandler handler)
{
    json::Array cacheSpecsJson;
    std::transform(cacheSpecs.begin(), cacheSpecs.end(), std::back_inserter(cacheSpecsJson), [](ZoteroCollectionSpec spec) {
       json::Object specJson;
       specJson[kName] = spec.name;
       specJson[kVersion] = spec.version;
       return specJson;
    });

    r::sexp::Protect protect;
    std::string collectionJsonStr;
    Error error = r::exec::RFunction(".rs.zoteroGetCollections", key, collections, cacheSpecsJson)
                                     .call(&collectionJsonStr);
    if (error)
    {
       handler(error, std::vector<ZoteroCollection>());
    }
    else
    {
       json::Array collectionsJson;
       error = collectionsJson.parse(collectionJsonStr);
       if (error)
       {
          handler(error, std::vector<ZoteroCollection>());
       }
       else
       {
           ZoteroCollections collections;
           std::transform(collectionsJson.begin(), collectionsJson.end(), std::back_inserter(collections), [](json::Value json) {
              ZoteroCollection collection;
              json::Object collectionJson = json.getObject();
              collection.name = collectionJson[kName].getString();
              collection.version = collectionJson[kVersion].getInt();
              collection.items = collectionJson[kItems].getArray();
              return collection;
           });
           handler(Success(), collections);
       }
    }
}

FilePath userHomeDir()
{
   std::string homeEnv;
#ifdef _WIN32
   homeEnv = "USERPROFILE";
#else
   homeEnv = "HOME";
#endif
   return FilePath(string_utils::systemToUtf8(core::system::getenv(homeEnv)));
}

// https://www.zotero.org/support/kb/profile_directory
FilePath zoteroProfilesDir()
{
   FilePath homeDir = userHomeDir();
   std::string profilesDir;
#if defined(_WIN32)
   profilesDir = "AppData\\Roaming\\Zotero\\Zotero\\Profiles";
#elif defined(__APPLE__)
   profilesDir = "Library/Application Support/Zotero/Profiles";
#else
   profilesDir = ".zotero/zotero";
#endif
   return homeDir.completeChildPath(profilesDir);
}

// https://www.zotero.org/support/zotero_data
FilePath defaultZoteroDataDir()
{
   FilePath homeDir = userHomeDir();
   return homeDir.completeChildPath("Zotero");
}

FilePath detectZoteroDataDir()
{
   // we'll fall back to the default if we can't find another dir in the profile
   FilePath dataDir = defaultZoteroDataDir();

   // find the zotero profiles dir
   FilePath profilesDir = zoteroProfilesDir();
   if (profilesDir.exists())
   {
      // there will be one path in the directory
      std::vector<FilePath> children;
      Error error = profilesDir.getChildren(children);
      if (error)
         LOG_ERROR(error);
      if (children.size() > 0)
      {
         // there will be a single directory inside the profiles dir
         FilePath profileDir = children[0];
         FilePath prefsFile = profileDir.completeChildPath("prefs.js");
         if (prefsFile.exists())
         {
            // read the prefs.js file
            std::string prefs;
            error = core::readStringFromFile(prefsFile, &prefs);
            if (error)
               LOG_ERROR(error);

            // look for the zotero.dataDir pref
            boost::smatch match;
            boost::regex regex("user_pref\\(\"extensions.zotero.dataDir\",\\s*\"([^\"]+)\"\\);");
            if (boost::regex_search(prefs, match, regex))
            {
               // set dataDiroly if the path exists
               FilePath profileDataDir(match[1]);
               if (profileDataDir.exists())
                  dataDir = profileDataDir;
            }
         }
      }
   }

   // return the data dir
   return dataDir;
}

} // end anonymous namespace

bool localZoteroAvailable()
{
   // availability based on server vs. desktop
#ifdef RSTUDIO_SERVER
   bool local = false;
#else
   bool local = true;
#endif

   // however, also make it available in debug mode for local dev/test
#ifndef NDEBUG
   local = true;
#endif

   return local;
}

// Detect the Zotero data directory and return it if it exists
FilePath detectedZoteroDataDirectory()
{
   if (localZoteroAvailable())
   {
      FilePath dataDir = detectZoteroDataDir();
      if (dataDir.exists())
         return dataDir;
      else
         return FilePath();
   }
   else
   {
      return FilePath();
   }
}

// Returns the zoteroDataDirectory (if any). This will return a valid FilePath
// if the user has specified a zotero data dir in the preferences; OR if
// a zotero data dir was detected on the system. In the former case the
// path may not exist (and this should be logged as an error)
FilePath zoteroDataDirectory()
{
   std::string dataDir = prefs::userState().zoteroDataDir();
   if (!dataDir.empty())
      return module_context::resolveAliasedPath(dataDir);
   else
      return detectedZoteroDataDirectory();
}

ZoteroCollectionSource localCollections()
{
   ZoteroCollectionSource source;
   source.getLibrary = getLocalLibrary;
   source.getCollections = getLocalCollections;
   return source;
}

} // end namespace collections
} // end namespace zotero
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<commit_msg>some initial sqlite wrapper functions<commit_after>/*
 * ZoteroCollectionsLocal.cpp
 *
 * Copyright (C) 2009-20 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ZoteroCollectionsLocal.hpp"

#include <boost/bind.hpp>
#include <boost/algorithm/algorithm.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/FileSerializer.hpp>
#include <core/Database.hpp>

#include <core/system/Process.hpp>

#include <r/RExec.hpp>

#include <session/prefs/UserState.hpp>
#include <session/SessionModuleContext.hpp>

#include "session-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace zotero {
namespace collections {

namespace {

SEXP createCacheSpecSEXP( ZoteroCollectionSpec cacheSpec, r::sexp::Protect* pProtect)
{
   std::vector<std::string> names;
   names.push_back(kName);
   names.push_back(kVersion);
   SEXP cacheSpecSEXP = r::sexp::createList(names, pProtect);
   r::sexp::setNamedListElement(cacheSpecSEXP, kName, cacheSpec.name);
   r::sexp::setNamedListElement(cacheSpecSEXP, kVersion, cacheSpec.version);
   return cacheSpecSEXP;
}

void execQuery(boost::shared_ptr<database::IConnection> pConnection,
               const std::string& sql,
               boost::function<void(const database::Row&)> rowHandler)
{
   database::Rowset rows;
   database::Query query = pConnection->query(sql);
   Error error = pConnection->execute(query, rows);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   for (database::RowsetIterator it = rows.begin(); it != rows.end(); ++it)
   {
      const database::Row& row = *it;
      rowHandler(row);
   }
}

ZoteroCollectionSpecs getCollections(boost::shared_ptr<database::IConnection> pConnection)
{
   ZoteroCollectionSpecs specs;
   execQuery(pConnection, "select collectionName, version from collections", [&specs](const database::Row& row) {
      ZoteroCollectionSpec spec;
      spec.name = row.get<std::string>("collectionName");
      spec.version = row.get<int>("version");
      specs.push_back(spec);
   });
   return specs;
}

int getLibraryVersion(boost::shared_ptr<database::IConnection> pConnection)
{
   int version = 0;
   execQuery(pConnection, "SELECT MAX(version) AS version from items", [&version](const database::Row& row) {
      std::string versionStr = row.get<std::string>("version");
      version = safe_convert::stringTo<int>(versionStr, 0);
   });
   return version;
}

void testZoteroSQLite(std::string dataDir)
{
   // connect to sqlite
   std::string db = dataDir + "/zotero.sqlite";
   database::SqliteConnectionOptions options = { db };
   boost::shared_ptr<database::IConnection> pConnection;
   Error error = database::connect(options, &pConnection);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   std::cerr << "library: " << getLibraryVersion(pConnection) << std::endl;

   ZoteroCollectionSpecs specs = getCollections(pConnection);
   for (auto spec : specs)
      std::cerr << spec.name << ": " << spec.version << std::endl;

}

void getLocalLibrary(std::string key,
                     ZoteroCollectionSpec cacheSpec,
                     ZoteroCollectionsHandler handler)
{
   testZoteroSQLite(key);

   r::sexp::Protect protect;
   std::string libraryJsonStr;
   Error error = r::exec::RFunction(".rs.zoteroGetLibrary", key,
                                    createCacheSpecSEXP(cacheSpec, &protect)).call(&libraryJsonStr);
   if (error)
   {
      handler(error, std::vector<ZoteroCollection>());
   }
   else
   {
      json::Object libraryJson;
      error = libraryJson.parse(libraryJsonStr);
      if (error)
      {
         handler(error, std::vector<ZoteroCollection>());
      }
      else
      {
         ZoteroCollection collection;
         collection.name = libraryJson[kName].getString();
         collection.version = libraryJson[kVersion].getInt();
         collection.items = libraryJson[kItems].getArray();
         handler(Success(), std::vector<ZoteroCollection>{ collection });
      }
   }
}

void getLocalCollections(std::string key,
                         std::vector<std::string> collections,
                         ZoteroCollectionSpecs cacheSpecs,
                         ZoteroCollectionsHandler handler)
{
    json::Array cacheSpecsJson;
    std::transform(cacheSpecs.begin(), cacheSpecs.end(), std::back_inserter(cacheSpecsJson), [](ZoteroCollectionSpec spec) {
       json::Object specJson;
       specJson[kName] = spec.name;
       specJson[kVersion] = spec.version;
       return specJson;
    });

    r::sexp::Protect protect;
    std::string collectionJsonStr;
    Error error = r::exec::RFunction(".rs.zoteroGetCollections", key, collections, cacheSpecsJson)
                                     .call(&collectionJsonStr);
    if (error)
    {
       handler(error, std::vector<ZoteroCollection>());
    }
    else
    {
       json::Array collectionsJson;
       error = collectionsJson.parse(collectionJsonStr);
       if (error)
       {
          handler(error, std::vector<ZoteroCollection>());
       }
       else
       {
           ZoteroCollections collections;
           std::transform(collectionsJson.begin(), collectionsJson.end(), std::back_inserter(collections), [](json::Value json) {
              ZoteroCollection collection;
              json::Object collectionJson = json.getObject();
              collection.name = collectionJson[kName].getString();
              collection.version = collectionJson[kVersion].getInt();
              collection.items = collectionJson[kItems].getArray();
              return collection;
           });
           handler(Success(), collections);
       }
    }
}

FilePath userHomeDir()
{
   std::string homeEnv;
#ifdef _WIN32
   homeEnv = "USERPROFILE";
#else
   homeEnv = "HOME";
#endif
   return FilePath(string_utils::systemToUtf8(core::system::getenv(homeEnv)));
}

// https://www.zotero.org/support/kb/profile_directory
FilePath zoteroProfilesDir()
{
   FilePath homeDir = userHomeDir();
   std::string profilesDir;
#if defined(_WIN32)
   profilesDir = "AppData\\Roaming\\Zotero\\Zotero\\Profiles";
#elif defined(__APPLE__)
   profilesDir = "Library/Application Support/Zotero/Profiles";
#else
   profilesDir = ".zotero/zotero";
#endif
   return homeDir.completeChildPath(profilesDir);
}

// https://www.zotero.org/support/zotero_data
FilePath defaultZoteroDataDir()
{
   FilePath homeDir = userHomeDir();
   return homeDir.completeChildPath("Zotero");
}

FilePath detectZoteroDataDir()
{
   // we'll fall back to the default if we can't find another dir in the profile
   FilePath dataDir = defaultZoteroDataDir();

   // find the zotero profiles dir
   FilePath profilesDir = zoteroProfilesDir();
   if (profilesDir.exists())
   {
      // there will be one path in the directory
      std::vector<FilePath> children;
      Error error = profilesDir.getChildren(children);
      if (error)
         LOG_ERROR(error);
      if (children.size() > 0)
      {
         // there will be a single directory inside the profiles dir
         FilePath profileDir = children[0];
         FilePath prefsFile = profileDir.completeChildPath("prefs.js");
         if (prefsFile.exists())
         {
            // read the prefs.js file
            std::string prefs;
            error = core::readStringFromFile(prefsFile, &prefs);
            if (error)
               LOG_ERROR(error);

            // look for the zotero.dataDir pref
            boost::smatch match;
            boost::regex regex("user_pref\\(\"extensions.zotero.dataDir\",\\s*\"([^\"]+)\"\\);");
            if (boost::regex_search(prefs, match, regex))
            {
               // set dataDiroly if the path exists
               FilePath profileDataDir(match[1]);
               if (profileDataDir.exists())
                  dataDir = profileDataDir;
            }
         }
      }
   }

   // return the data dir
   return dataDir;
}

} // end anonymous namespace

bool localZoteroAvailable()
{
   // availability based on server vs. desktop
#ifdef RSTUDIO_SERVER
   bool local = false;
#else
   bool local = true;
#endif

   // however, also make it available in debug mode for local dev/test
#ifndef NDEBUG
   local = true;
#endif

   return local;
}

// Detect the Zotero data directory and return it if it exists
FilePath detectedZoteroDataDirectory()
{
   if (localZoteroAvailable())
   {
      FilePath dataDir = detectZoteroDataDir();
      if (dataDir.exists())
         return dataDir;
      else
         return FilePath();
   }
   else
   {
      return FilePath();
   }
}

// Returns the zoteroDataDirectory (if any). This will return a valid FilePath
// if the user has specified a zotero data dir in the preferences; OR if
// a zotero data dir was detected on the system. In the former case the
// path may not exist (and this should be logged as an error)
FilePath zoteroDataDirectory()
{
   std::string dataDir = prefs::userState().zoteroDataDir();
   if (!dataDir.empty())
      return module_context::resolveAliasedPath(dataDir);
   else
      return detectedZoteroDataDirectory();
}

ZoteroCollectionSource localCollections()
{
   ZoteroCollectionSource source;
   source.getLibrary = getLocalLibrary;
   source.getCollections = getLocalCollections;
   return source;
}

} // end namespace collections
} // end namespace zotero
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<|endoftext|>


<commit_before>// Copyright 2019 Google Inc.
//
// 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.

#include "google/cloud/spanner/client.h"
#include "google/cloud/spanner/database.h"
#include "google/cloud/spanner/database_admin_client.h"
#include "google/cloud/spanner/instance_admin_client.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/internal/random.h"
#include <functional>

namespace spanner = google::cloud::spanner;

std::function<void()> drop_database = [] {};

int main(int argc, char* argv[]) try {
  if (argc != 1) {
    std::string const cmd = argv[0];
    auto last_slash = std::string(argv[0]).find_last_of('/');
    std::cerr << "Usage: " << cmd.substr(last_slash + 1) << "\n";
    return 1;
  }

  auto project_id =
      google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value_or("");
  if (project_id.empty()) {
    throw std::runtime_error(
        "The GOOGLE_CLOUD_PROJECT environment variable should be set to a "
        "non-empty value");
  }

  // This program is used to test the libraries after they are installed. We
  // cannot use any of the functions in the testing support libraries as those
  // do not get installed.
  spanner::DatabaseAdminClient admin_client;

  auto generator = google::cloud::internal::MakeDefaultPRNG();

  auto instance_id = [&project_id, &generator] {
    spanner::InstanceAdminClient instance_admin{
        spanner::MakeInstanceAdminConnection()};
    std::vector<std::string> names;
    for (auto const& instance : instance_admin.ListInstances(project_id, {})) {
      if (!instance) throw std::runtime_error("Error reading instance list");
      auto full_name = instance->name();
      names.push_back(full_name.substr(full_name.rfind('/') + 1));
    }
    if (names.empty()) throw std::runtime_error("No instances in the project");

    return names[std::uniform_int_distribution<std::size_t>(
        0, names.size() - 1)(generator)];
  }();

  auto database_id =
      "db-" + google::cloud::internal::Sample(
                  generator, 20, "abcdefghijlkmnopqrstuvwxyz0123456789");

  spanner::Database const database(project_id, instance_id, database_id);
  std::cout << "Will run the test in database: " << database.FullName() << "\n";

  using google::cloud::future;
  using google::cloud::StatusOr;

  std::cout << "Creating database [" << database_id << "] " << std::flush;
  future<StatusOr<google::spanner::admin::database::v1::Database>>
      created_database =
          admin_client.CreateDatabase(database, {R"""(
                        CREATE TABLE Singers (
                                SingerId   INT64 NOT NULL,
                                FirstName  STRING(1024),
                                LastName   STRING(1024),
                                SingerInfo BYTES(MAX)
                        ) PRIMARY KEY (SingerId))""",
                                                 R"""(CREATE TABLE Albums (
                                SingerId     INT64 NOT NULL,
                                AlbumId      INT64 NOT NULL,
                                AlbumTitle   STRING(MAX)
                        ) PRIMARY KEY (SingerId, AlbumId),
                        INTERLEAVE IN PARENT Singers ON DELETE CASCADE)"""});

  for (;;) {
    auto status = created_database.wait_for(std::chrono::seconds(1));
    if (status == std::future_status::ready) break;
    std::cout << '.' << std::flush;
  }
  std::cout << " DONE\n";

  auto db = created_database.get();
  if (!db) throw std::runtime_error(db.status().message());

  drop_database = [admin_client, database]() mutable {
    auto drop = admin_client.DropDatabase(database);
    if (!drop.ok()) throw std::runtime_error(drop.message());
    std::cout << "Database dropped\n";
  };

  spanner::Client client(spanner::MakeConnection(database));

  auto rows =
      client.ExecuteQuery(spanner::SqlStatement("SELECT 'Hello World'"));

  for (auto const& row : spanner::StreamOf<std::tuple<std::string>>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << std::get<0>(*row) << "\n";
  }

  drop_database();
  return 0;
} catch (std::exception const& ex) {
  std::cerr << "Standard exception raised: " << ex.what() << "\n";
  drop_database();
  return 1;
}
<commit_msg>bug: skip non-testing instances in install test (googleapis/google-cloud-cpp-spanner#991)<commit_after>// Copyright 2019 Google Inc.
//
// 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.

#include "google/cloud/spanner/client.h"
#include "google/cloud/spanner/database.h"
#include "google/cloud/spanner/database_admin_client.h"
#include "google/cloud/spanner/instance_admin_client.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/internal/random.h"
#include <functional>

namespace spanner = google::cloud::spanner;

std::function<void()> drop_database = [] {};

int main(int argc, char* argv[]) try {
  if (argc != 1) {
    std::string const cmd = argv[0];
    auto last_slash = std::string(argv[0]).find_last_of('/');
    std::cerr << "Usage: " << cmd.substr(last_slash + 1) << "\n";
    return 1;
  }

  auto project_id =
      google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value_or("");
  if (project_id.empty()) {
    throw std::runtime_error(
        "The GOOGLE_CLOUD_PROJECT environment variable should be set to a "
        "non-empty value");
  }

  // This program is used to test the libraries after they are installed. We
  // cannot use any of the functions in the testing support libraries as those
  // do not get installed.
  spanner::DatabaseAdminClient admin_client;

  auto generator = google::cloud::internal::MakeDefaultPRNG();

  auto instance_id = [&project_id, &generator] {
    spanner::InstanceAdminClient instance_admin{
        spanner::MakeInstanceAdminConnection()};
    std::vector<std::string> names;
    // All the test instances in the projects used for integration tests start
    // with this prefix. The projects sometimes have other (often transient)
    // instances that we should not use.
    std::string const testing_prefix = "test-instance-";
    std::string const substr = "/instances/" + testing_prefix;
    for (auto const& instance : instance_admin.ListInstances(project_id, {})) {
      if (!instance) throw std::runtime_error("Error reading instance list");
      auto full_name = instance->name();
      // Skip non-testing instances.
      if (full_name.find(substr) == std::string::npos) continue;
      names.push_back(full_name.substr(full_name.rfind('/') + 1));
    }
    if (names.empty()) throw std::runtime_error("No instances in the project");

    return names[std::uniform_int_distribution<std::size_t>(
        0, names.size() - 1)(generator)];
  }();

  auto database_id =
      "db-" + google::cloud::internal::Sample(
                  generator, 20, "abcdefghijlkmnopqrstuvwxyz0123456789");

  spanner::Database const database(project_id, instance_id, database_id);
  std::cout << "Will run the test in database: " << database.FullName() << "\n";

  using google::cloud::future;
  using google::cloud::StatusOr;

  std::cout << "Creating database [" << database_id << "] " << std::flush;
  future<StatusOr<google::spanner::admin::database::v1::Database>>
      created_database =
          admin_client.CreateDatabase(database, {R"""(
                        CREATE TABLE Singers (
                                SingerId   INT64 NOT NULL,
                                FirstName  STRING(1024),
                                LastName   STRING(1024),
                                SingerInfo BYTES(MAX)
                        ) PRIMARY KEY (SingerId))""",
                                                 R"""(CREATE TABLE Albums (
                                SingerId     INT64 NOT NULL,
                                AlbumId      INT64 NOT NULL,
                                AlbumTitle   STRING(MAX)
                        ) PRIMARY KEY (SingerId, AlbumId),
                        INTERLEAVE IN PARENT Singers ON DELETE CASCADE)"""});

  for (;;) {
    auto status = created_database.wait_for(std::chrono::seconds(1));
    if (status == std::future_status::ready) break;
    std::cout << '.' << std::flush;
  }
  std::cout << " DONE\n";

  auto db = created_database.get();
  if (!db) throw std::runtime_error(db.status().message());

  drop_database = [admin_client, database]() mutable {
    auto drop = admin_client.DropDatabase(database);
    if (!drop.ok()) throw std::runtime_error(drop.message());
    std::cout << "Database dropped\n";
  };

  spanner::Client client(spanner::MakeConnection(database));

  auto rows =
      client.ExecuteQuery(spanner::SqlStatement("SELECT 'Hello World'"));

  for (auto const& row : spanner::StreamOf<std::tuple<std::string>>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << std::get<0>(*row) << "\n";
  }

  drop_database();
  return 0;
} catch (std::exception const& ex) {
  std::cerr << "Standard exception raised: " << ex.what() << "\n";
  drop_database();
  return 1;
}
<|endoftext|>


<commit_before>/*
 * ZoteroCollectionsLocal.cpp
 *
 * Copyright (C) 2009-20 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ZoteroCollectionsLocal.hpp"

#include <boost/bind.hpp>
#include <boost/algorithm/algorithm.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/system/Process.hpp>

#include <r/RExec.hpp>

#include <session/prefs/UserState.hpp>
#include <session/SessionModuleContext.hpp>

#include "session-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace zotero {
namespace collections {

namespace {

SEXP createCacheSpecSEXP( ZoteroCollectionSpec cacheSpec, r::sexp::Protect* pProtect)
{
   std::vector<std::string> names;
   names.push_back(kName);
   names.push_back(kVersion);
   SEXP cacheSpecSEXP = r::sexp::createList(names, pProtect);
   r::sexp::setNamedListElement(cacheSpecSEXP, kName, cacheSpec.name);
   r::sexp::setNamedListElement(cacheSpecSEXP, kVersion, cacheSpec.version);
   return cacheSpecSEXP;
}

void getLocalLibrary(std::string key,
                     ZoteroCollectionSpec cacheSpec,
                     ZoteroCollectionsHandler handler)
{
   r::sexp::Protect protect;
   std::string libraryJsonStr;
   Error error = r::exec::RFunction(".rs.zoteroGetLibrary", key,
                                    createCacheSpecSEXP(cacheSpec, &protect)).call(&libraryJsonStr);
   if (error)
   {
      handler(error, std::vector<ZoteroCollection>());
   }
   else
   {
      json::Object libraryJson;
      error = libraryJson.parse(libraryJsonStr);
      if (error)
      {
         handler(error, std::vector<ZoteroCollection>());
      }
      else
      {
         ZoteroCollection collection;
         collection.name = libraryJson[kName].getString();
         collection.version = libraryJson[kVersion].getInt();
         collection.items = libraryJson[kItems].getArray();
         handler(Success(), std::vector<ZoteroCollection>{ collection });
      }
   }
}

void getLocalCollections(std::string key,
                         std::vector<std::string> collections,
                         ZoteroCollectionSpecs cacheSpecs,
                         ZoteroCollectionsHandler handler)
{
    json::Array cacheSpecsJson;
    std::transform(cacheSpecs.begin(), cacheSpecs.end(), std::back_inserter(cacheSpecsJson), [](ZoteroCollectionSpec spec) {
       json::Object specJson;
       specJson[kName] = spec.name;
       specJson[kVersion] = spec.version;
       return specJson;
    });

    r::sexp::Protect protect;
    std::string collectionJsonStr;
    Error error = r::exec::RFunction(".rs.zoteroGetCollections", key, collections, cacheSpecsJson)
                                     .call(&collectionJsonStr);
    if (error)
    {
       handler(error, std::vector<ZoteroCollection>());
    }
    else
    {
       json::Array collectionsJson;
       error = collectionsJson.parse(collectionJsonStr);
       if (error)
       {
          handler(error, std::vector<ZoteroCollection>());
       }
       else
       {
           ZoteroCollections collections;
           std::transform(collectionsJson.begin(), collectionsJson.end(), std::back_inserter(collections), [](json::Value json) {
              ZoteroCollection collection;
              json::Object collectionJson = json.getObject();
              collection.name = collectionJson[kName].getString();
              collection.version = collectionJson[kVersion].getInt();
              collection.items = collectionJson[kItems].getArray();
              return collection;
           });
           handler(Success(), collections);
       }
    }
}

} // end anonymous namespace

bool localZoteroAvailable()
{
   // availability based on server vs. desktop
#ifdef RSTUDIO_SERVER
   bool local = false;
#else
   bool local = true;
#endif

   // however, also make it available in debug mode for local dev/test
#ifndef NDEBUG
   local = true;
#endif

   return local;
}

// Returns the zoteroDataDirectory (if any). This will return a valid FilePath
// if the user has specified a zotero data dir in the preferences; OR if
// a zotero data dir was detected on the system. In the former case the
// path may not exist (and this should be logged as an error)
FilePath zoteroDataDirectory()
{
   std::string dataDir = prefs::userState().zoteroDataDir();
   if (!dataDir.empty())
      return module_context::resolveAliasedPath(dataDir);
   else
      return detectedZoteroDataDirectory();
}

// Automatically detect the Zotero data directory and return it if it exists
FilePath detectedZoteroDataDirectory()
{
   if (localZoteroAvailable())
   {
      std::string homeEnv;
   #ifdef _WIN32
      homeEnv = "USERPROFILE";
   #else
      homeEnv = "HOME";
   #endif
      FilePath homeDir = FilePath(string_utils::systemToUtf8(core::system::getenv(homeEnv)));
      FilePath zoteroPath = homeDir.completeChildPath("Zotero");
      if (zoteroPath.exists())
         return zoteroPath;
      else
         return FilePath();
   }
   else
   {
      return FilePath();
   }
}

ZoteroCollectionSource localCollections()
{
   ZoteroCollectionSource source;
   source.getLibrary = getLocalLibrary;
   source.getCollections = getLocalCollections;
   return source;
}

} // end namespace collections
} // end namespace zotero
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<commit_msg>detect zotero data dir based on user prefs (w/ default location as fallback)<commit_after>/*
 * ZoteroCollectionsLocal.cpp
 *
 * Copyright (C) 2009-20 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ZoteroCollectionsLocal.hpp"

#include <boost/bind.hpp>
#include <boost/algorithm/algorithm.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <shared_core/Error.hpp>
#include <shared_core/json/Json.hpp>

#include <core/FileSerializer.hpp>

#include <core/system/Process.hpp>

#include <r/RExec.hpp>

#include <session/prefs/UserState.hpp>
#include <session/SessionModuleContext.hpp>

#include "session-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace zotero {
namespace collections {

namespace {

SEXP createCacheSpecSEXP( ZoteroCollectionSpec cacheSpec, r::sexp::Protect* pProtect)
{
   std::vector<std::string> names;
   names.push_back(kName);
   names.push_back(kVersion);
   SEXP cacheSpecSEXP = r::sexp::createList(names, pProtect);
   r::sexp::setNamedListElement(cacheSpecSEXP, kName, cacheSpec.name);
   r::sexp::setNamedListElement(cacheSpecSEXP, kVersion, cacheSpec.version);
   return cacheSpecSEXP;
}

void getLocalLibrary(std::string key,
                     ZoteroCollectionSpec cacheSpec,
                     ZoteroCollectionsHandler handler)
{
   r::sexp::Protect protect;
   std::string libraryJsonStr;
   Error error = r::exec::RFunction(".rs.zoteroGetLibrary", key,
                                    createCacheSpecSEXP(cacheSpec, &protect)).call(&libraryJsonStr);
   if (error)
   {
      handler(error, std::vector<ZoteroCollection>());
   }
   else
   {
      json::Object libraryJson;
      error = libraryJson.parse(libraryJsonStr);
      if (error)
      {
         handler(error, std::vector<ZoteroCollection>());
      }
      else
      {
         ZoteroCollection collection;
         collection.name = libraryJson[kName].getString();
         collection.version = libraryJson[kVersion].getInt();
         collection.items = libraryJson[kItems].getArray();
         handler(Success(), std::vector<ZoteroCollection>{ collection });
      }
   }
}

void getLocalCollections(std::string key,
                         std::vector<std::string> collections,
                         ZoteroCollectionSpecs cacheSpecs,
                         ZoteroCollectionsHandler handler)
{
    json::Array cacheSpecsJson;
    std::transform(cacheSpecs.begin(), cacheSpecs.end(), std::back_inserter(cacheSpecsJson), [](ZoteroCollectionSpec spec) {
       json::Object specJson;
       specJson[kName] = spec.name;
       specJson[kVersion] = spec.version;
       return specJson;
    });

    r::sexp::Protect protect;
    std::string collectionJsonStr;
    Error error = r::exec::RFunction(".rs.zoteroGetCollections", key, collections, cacheSpecsJson)
                                     .call(&collectionJsonStr);
    if (error)
    {
       handler(error, std::vector<ZoteroCollection>());
    }
    else
    {
       json::Array collectionsJson;
       error = collectionsJson.parse(collectionJsonStr);
       if (error)
       {
          handler(error, std::vector<ZoteroCollection>());
       }
       else
       {
           ZoteroCollections collections;
           std::transform(collectionsJson.begin(), collectionsJson.end(), std::back_inserter(collections), [](json::Value json) {
              ZoteroCollection collection;
              json::Object collectionJson = json.getObject();
              collection.name = collectionJson[kName].getString();
              collection.version = collectionJson[kVersion].getInt();
              collection.items = collectionJson[kItems].getArray();
              return collection;
           });
           handler(Success(), collections);
       }
    }
}

FilePath userHomeDir()
{
   std::string homeEnv;
#ifdef _WIN32
   homeEnv = "USERPROFILE";
#else
   homeEnv = "HOME";
#endif
   return FilePath(string_utils::systemToUtf8(core::system::getenv(homeEnv)));
}

// https://www.zotero.org/support/kb/profile_directory
FilePath zoteroProfilesDir()
{
   FilePath homeDir = userHomeDir();
   std::string profilesDir;
#if defined(_WIN32)
   profilesDir = "AppData\\Roaming\\Zotero\\Zotero\\Profiles";
#elif defined(__APPLE__)
   profilesDir = "Library/Application Support/Zotero/Profiles";
#else
   profilesDir = ".zotero/zotero";
#endif
   return homeDir.completeChildPath(profilesDir);
}

// https://www.zotero.org/support/zotero_data
FilePath defaultZoteroDataDir()
{
   FilePath homeDir = userHomeDir();
   return homeDir.completeChildPath("Zotero");
}

FilePath detectZoteroDataDir()
{
   // we'll fall back to the default if we can't find another dir in the profile
   FilePath dataDir = defaultZoteroDataDir();

   // find the zotero profiles dir
   FilePath profilesDir = zoteroProfilesDir();
   if (profilesDir.exists())
   {
      // there will be one path in the directory
      std::vector<FilePath> children;
      Error error = profilesDir.getChildren(children);
      if (error)
         LOG_ERROR(error);
      if (children.size() > 0)
      {
         // there will be a single directory inside the profiles dir
         FilePath profileDir = children[0];
         FilePath prefsFile = profileDir.completeChildPath("prefs.js");
         if (prefsFile.exists())
         {
            // read the prefs.js file
            std::string prefs;
            error = core::readStringFromFile(prefsFile, &prefs);
            if (error)
               LOG_ERROR(error);

            // look for the zotero.dataDir pref
            boost::smatch match;
            boost::regex regex("user_pref\\(\"extensions.zotero.dataDir\",\\s*\"([^\"]+)\"\\);");
            if (boost::regex_search(prefs, match, regex))
            {
               // set dataDiroly if the path exists
               FilePath profileDataDir(match[1]);
               if (profileDataDir.exists())
                  dataDir = profileDataDir;
            }
         }
      }
   }

   // return the data dir
   return dataDir;
}

} // end anonymous namespace

bool localZoteroAvailable()
{
   // availability based on server vs. desktop
#ifdef RSTUDIO_SERVER
   bool local = false;
#else
   bool local = true;
#endif

   // however, also make it available in debug mode for local dev/test
#ifndef NDEBUG
   local = true;
#endif

   return local;
}

// Detect the Zotero data directory and return it if it exists
FilePath detectedZoteroDataDirectory()
{
   if (localZoteroAvailable())
   {
      FilePath dataDir = detectZoteroDataDir();
      if (dataDir.exists())
         return dataDir;
      else
         return FilePath();
   }
   else
   {
      return FilePath();
   }
}

// Returns the zoteroDataDirectory (if any). This will return a valid FilePath
// if the user has specified a zotero data dir in the preferences; OR if
// a zotero data dir was detected on the system. In the former case the
// path may not exist (and this should be logged as an error)
FilePath zoteroDataDirectory()
{
   std::string dataDir = prefs::userState().zoteroDataDir();
   if (!dataDir.empty())
      return module_context::resolveAliasedPath(dataDir);
   else
      return detectedZoteroDataDirectory();
}

ZoteroCollectionSource localCollections()
{
   ZoteroCollectionSource source;
   source.getLibrary = getLocalLibrary;
   source.getCollections = getLocalCollections;
   return source;
}

} // end namespace collections
} // end namespace zotero
} // end namespace modules
} // end namespace session
} // end namespace rstudio
<|endoftext|>


<commit_before>#ifndef VIENNAGRID_ALGORITHM_GEOMETRY_HPP
#define VIENNAGRID_ALGORITHM_GEOMETRY_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaGrid - The Vienna Grid Library
                            -----------------

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <limits>
#include "viennagrid/mesh/mesh.hpp"
#include "viennagrid/algorithm/cross_prod.hpp"
#include "viennagrid/algorithm/detail/numeric.hpp"
#include "viennagrid/algorithm/intersect.hpp"

/** @file viennagrid/algorithm/geometry.hpp
    @brief Contains various functions for computing geometric quantities
*/

namespace viennagrid
{

  template<typename ElementT, typename PointAccessorT>
  typename PointAccessorT::value_type normal_vector( PointAccessorT const point_accessor, ElementT const & element )
  {
    typedef typename PointAccessorT::value_type    point_type;

    point_type const & p0 = point_accessor( viennagrid::vertices(element)[0] );
    point_type const & p1 = point_accessor( viennagrid::vertices(element)[1] );
    point_type const & p2 = point_accessor( viennagrid::vertices(element)[2] );

    return viennagrid::cross_prod( p1-p0, p2-p0 );
  }

  template<typename ElementT>
  typename viennagrid::result_of::point<ElementT>::type normal_vector( ElementT const & element )
  {
    return normal_vector( default_point_accessor(element), element );
  }

  //namespace geometry
  //{

    template<typename PointT>
    typename viennagrid::result_of::coord<PointT>::type determinant( PointT const & p0, PointT const & p1, PointT const & p2 )
    {
      return p0[0]*p1[1]*p2[2] + p1[0]*p2[1]*p0[2] + p2[0]*p0[1]*p1[2] - p0[2]*p1[1]*p2[0] - p1[2]*p2[1]*p0[0] - p2[2]*p0[1]*p1[0];
    }

    template<typename PointIteratorT>
    std::pair<
        typename std::iterator_traits<PointIteratorT>::value_type,
        typename std::iterator_traits<PointIteratorT>::value_type
    > bounding_box( PointIteratorT it, PointIteratorT const & it_end )
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type   PointType;
      typedef typename viennagrid::result_of::coord<PointType>::type      NumericType;

      PointType lower_left;
      PointType upper_right;

      std::fill( lower_left.begin(), lower_left.end(), std::numeric_limits<NumericType>::max() );
      std::fill( upper_right.begin(), upper_right.end(), - std::numeric_limits<NumericType>::max() );
      //std::fill( upper_right.begin(), upper_right.end(), std::numeric_limits<NumericType>::lowest() );    C++11

      for (; it != it_end; ++it )
      {
        lower_left = viennagrid::min( lower_left, *it );
        upper_right = viennagrid::max( upper_right, *it );
      }

      return std::make_pair( lower_left, upper_right );
    }

    template<typename IteratorT>
    IteratorT circular_next(IteratorT it, IteratorT const & start_it, IteratorT const & end_it)
    {
      if (++it == end_it)
        return start_it;
      else
        return it;
    }

    template<typename IteratorT>
    IteratorT circular_prev(IteratorT it, IteratorT const & start_it, IteratorT const & end_it)
    {
      if (it == start_it)
        it = end_it;

      return --it;
    }

//         template<typename point_iterator_type, typename polygon_tag_type, typename point, typename numeric_config>
//         bool is_inside( point_iterator_type const & it_start, point_iterator_type it_end, polygon_tag_type polygon_tag,
//                         point const & point_to_test,
//                         numeric_config nc)
//         {
//             typedef typename std::iterator_traits<point_iterator_type>::value_type PolygonPointType;
//             std::pair<PolygonPointType, PolygonPointType> poly_bounding_box = bounding_box(it_start, it_end);
//
//             PolygonPointType outer_point;
//             outer_point[0] = point_to_test[0];
//             outer_point[1] = poly_bounding_box.first[1] - 1;
//             bool is_inside = false;
//
//             point_iterator_type it_prev = it_end; --it_prev;
//             point_iterator_type it_cur = it_start;
//
//             for ( ;it_cur != it_end ; ++it_cur, it_prev = circular_next(it_prev, it_start, it_end) )
//             {
//                 PolygonPointType const & q0 = *it_prev;
//                 PolygonPointType const & q1 = *it_cur;
//
//                 // is inner point on polygon line?
//                 if ( point_line_intersect( point_to_test, q0, q1, interval::closed_open_tag(), nc ) )
//                     return !is_open(polygon_tag);
//
//
//                 // is current line on test line?
//                 if ( !detail::is_equal(nc, q0[0], point_to_test[0]) || !detail::is_equal(nc, q1[0], point_to_test[0]) )
//                 {
//                     if ( line_line_intersect( q0, q1, interval::open_open_tag(), point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                     {
//                         is_inside = !is_inside;
//                     }
//                 }
//             }
//
//
//             // find point which is not on the testing line
//             point_iterator_type it = it_start;
//             while ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//             {
//                 it = circular_prev(it, it_start, it_end);
//                 if (it == it_start)
//                     break;
//             }
//
//             // no point found -> no intersection
//             if ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                 return false;
//
//
//             point_iterator_type circular_start_it = it;
//             it_prev = it;
//             it = circular_next(it, it_start, it_end);
//
//             // iterating over all points
//             while (it != circular_start_it)
//             {
//                 // is point on testing line?
//                 if ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                 {
//                     // find next point which is not on testing line
//                     point_iterator_type it_next = circular_next(it, it_start, it_end);
//                     while ( point_line_intersect( *it_next, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                         it_next = circular_next(it_next, it_start, it_end);
//
//                     // check if the the lines/points are an ear
//                     if ( ((*it_prev)[0] - (*it)[0]) * ((*it_next)[0] - (*it)[0]) < 0 )
//                     {
//                         is_inside = !is_inside;
//                     }
//
//                     it_prev = it;
//                     it = it_next;
//                 }
//                 else
//                 {
//                     it_prev = it;
//                     it = circular_next(it, it_start, it_end);
//                 }
//             }
//
//             return is_inside;
//         }

    template<typename IteratorT, typename VectorT>
    VectorT orthogonalize_one_vector( IteratorT it, const IteratorT & end, VectorT vec )
    {
      for (; it != end; ++it)
        vec -= viennagrid::inner_prod( vec, *it ) / viennagrid::inner_prod( *it, *it ) * (*it);

      return vec;
    }

    template<typename IteratorT, typename NumericConfigT>
    bool orthogonalize( IteratorT start, IteratorT end, NumericConfigT nc )
    {
      typedef typename std::iterator_traits<IteratorT>::value_type      vector_type;
      typedef typename viennagrid::result_of::coord<vector_type>::type  coord_type;

      for (IteratorT n = start; n != end; ++n)
      {
        *n = orthogonalize_one_vector(start, n, *n);

        if ( viennagrid::norm_1(*n) < detail::absolute_tolerance<coord_type>(nc) )
          return false;
      }

      return true;
    }

    template<typename PointIteratorT, typename NumericConfigT, typename OutPointT, typename OutPointIteratorT>
    bool projection_matrix(const PointIteratorT & start, const PointIteratorT & end, NumericConfigT nc,
                           OutPointT & center, OutPointIteratorT projection_matrix_start)
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type                    point_type;
      typedef typename viennagrid::result_of::coord<point_type>::type                      coord_type;
      typedef typename detail::result_of::numeric_type<NumericConfigT, coord_type>::type  numeric_type;

      OutPointIteratorT projection_matrix_end = projection_matrix_start;
      ++projection_matrix_end; ++projection_matrix_end;

      std::fill( projection_matrix_start, projection_matrix_end, point_type() );

      if (start == end) return false; // too few points

      PointIteratorT pit;

      // calculating the center
      pit = start;
      unsigned int num_points = 1;
      center = *pit; ++pit;

      for (; pit != end; ++pit)
      {
        center += *pit;
        ++num_points;
      }

      if (num_points < 3) return false; // too few points
      center /= num_points;

      // setting up a map of vectors from the center to all points, sorted descending by the length of that vector
      typedef std::multimap<numeric_type, point_type, std::greater<numeric_type> > vector_map_type;
      vector_map_type sorted_vectors;
      pit = start;
      for (; pit != end; ++pit)
      {
        point_type vector = center - *pit;
        typename vector_map_type::iterator it = sorted_vectors.insert( std::make_pair( viennagrid::norm_2( vector ), vector ) );
        it->second /= it->first; // normalizing the vector
      }

      // finding 2 non-liner dependent vectors
      unsigned int projection_matrix_index = 0;
      typename vector_map_type::iterator it = sorted_vectors.begin();
      while (projection_matrix_index < 2)
      {
        if ( it->first < detail::absolute_tolerance<coord_type>(nc) )
          return false; // points are too close together

        // check linear dependency with other vectors in projection matrix
        unsigned int index = 0;
        OutPointIteratorT pmit = projection_matrix_start;
        for (; index < projection_matrix_index; ++index, ++pmit)
        {
          numeric_type angle_cos = viennagrid::inner_prod( it->second, *pmit );
          if ( std::abs(angle_cos) > 1 - detail::absolute_tolerance<coord_type>(nc))
            break;
        }

        if ( index == projection_matrix_index)
        {
          *pmit = it->second;
          ++projection_matrix_index;
        }

        ++it;
      }

      // orthogonalize vectors
      orthogonalize( projection_matrix_start, projection_matrix_end, nc );

      // normalize vectors
      for (OutPointIteratorT it = projection_matrix_start; it != projection_matrix_end; ++it)
        *it /= viennagrid::norm_2(*it);

      return true;
    }

    template<typename PointIteratorT, typename OutPointIteratorT, typename point_type, typename ProjectionPointIteratorT>
    void project(PointIteratorT in, const PointIteratorT & in_end,
                OutPointIteratorT out,
                point_type center,
                ProjectionPointIteratorT const & proj_start, ProjectionPointIteratorT const & proj_end)
    {
      for ( ; in != in_end; ++in, ++out)
      {
        point_type tmp = *in - center;

        size_t index = 0;
        for (ProjectionPointIteratorT it = proj_start; it != proj_end; ++it, ++index)
          (*out)[index] = viennagrid::inner_prod( tmp, *it );
      }
    }

    template<typename PointIteratorT, typename OutPointIteratorT, typename NumericConfigT>
    bool project_onto_2dplane(PointIteratorT in, const PointIteratorT & in_end,
                              OutPointIteratorT out,
                              NumericConfigT nc)
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type       InPointType;

      size_t dimension = (*in).size();
      std::vector<InPointType> projection_matrix(dimension);
      InPointType center;

      if (!projection_matrix(in, in_end, projection_matrix.begin(), center, nc))
        return false;

      project(in, in_end, out, center, projection_matrix.begin(), projection_matrix.begin() + 2);
    }

  //} // namespace geometry

}

#endif

<commit_msg>added determinant for 2 points<commit_after>#ifndef VIENNAGRID_ALGORITHM_GEOMETRY_HPP
#define VIENNAGRID_ALGORITHM_GEOMETRY_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaGrid - The Vienna Grid Library
                            -----------------

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <limits>
#include "viennagrid/mesh/mesh.hpp"
#include "viennagrid/algorithm/cross_prod.hpp"
#include "viennagrid/algorithm/detail/numeric.hpp"
#include "viennagrid/algorithm/intersect.hpp"

/** @file viennagrid/algorithm/geometry.hpp
    @brief Contains various functions for computing geometric quantities
*/

namespace viennagrid
{

  template<typename ElementT, typename PointAccessorT>
  typename PointAccessorT::value_type normal_vector( PointAccessorT const point_accessor, ElementT const & element )
  {
    typedef typename PointAccessorT::value_type    point_type;

    point_type const & p0 = point_accessor( viennagrid::vertices(element)[0] );
    point_type const & p1 = point_accessor( viennagrid::vertices(element)[1] );
    point_type const & p2 = point_accessor( viennagrid::vertices(element)[2] );

    return viennagrid::cross_prod( p1-p0, p2-p0 );
  }

  template<typename ElementT>
  typename viennagrid::result_of::point<ElementT>::type normal_vector( ElementT const & element )
  {
    return normal_vector( default_point_accessor(element), element );
  }

  //namespace geometry
  //{

    template<typename PointT>
    typename viennagrid::result_of::coord<PointT>::type determinant( PointT const & p0, PointT const & p1 )
    {
      return p0[0]*p1[1] - p0[1]*p1[0];
    }

    template<typename PointT>
    typename viennagrid::result_of::coord<PointT>::type determinant( PointT const & p0, PointT const & p1, PointT const & p2 )
    {
      return p0[0]*p1[1]*p2[2] + p1[0]*p2[1]*p0[2] + p2[0]*p0[1]*p1[2] - p0[2]*p1[1]*p2[0] - p1[2]*p2[1]*p0[0] - p2[2]*p0[1]*p1[0];
    }

    template<typename PointIteratorT>
    std::pair<
        typename std::iterator_traits<PointIteratorT>::value_type,
        typename std::iterator_traits<PointIteratorT>::value_type
    > bounding_box( PointIteratorT it, PointIteratorT const & it_end )
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type   PointType;
      typedef typename viennagrid::result_of::coord<PointType>::type      NumericType;

      PointType lower_left;
      PointType upper_right;

      std::fill( lower_left.begin(), lower_left.end(), std::numeric_limits<NumericType>::max() );
      std::fill( upper_right.begin(), upper_right.end(), - std::numeric_limits<NumericType>::max() );
      //std::fill( upper_right.begin(), upper_right.end(), std::numeric_limits<NumericType>::lowest() );    C++11

      for (; it != it_end; ++it )
      {
        lower_left = viennagrid::min( lower_left, *it );
        upper_right = viennagrid::max( upper_right, *it );
      }

      return std::make_pair( lower_left, upper_right );
    }

    template<typename IteratorT>
    IteratorT circular_next(IteratorT it, IteratorT const & start_it, IteratorT const & end_it)
    {
      if (++it == end_it)
        return start_it;
      else
        return it;
    }

    template<typename IteratorT>
    IteratorT circular_prev(IteratorT it, IteratorT const & start_it, IteratorT const & end_it)
    {
      if (it == start_it)
        it = end_it;

      return --it;
    }

//         template<typename point_iterator_type, typename polygon_tag_type, typename point, typename numeric_config>
//         bool is_inside( point_iterator_type const & it_start, point_iterator_type it_end, polygon_tag_type polygon_tag,
//                         point const & point_to_test,
//                         numeric_config nc)
//         {
//             typedef typename std::iterator_traits<point_iterator_type>::value_type PolygonPointType;
//             std::pair<PolygonPointType, PolygonPointType> poly_bounding_box = bounding_box(it_start, it_end);
//
//             PolygonPointType outer_point;
//             outer_point[0] = point_to_test[0];
//             outer_point[1] = poly_bounding_box.first[1] - 1;
//             bool is_inside = false;
//
//             point_iterator_type it_prev = it_end; --it_prev;
//             point_iterator_type it_cur = it_start;
//
//             for ( ;it_cur != it_end ; ++it_cur, it_prev = circular_next(it_prev, it_start, it_end) )
//             {
//                 PolygonPointType const & q0 = *it_prev;
//                 PolygonPointType const & q1 = *it_cur;
//
//                 // is inner point on polygon line?
//                 if ( point_line_intersect( point_to_test, q0, q1, interval::closed_open_tag(), nc ) )
//                     return !is_open(polygon_tag);
//
//
//                 // is current line on test line?
//                 if ( !detail::is_equal(nc, q0[0], point_to_test[0]) || !detail::is_equal(nc, q1[0], point_to_test[0]) )
//                 {
//                     if ( line_line_intersect( q0, q1, interval::open_open_tag(), point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                     {
//                         is_inside = !is_inside;
//                     }
//                 }
//             }
//
//
//             // find point which is not on the testing line
//             point_iterator_type it = it_start;
//             while ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//             {
//                 it = circular_prev(it, it_start, it_end);
//                 if (it == it_start)
//                     break;
//             }
//
//             // no point found -> no intersection
//             if ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                 return false;
//
//
//             point_iterator_type circular_start_it = it;
//             it_prev = it;
//             it = circular_next(it, it_start, it_end);
//
//             // iterating over all points
//             while (it != circular_start_it)
//             {
//                 // is point on testing line?
//                 if ( point_line_intersect( *it, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                 {
//                     // find next point which is not on testing line
//                     point_iterator_type it_next = circular_next(it, it_start, it_end);
//                     while ( point_line_intersect( *it_next, point_to_test, outer_point, interval::open_open_tag(), nc ) )
//                         it_next = circular_next(it_next, it_start, it_end);
//
//                     // check if the the lines/points are an ear
//                     if ( ((*it_prev)[0] - (*it)[0]) * ((*it_next)[0] - (*it)[0]) < 0 )
//                     {
//                         is_inside = !is_inside;
//                     }
//
//                     it_prev = it;
//                     it = it_next;
//                 }
//                 else
//                 {
//                     it_prev = it;
//                     it = circular_next(it, it_start, it_end);
//                 }
//             }
//
//             return is_inside;
//         }

    template<typename IteratorT, typename VectorT>
    VectorT orthogonalize_one_vector( IteratorT it, const IteratorT & end, VectorT vec )
    {
      for (; it != end; ++it)
        vec -= viennagrid::inner_prod( vec, *it ) / viennagrid::inner_prod( *it, *it ) * (*it);

      return vec;
    }

    template<typename IteratorT, typename NumericConfigT>
    bool orthogonalize( IteratorT start, IteratorT end, NumericConfigT nc )
    {
      typedef typename std::iterator_traits<IteratorT>::value_type      vector_type;
      typedef typename viennagrid::result_of::coord<vector_type>::type  coord_type;

      for (IteratorT n = start; n != end; ++n)
      {
        *n = orthogonalize_one_vector(start, n, *n);

        if ( viennagrid::norm_1(*n) < detail::absolute_tolerance<coord_type>(nc) )
          return false;
      }

      return true;
    }

    template<typename PointIteratorT, typename NumericConfigT, typename OutPointT, typename OutPointIteratorT>
    bool projection_matrix(const PointIteratorT & start, const PointIteratorT & end, NumericConfigT nc,
                           OutPointT & center, OutPointIteratorT projection_matrix_start)
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type                    point_type;
      typedef typename viennagrid::result_of::coord<point_type>::type                      coord_type;
      typedef typename detail::result_of::numeric_type<NumericConfigT, coord_type>::type  numeric_type;

      OutPointIteratorT projection_matrix_end = projection_matrix_start;
      ++projection_matrix_end; ++projection_matrix_end;

      std::fill( projection_matrix_start, projection_matrix_end, point_type() );

      if (start == end) return false; // too few points

      PointIteratorT pit;

      // calculating the center
      pit = start;
      unsigned int num_points = 1;
      center = *pit; ++pit;

      for (; pit != end; ++pit)
      {
        center += *pit;
        ++num_points;
      }

      if (num_points < 3) return false; // too few points
      center /= num_points;

      // setting up a map of vectors from the center to all points, sorted descending by the length of that vector
      typedef std::multimap<numeric_type, point_type, std::greater<numeric_type> > vector_map_type;
      vector_map_type sorted_vectors;
      pit = start;
      for (; pit != end; ++pit)
      {
        point_type vector = center - *pit;
        typename vector_map_type::iterator it = sorted_vectors.insert( std::make_pair( viennagrid::norm_2( vector ), vector ) );
        it->second /= it->first; // normalizing the vector
      }

      // finding 2 non-liner dependent vectors
      unsigned int projection_matrix_index = 0;
      typename vector_map_type::iterator it = sorted_vectors.begin();
      while (projection_matrix_index < 2)
      {
        if ( it->first < detail::absolute_tolerance<coord_type>(nc) )
          return false; // points are too close together

        // check linear dependency with other vectors in projection matrix
        unsigned int index = 0;
        OutPointIteratorT pmit = projection_matrix_start;
        for (; index < projection_matrix_index; ++index, ++pmit)
        {
          numeric_type angle_cos = viennagrid::inner_prod( it->second, *pmit );
          if ( std::abs(angle_cos) > 1 - detail::absolute_tolerance<coord_type>(nc))
            break;
        }

        if ( index == projection_matrix_index)
        {
          *pmit = it->second;
          ++projection_matrix_index;
        }

        ++it;
      }

      // orthogonalize vectors
      orthogonalize( projection_matrix_start, projection_matrix_end, nc );

      // normalize vectors
      for (OutPointIteratorT it = projection_matrix_start; it != projection_matrix_end; ++it)
        *it /= viennagrid::norm_2(*it);

      return true;
    }

    template<typename PointIteratorT, typename OutPointIteratorT, typename point_type, typename ProjectionPointIteratorT>
    void project(PointIteratorT in, const PointIteratorT & in_end,
                OutPointIteratorT out,
                point_type center,
                ProjectionPointIteratorT const & proj_start, ProjectionPointIteratorT const & proj_end)
    {
      for ( ; in != in_end; ++in, ++out)
      {
        point_type tmp = *in - center;

        size_t index = 0;
        for (ProjectionPointIteratorT it = proj_start; it != proj_end; ++it, ++index)
          (*out)[index] = viennagrid::inner_prod( tmp, *it );
      }
    }

    template<typename PointIteratorT, typename OutPointIteratorT, typename NumericConfigT>
    bool project_onto_2dplane(PointIteratorT in, const PointIteratorT & in_end,
                              OutPointIteratorT out,
                              NumericConfigT nc)
    {
      typedef typename std::iterator_traits<PointIteratorT>::value_type       InPointType;

      size_t dimension = (*in).size();
      std::vector<InPointType> projection_matrix(dimension);
      InPointType center;

      if (!projection_matrix(in, in_end, projection_matrix.begin(), center, nc))
        return false;

      project(in, in_end, out, center, projection_matrix.begin(), projection_matrix.begin() + 2);
    }

  //} // namespace geometry

}

#endif

<|endoftext|>


<commit_before>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFontMgr.h"
#include "SkFontStyle.h"
#include "SkFontConfigInterface.h"
#include "SkFontConfigTypeface.h"
#include "SkMath.h"
#include "SkString.h"
#include "SkTDArray.h"

// for now we pull these in directly. eventually we will solely rely on the
// SkFontConfigInterface instance.
#include <fontconfig/fontconfig.h>
#include <unistd.h>

// Defined in SkFontHost_FreeType.cpp
bool find_name_and_attributes(SkStream* stream, SkString* name,
                              SkTypeface::Style* style, bool* isFixedWidth);

// borrow this global from SkFontHost_fontconfig. eventually that file should
// go away, and be replaced with this one.
extern SkFontConfigInterface* SkFontHost_fontconfig_ref_global();
static SkFontConfigInterface* RefFCI() {
    return SkFontHost_fontconfig_ref_global();
}

// look for the last substring after a '/' and return that, or return null.
static const char* find_just_name(const char* str) {
    const char* last = strrchr(str, '/');
    return last ? last + 1 : NULL;
}

static bool is_lower(char c) {
    return c >= 'a' && c <= 'z';
}

static int get_int(FcPattern* pattern, const char field[]) {
    int value;
    if (FcPatternGetInteger(pattern, field, 0, &value) != FcResultMatch) {
        value = SK_MinS32;
    }
    return value;
}

static const char* get_name(FcPattern* pattern, const char field[]) {
    const char* name;
    if (FcPatternGetString(pattern, field, 0, (FcChar8**)&name) != FcResultMatch) {
        name = "";
    }
    return name;
}

static bool valid_pattern(FcPattern* pattern) {
    FcBool is_scalable;
    if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) {
        return false;
    }

    // fontconfig can also return fonts which are unreadable
    const char* c_filename = get_name(pattern, FC_FILE);
    if (0 == *c_filename) {
        return false;
    }
    if (access(c_filename, R_OK) != 0) {
        return false;
    }
    return true;
}

static bool match_name(FcPattern* pattern, const char family_name[]) {
    return !strcasecmp(family_name, get_name(pattern, FC_FAMILY));
}

static FcPattern** MatchFont(FcFontSet* font_set,
                             const char post_config_family[],
                             int* count) {
  // Older versions of fontconfig have a bug where they cannot select
  // only scalable fonts so we have to manually filter the results.

    FcPattern** iter = font_set->fonts;
    FcPattern** stop = iter + font_set->nfont;
    // find the first good match
    for (; iter < stop; ++iter) {
        if (valid_pattern(*iter)) {
            break;
        }
    }

    if (iter == stop || !match_name(*iter, post_config_family)) {
        return NULL;
    }

    FcPattern** firstIter = iter++;
    for (; iter < stop; ++iter) {
        if (!valid_pattern(*iter) || !match_name(*iter, post_config_family)) {
            break;
        }
    }

    *count = iter - firstIter;
    return firstIter;
}

class SkFontStyleSet_FC : public SkFontStyleSet {
public:
    SkFontStyleSet_FC(FcPattern** matches, int count);
    virtual ~SkFontStyleSet_FC();

    virtual int count() SK_OVERRIDE { return fRecCount; }
    virtual void getStyle(int index, SkFontStyle*, SkString* style) SK_OVERRIDE;
    virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
    virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;

private:
    struct Rec {
        SkString    fStyleName;
        SkString    fFileName;
        SkFontStyle fStyle;
    };
    Rec* fRecs;
    int  fRecCount;
};

static int map_range(int value,
                     int old_min, int old_max, int new_min, int new_max) {
    SkASSERT(old_min < old_max);
    SkASSERT(new_min < new_max);
    return new_min + SkMulDiv(value - old_min,
                              new_max - new_min, old_max - old_min);
}

static SkFontStyle make_fontconfig_style(FcPattern* match) {
    int weight = get_int(match, FC_WEIGHT);
    int width = get_int(match, FC_WIDTH);
    int slant = get_int(match, FC_SLANT);
//    SkDebugf("old weight %d new weight %d\n", weight, map_range(weight, 0, 80, 0, 400));

    // fontconfig weight seems to be 0..200 or so, so we remap it here
    weight = map_range(weight, 0, 80, 0, 400);
    width = map_range(width, 0, 200, 0, 9);
    return SkFontStyle(weight, width, slant > 0 ? SkFontStyle::kItalic_Slant
                                                : SkFontStyle::kUpright_Slant);
}

SkFontStyleSet_FC::SkFontStyleSet_FC(FcPattern** matches, int count) {
    fRecCount = count;
    fRecs = SkNEW_ARRAY(Rec, count);
    for (int i = 0; i < count; ++i) {
        fRecs[i].fStyleName.set(get_name(matches[i], FC_STYLE));
        fRecs[i].fFileName.set(get_name(matches[i], FC_FILE));
        fRecs[i].fStyle = make_fontconfig_style(matches[i]);
    }
}

SkFontStyleSet_FC::~SkFontStyleSet_FC() {
    SkDELETE_ARRAY(fRecs);
}

void SkFontStyleSet_FC::getStyle(int index, SkFontStyle* style,
                                 SkString* styleName) {
    SkASSERT((unsigned)index < (unsigned)fRecCount);
    if (style) {
        *style = fRecs[index].fStyle;
    }
    if (styleName) {
        *styleName = fRecs[index].fStyleName;
    }
}

SkTypeface* SkFontStyleSet_FC::createTypeface(int index) {
    return NULL;
}

SkTypeface* SkFontStyleSet_FC::matchStyle(const SkFontStyle& pattern) {
    return NULL;
}

class SkFontMgr_fontconfig : public SkFontMgr {
    SkAutoTUnref<SkFontConfigInterface> fFCI;
    SkDataTable* fFamilyNames;

public:
    SkFontMgr_fontconfig(SkFontConfigInterface* fci)
        : fFCI(fci)
        , fFamilyNames(fFCI->getFamilyNames()) {}

    virtual ~SkFontMgr_fontconfig() {
        SkSafeUnref(fFamilyNames);
    }

protected:
    virtual int onCountFamilies() const SK_OVERRIDE {
        return fFamilyNames->count();
    }

    virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
        familyName->set(fFamilyNames->atStr(index));
    }

    virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
        return this->onMatchFamily(fFamilyNames->atStr(index));
    }

    virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
        FcPattern* pattern = FcPatternCreate();

        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
#if 0
        FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
#endif
        FcConfigSubstitute(NULL, pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        const char* post_config_family = get_name(pattern, FC_FAMILY);

        FcResult result;
        FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
        if (!font_set) {
            FcPatternDestroy(pattern);
            return NULL;
        }

        int count;
        FcPattern** match = MatchFont(font_set, post_config_family, &count);
        if (!match) {
            FcPatternDestroy(pattern);
            FcFontSetDestroy(font_set);
            return NULL;
        }

        FcPatternDestroy(pattern);

        SkTDArray<FcPattern*> trimmedMatches;
        for (int i = 0; i < count; ++i) {
            const char* justName = find_just_name(get_name(match[i], FC_FILE));
            if (!is_lower(*justName)) {
                *trimmedMatches.append() = match[i];
            }
        }

        SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
                                             (trimmedMatches.begin(),
                                              trimmedMatches.count()));
        return sset;
    }

    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
                                           const SkFontStyle&) const SK_OVERRIDE { return NULL; }
    virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
                                         const SkFontStyle&) const SK_OVERRIDE { return NULL; }

    virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const SK_OVERRIDE { return NULL; }

    virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
        const size_t length = stream->getLength();
        if (!length) {
            return NULL;
        }
        if (length >= 1024 * 1024 * 1024) {
            return NULL;  // don't accept too large fonts (>= 1GB) for safety.
        }

        // TODO should the caller give us the style or should we get it from freetype?
        SkTypeface::Style style = SkTypeface::kNormal;
        bool isFixedWidth = false;
        if (!find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
            return NULL;
        }

        SkTypeface* face = SkNEW_ARGS(FontConfigTypeface, (style, isFixedWidth, stream));
        return face;
    }

    virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
        SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
        return stream.get() ? this->createFromStream(stream, ttcIndex) : NULL;
    }

    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
                                               unsigned styleBits) const SK_OVERRIDE {
        return FontConfigTypeface::LegacyCreateTypeface(NULL, familyName,
                                                  (SkTypeface::Style)styleBits);
    }
};

SkFontMgr* SkFontMgr::Factory() {
    SkFontConfigInterface* fci = RefFCI();
    return fci ? SkNEW_ARGS(SkFontMgr_fontconfig, (fci)) : NULL;
}
<commit_msg>Add global fontconfig lock.<commit_after>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFontMgr.h"
#include "SkFontStyle.h"
#include "SkFontConfigInterface.h"
#include "SkFontConfigTypeface.h"
#include "SkMath.h"
#include "SkString.h"
#include "SkTDArray.h"
#include "SkThread.h"

// for now we pull these in directly. eventually we will solely rely on the
// SkFontConfigInterface instance.
#include <fontconfig/fontconfig.h>
#include <unistd.h>

namespace {

// Fontconfig is not threadsafe before 2.10.91.  Before that, we lock with a global mutex.
// See skia:1497 for background.
SK_DECLARE_STATIC_MUTEX(gFCMutex);
static bool gFCSafeToUse;

struct FCLocker {
    FCLocker() {
        if (FcGetVersion() < 21091) {  // We assume FcGetVersion() has always been thread safe.
            gFCMutex.acquire();
            fUnlock = true;
        } else {
            fUnlock = false;
        }
        gFCSafeToUse = true;
    }

    ~FCLocker() {
        gFCSafeToUse = false;
        if (fUnlock) {
            gFCMutex.release();
        }
    }

private:
    bool fUnlock;
};

} // namespace

// Defined in SkFontHost_FreeType.cpp
bool find_name_and_attributes(SkStream* stream, SkString* name,
                              SkTypeface::Style* style, bool* isFixedWidth);

// borrow this global from SkFontHost_fontconfig. eventually that file should
// go away, and be replaced with this one.
extern SkFontConfigInterface* SkFontHost_fontconfig_ref_global();
static SkFontConfigInterface* RefFCI() {
    return SkFontHost_fontconfig_ref_global();
}

// look for the last substring after a '/' and return that, or return null.
static const char* find_just_name(const char* str) {
    const char* last = strrchr(str, '/');
    return last ? last + 1 : NULL;
}

static bool is_lower(char c) {
    return c >= 'a' && c <= 'z';
}

static int get_int(FcPattern* pattern, const char field[]) {
    SkASSERT(gFCSafeToUse);
    int value;
    if (FcPatternGetInteger(pattern, field, 0, &value) != FcResultMatch) {
        value = SK_MinS32;
    }
    return value;
}

static const char* get_name(FcPattern* pattern, const char field[]) {
    SkASSERT(gFCSafeToUse);
    const char* name;
    if (FcPatternGetString(pattern, field, 0, (FcChar8**)&name) != FcResultMatch) {
        name = "";
    }
    return name;
}

static bool valid_pattern(FcPattern* pattern) {
    SkASSERT(gFCSafeToUse);
    FcBool is_scalable;
    if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) {
        return false;
    }

    // fontconfig can also return fonts which are unreadable
    const char* c_filename = get_name(pattern, FC_FILE);
    if (0 == *c_filename) {
        return false;
    }
    if (access(c_filename, R_OK) != 0) {
        return false;
    }
    return true;
}

static bool match_name(FcPattern* pattern, const char family_name[]) {
    return !strcasecmp(family_name, get_name(pattern, FC_FAMILY));
}

static FcPattern** MatchFont(FcFontSet* font_set,
                             const char post_config_family[],
                             int* count) {
  // Older versions of fontconfig have a bug where they cannot select
  // only scalable fonts so we have to manually filter the results.

    FcPattern** iter = font_set->fonts;
    FcPattern** stop = iter + font_set->nfont;
    // find the first good match
    for (; iter < stop; ++iter) {
        if (valid_pattern(*iter)) {
            break;
        }
    }

    if (iter == stop || !match_name(*iter, post_config_family)) {
        return NULL;
    }

    FcPattern** firstIter = iter++;
    for (; iter < stop; ++iter) {
        if (!valid_pattern(*iter) || !match_name(*iter, post_config_family)) {
            break;
        }
    }

    *count = iter - firstIter;
    return firstIter;
}

class SkFontStyleSet_FC : public SkFontStyleSet {
public:
    SkFontStyleSet_FC(FcPattern** matches, int count);
    virtual ~SkFontStyleSet_FC();

    virtual int count() SK_OVERRIDE { return fRecCount; }
    virtual void getStyle(int index, SkFontStyle*, SkString* style) SK_OVERRIDE;
    virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
    virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;

private:
    struct Rec {
        SkString    fStyleName;
        SkString    fFileName;
        SkFontStyle fStyle;
    };
    Rec* fRecs;
    int  fRecCount;
};

static int map_range(int value,
                     int old_min, int old_max, int new_min, int new_max) {
    SkASSERT(old_min < old_max);
    SkASSERT(new_min < new_max);
    return new_min + SkMulDiv(value - old_min,
                              new_max - new_min, old_max - old_min);
}

static SkFontStyle make_fontconfig_style(FcPattern* match) {
    int weight = get_int(match, FC_WEIGHT);
    int width = get_int(match, FC_WIDTH);
    int slant = get_int(match, FC_SLANT);
//    SkDebugf("old weight %d new weight %d\n", weight, map_range(weight, 0, 80, 0, 400));

    // fontconfig weight seems to be 0..200 or so, so we remap it here
    weight = map_range(weight, 0, 80, 0, 400);
    width = map_range(width, 0, 200, 0, 9);
    return SkFontStyle(weight, width, slant > 0 ? SkFontStyle::kItalic_Slant
                                                : SkFontStyle::kUpright_Slant);
}

SkFontStyleSet_FC::SkFontStyleSet_FC(FcPattern** matches, int count) {
    fRecCount = count;
    fRecs = SkNEW_ARRAY(Rec, count);
    for (int i = 0; i < count; ++i) {
        fRecs[i].fStyleName.set(get_name(matches[i], FC_STYLE));
        fRecs[i].fFileName.set(get_name(matches[i], FC_FILE));
        fRecs[i].fStyle = make_fontconfig_style(matches[i]);
    }
}

SkFontStyleSet_FC::~SkFontStyleSet_FC() {
    SkDELETE_ARRAY(fRecs);
}

void SkFontStyleSet_FC::getStyle(int index, SkFontStyle* style,
                                 SkString* styleName) {
    SkASSERT((unsigned)index < (unsigned)fRecCount);
    if (style) {
        *style = fRecs[index].fStyle;
    }
    if (styleName) {
        *styleName = fRecs[index].fStyleName;
    }
}

SkTypeface* SkFontStyleSet_FC::createTypeface(int index) {
    return NULL;
}

SkTypeface* SkFontStyleSet_FC::matchStyle(const SkFontStyle& pattern) {
    return NULL;
}

class SkFontMgr_fontconfig : public SkFontMgr {
    SkAutoTUnref<SkFontConfigInterface> fFCI;
    SkDataTable* fFamilyNames;

public:
    SkFontMgr_fontconfig(SkFontConfigInterface* fci)
        : fFCI(fci)
        , fFamilyNames(fFCI->getFamilyNames()) {}

    virtual ~SkFontMgr_fontconfig() {
        SkSafeUnref(fFamilyNames);
    }

protected:
    virtual int onCountFamilies() const SK_OVERRIDE {
        return fFamilyNames->count();
    }

    virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
        familyName->set(fFamilyNames->atStr(index));
    }

    virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
        return this->onMatchFamily(fFamilyNames->atStr(index));
    }

    virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
        FCLocker lock;

        FcPattern* pattern = FcPatternCreate();

        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
#if 0
        FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
#endif
        FcConfigSubstitute(NULL, pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        const char* post_config_family = get_name(pattern, FC_FAMILY);

        FcResult result;
        FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
        if (!font_set) {
            FcPatternDestroy(pattern);
            return NULL;
        }

        int count;
        FcPattern** match = MatchFont(font_set, post_config_family, &count);
        if (!match) {
            FcPatternDestroy(pattern);
            FcFontSetDestroy(font_set);
            return NULL;
        }

        FcPatternDestroy(pattern);

        SkTDArray<FcPattern*> trimmedMatches;
        for (int i = 0; i < count; ++i) {
            const char* justName = find_just_name(get_name(match[i], FC_FILE));
            if (!is_lower(*justName)) {
                *trimmedMatches.append() = match[i];
            }
        }

        SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
                                             (trimmedMatches.begin(),
                                              trimmedMatches.count()));
        return sset;
    }

    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
                                           const SkFontStyle&) const SK_OVERRIDE { return NULL; }
    virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
                                         const SkFontStyle&) const SK_OVERRIDE { return NULL; }

    virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const SK_OVERRIDE { return NULL; }

    virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
        const size_t length = stream->getLength();
        if (!length) {
            return NULL;
        }
        if (length >= 1024 * 1024 * 1024) {
            return NULL;  // don't accept too large fonts (>= 1GB) for safety.
        }

        // TODO should the caller give us the style or should we get it from freetype?
        SkTypeface::Style style = SkTypeface::kNormal;
        bool isFixedWidth = false;
        if (!find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
            return NULL;
        }

        SkTypeface* face = SkNEW_ARGS(FontConfigTypeface, (style, isFixedWidth, stream));
        return face;
    }

    virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
        SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
        return stream.get() ? this->createFromStream(stream, ttcIndex) : NULL;
    }

    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
                                               unsigned styleBits) const SK_OVERRIDE {
        FCLocker lock;
        return FontConfigTypeface::LegacyCreateTypeface(NULL, familyName,
                                                  (SkTypeface::Style)styleBits);
    }
};

SkFontMgr* SkFontMgr::Factory() {
    SkFontConfigInterface* fci = RefFCI();
    return fci ? SkNEW_ARGS(SkFontMgr_fontconfig, (fci)) : NULL;
}
<|endoftext|>