<commit_before>//
// Copyright (c) 2018 Rokas Kupstys
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <Urho3D/Resource/XMLFile.h>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/StringUtils.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Resource/ResourceEvents.h>
#include <Urho3D/SystemUI/SystemUI.h>
#include <Tabs/ConsoleTab.h>
#include <Tabs/ResourceTab.h>
#include <Tabs/HierarchyTab.h>
#include <Tabs/InspectorTab.h>
#include <Toolbox/SystemUI/Widgets.h>
#include <ThirdParty/tracy/server/IconsFontAwesome5.h>

#include "Editor.h"
#include "EditorEvents.h"
#include "Project.h"
#include "Tabs/Scene/SceneTab.h"
#include "Tabs/UI/UITab.h"

namespace Urho3D
{

Project::Project(Context* context)
    : Object(context)
    , assetConverter_(context)
#if URHO3D_PLUGINS
    , plugins_(context)
#endif
{
    SubscribeToEvent(E_EDITORRESOURCESAVED, std::bind(&Project::SaveProject, this));
    SubscribeToEvent(E_RESOURCERENAMED, [this](StringHash, VariantMap& args) {
        using namespace ResourceRenamed;
        if (args[P_FROM].GetString() == defaultScene_)
            defaultScene_ = args[P_TO].GetString();
    });
    SubscribeToEvent(E_RESOURCEBROWSERDELETE, [this](StringHash, VariantMap& args) {
        using namespace ResourceBrowserDelete;
        if (args[P_NAME].GetString() == defaultScene_)
            defaultScene_ = String::EMPTY;
    });

    SubscribeToEvent(E_ENDFRAME, [this](StringHash, VariantMap&) {
        // Save project every minute.
        // TODO: Make save time configurable.
        if (saveProjectTimer_.GetMSec(false) >= 60000)
        {
            SaveProject();
            saveProjectTimer_.Reset();
        }
    });
}

Project::~Project()
{
    if (GetSystemUI())
        ui::GetIO().IniFilename = nullptr;

    if (auto* cache = GetCache())
    {
        cache->RemoveResourceDir(GetCachePath());
        cache->RemoveResourceDir(GetResourcePath());

        for (const auto& path : cachedEngineResourcePaths_)
            GetCache()->AddResourceDir(path);
    }
}

bool Project::LoadProject(const String& projectPath)
{
    if (!projectFileDir_.Empty())
        // Project is already loaded.
        return false;

    if (projectPath.Empty())
        return false;

    projectFileDir_ = AddTrailingSlash(projectPath);

    if (!GetFileSystem()->Exists(GetCachePath()))
        GetFileSystem()->CreateDirsRecursive(GetCachePath());

    if (!GetFileSystem()->Exists(GetResourcePath()))
    {
        // Initialize new project
        GetFileSystem()->CreateDirsRecursive(GetResourcePath());

        for (const auto& path : GetCache()->GetResourceDirs())
        {
            if (path.EndsWith("/EditorData/") || path.Contains("/Autoload/"))
                continue;

            StringVector names;

            URHO3D_LOGINFOF("Importing resources from '%s'", path.CString());

            // Copy default resources to the project.
            GetFileSystem()->ScanDir(names, path, "*", SCAN_FILES, false);
            for (const auto& name : names)
                GetFileSystem()->Copy(path + name, GetResourcePath() + name);

            GetFileSystem()->ScanDir(names, path, "*", SCAN_DIRS, false);
            for (const auto& name : names)
            {
                if (name == "." || name == "..")
                    continue;
                GetFileSystem()->CopyDir(path + name, GetResourcePath() + name);
            }
        }
    }

    // Register asset dirs
    GetCache()->AddResourceDir(GetCachePath(), 0);
    GetCache()->AddResourceDir(GetResourcePath(), 1);
    if (!GetEngine()->IsHeadless())
    {
        assetConverter_.SetCachePath(GetCachePath());
        assetConverter_.AddAssetDirectory(GetResourcePath());
        assetConverter_.VerifyCacheAsync();
    }

    // Unregister engine dirs
    auto enginePrefixPath = GetSubsystem<Editor>()->GetCoreResourcePrefixPath();
    auto pathsCopy = GetCache()->GetResourceDirs();
    cachedEngineResourcePaths_.Clear();
    for (const auto& path : pathsCopy)
    {
        if (path.StartsWith(enginePrefixPath) && !path.EndsWith("/EditorData/"))
        {
            cachedEngineResourcePaths_.EmplaceBack(path);
            GetCache()->RemoveResourceDir(path);
        }
    }

    if (GetSystemUI())
    {
        uiConfigPath_ = projectFileDir_ + ".ui.ini";
        ui::GetIO().IniFilename = uiConfigPath_.CString();

        ImGuiSettingsHandler handler;
        handler.TypeName = "Project";
        handler.TypeHash = ImHash(handler.TypeName, 0, 0);
        handler.ReadOpenFn = [](ImGuiContext* context, ImGuiSettingsHandler* handler, const char* name) -> void*
        {
            if (strcmp(name, "Window") == 0)
                return (void*) 1;
            return (void*) name;
        };
        handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
        {
            SystemUI* systemUI = ui::GetSystemUI();
            auto* editor = systemUI->GetSubsystem<Editor>();

            if (entry == (void*) 1)
            {
                auto* project = systemUI->GetSubsystem<Project>();
                project->isNewProject_ = false;
                editor->CreateDefaultTabs();

                int x, y, w, h;
                if (sscanf(line, "Rect=%d,%d,%d,%d", &x, &y, &w, &h) == 4)
                {
                    w = Max(w, 100);            // Foot-shooting prevention
                    h = Max(h, 100);
                    systemUI->GetGraphics()->SetWindowPosition(x, y);
                    systemUI->GetGraphics()->SetMode(w, h);
                }
                else
                    return;
            }
            else
            {
                const char* name = static_cast<const char*>(entry);

                Tab* tab = editor->GetTabByName(name);
                if (tab == nullptr)
                {
                    StringVector parts = String(name).Split('#');
                    tab = editor->CreateTab(parts.Front());
                }
                tab->OnLoadUISettings(name, line);
            }
        };
        handler.WriteAllFn = [](ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
        {
            buf->appendf("[Project][Window]\n");
            auto* systemUI = ui::GetSystemUI();
            auto* editor = systemUI->GetSubsystem<Editor>();
            auto* project = systemUI->GetSubsystem<Project>();
            IntVector2
            wSize = systemUI->GetGraphics()->GetSize();
            IntVector2
            wPos = systemUI->GetGraphics()->GetWindowPosition();
            buf->appendf("Rect=%d,%d,%d,%d\n", wPos.x_, wPos.y_, wSize.x_, wSize.y_);

            // Save tabs
            for (auto& tab : editor->GetContentTabs())
                tab->OnSaveUISettings(buf);
        };
        ui::GetCurrentContext()->SettingsHandlers.push_back(handler);
    }

#if URHO3D_HASH_DEBUG
    // StringHashNames.json
    {
        String filePath(projectFileDir_ + "StringHashNames.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            for (const auto& value : file.GetRoot().GetArray())
            {
                // Seed global string hash to name map.
                StringHash hash(value.GetString());
                (void) (hash);
            }
        }
    }
#endif

    // Project.json
    {
        String filePath(projectFileDir_ + "Project.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            const auto& root = file.GetRoot().GetObject();
#if URHO3D_PLUGINS
            if (root.Contains("plugins"))
            {
                const auto& plugins = root["plugins"]->GetArray();
                for (const auto& pluginInfoValue : plugins)
                {
                    const JSONObject& pluginInfo = pluginInfoValue.GetObject();
                    Plugin* plugin = plugins_.Load(pluginInfo["name"]->GetString());
                    if (pluginInfo["private"]->GetBool())
                        plugin->SetFlags(plugin->GetFlags() | PLUGIN_PRIVATE);
                }
                // Tick plugins once to ensure plugins are loaded before loading any possibly open scenes. This makes
                // plugins register themselves with the engine so that loaded scenes can properly load components
                // provided by plugins. Not doing this would cause scenes to load these components as UnknownComponent.
                plugins_.OnEndFrame();
            }
#endif
            if (root.Contains("default-scene"))
                defaultScene_ = root["default-scene"]->GetString();

            using namespace EditorProjectLoading;
            SendEvent(E_EDITORPROJECTLOADING, P_ROOT, (void*)&root);
        }
    }

    // Settings.json
    {
        String filePath(projectFileDir_ + "Settings.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            for (auto& pair : file.GetRoot().GetObject())
                engineParameters_[pair.first_] = pair.second_.GetVariant();
        }
    }

    return true;
}

bool Project::SaveProject()
{
    if (GetEngine()->IsHeadless())
    {
        URHO3D_LOGERROR("Headless instance is supposed to use project as read-only.");
        return false;
    }

    // Saving project data of tabs may trigger saving resources, which in turn triggers saving editor project. Avoid
    // that loop.
    UnsubscribeFromEvent(E_EDITORRESOURCESAVED);

    if (projectFileDir_.Empty())
    {
        URHO3D_LOGERROR("Unable to save project. Project path is empty.");
        return false;
    }

    // Project.json
    {
        JSONFile file(context_);
        JSONValue& root = file.GetRoot();

        root["version"] = 0;

        // Plugins
#if URHO3D_PLUGINS
        {
            JSONArray plugins{};
            for (const auto& plugin : plugins_.GetPlugins())
            {
                plugins.Push(JSONObject{{"name",    plugin->GetName()},
                                        {"private", plugin->GetFlags() & PLUGIN_PRIVATE ? true : false}});
            }
            Sort(plugins.Begin(), plugins.End(), [](JSONValue& a, JSONValue& b) {
                const String& nameA = a.GetObject()["name"]->GetString();
                const String& nameB = b.GetObject()["name"]->GetString();
                return nameA.Compare(nameB);
            });
            root["plugins"] = plugins;
        }
#endif
        root["default-scene"] = defaultScene_;

        String filePath(projectFileDir_ + "Project.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving project to '%s' failed", filePath.CString());
            return false;
        }

        using namespace EditorProjectSaving;
        SendEvent(E_EDITORPROJECTSAVING, P_ROOT, (void*)&root);
    }

    // Settings.json
    {
        JSONFile file(context_);
        JSONValue& root = file.GetRoot();

        for (const auto& pair : engineParameters_)
            root[pair.first_].SetVariant(pair.second_, context_);

        String filePath(projectFileDir_ + "Settings.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving project to '%s' failed", filePath.CString());
            return false;
        }
    }

#if URHO3D_HASH_DEBUG
    // StringHashNames.json
    {
        auto hashNames = StringHash::GetGlobalStringHashRegister()->GetInternalMap().Values();
        Sort(hashNames.Begin(), hashNames.End());
        JSONFile file(context_);
        JSONArray names;
        for (const auto& string : hashNames)
            names.Push(string);
        file.GetRoot() = names;

        String filePath(projectFileDir_ + "StringHashNames.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving StringHash names to '%s' failed", filePath.CString());
            return false;
        }
    }
#endif

    ui::SaveIniSettingsToDisk(uiConfigPath_.CString());

    SubscribeToEvent(E_EDITORRESOURCESAVED, std::bind(&Project::SaveProject, this));

    return true;
}

String Project::GetCachePath() const
{
    if (projectFileDir_.Empty())
        return String::EMPTY;
    return projectFileDir_ + "Cache/";
}

String Project::GetResourcePath() const
{
    if (projectFileDir_.Empty())
        return String::EMPTY;
    return projectFileDir_ + "Resources/";
}

}
<commit_msg>Editor: Fix crash during project loading when loading plugin fails. Output error instead.<commit_after>//
// Copyright (c) 2018 Rokas Kupstys
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <Urho3D/Resource/XMLFile.h>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/StringUtils.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Resource/ResourceEvents.h>
#include <Urho3D/SystemUI/SystemUI.h>
#include <Tabs/ConsoleTab.h>
#include <Tabs/ResourceTab.h>
#include <Tabs/HierarchyTab.h>
#include <Tabs/InspectorTab.h>
#include <Toolbox/SystemUI/Widgets.h>
#include <ThirdParty/tracy/server/IconsFontAwesome5.h>

#include "Editor.h"
#include "EditorEvents.h"
#include "Project.h"
#include "Tabs/Scene/SceneTab.h"
#include "Tabs/UI/UITab.h"

namespace Urho3D
{

Project::Project(Context* context)
    : Object(context)
    , assetConverter_(context)
#if URHO3D_PLUGINS
    , plugins_(context)
#endif
{
    SubscribeToEvent(E_EDITORRESOURCESAVED, std::bind(&Project::SaveProject, this));
    SubscribeToEvent(E_RESOURCERENAMED, [this](StringHash, VariantMap& args) {
        using namespace ResourceRenamed;
        if (args[P_FROM].GetString() == defaultScene_)
            defaultScene_ = args[P_TO].GetString();
    });
    SubscribeToEvent(E_RESOURCEBROWSERDELETE, [this](StringHash, VariantMap& args) {
        using namespace ResourceBrowserDelete;
        if (args[P_NAME].GetString() == defaultScene_)
            defaultScene_ = String::EMPTY;
    });

    SubscribeToEvent(E_ENDFRAME, [this](StringHash, VariantMap&) {
        // Save project every minute.
        // TODO: Make save time configurable.
        if (saveProjectTimer_.GetMSec(false) >= 60000)
        {
            SaveProject();
            saveProjectTimer_.Reset();
        }
    });
}

Project::~Project()
{
    if (GetSystemUI())
        ui::GetIO().IniFilename = nullptr;

    if (auto* cache = GetCache())
    {
        cache->RemoveResourceDir(GetCachePath());
        cache->RemoveResourceDir(GetResourcePath());

        for (const auto& path : cachedEngineResourcePaths_)
            GetCache()->AddResourceDir(path);
    }
}

bool Project::LoadProject(const String& projectPath)
{
    if (!projectFileDir_.Empty())
        // Project is already loaded.
        return false;

    if (projectPath.Empty())
        return false;

    projectFileDir_ = AddTrailingSlash(projectPath);

    if (!GetFileSystem()->Exists(GetCachePath()))
        GetFileSystem()->CreateDirsRecursive(GetCachePath());

    if (!GetFileSystem()->Exists(GetResourcePath()))
    {
        // Initialize new project
        GetFileSystem()->CreateDirsRecursive(GetResourcePath());

        for (const auto& path : GetCache()->GetResourceDirs())
        {
            if (path.EndsWith("/EditorData/") || path.Contains("/Autoload/"))
                continue;

            StringVector names;

            URHO3D_LOGINFOF("Importing resources from '%s'", path.CString());

            // Copy default resources to the project.
            GetFileSystem()->ScanDir(names, path, "*", SCAN_FILES, false);
            for (const auto& name : names)
                GetFileSystem()->Copy(path + name, GetResourcePath() + name);

            GetFileSystem()->ScanDir(names, path, "*", SCAN_DIRS, false);
            for (const auto& name : names)
            {
                if (name == "." || name == "..")
                    continue;
                GetFileSystem()->CopyDir(path + name, GetResourcePath() + name);
            }
        }
    }

    // Register asset dirs
    GetCache()->AddResourceDir(GetCachePath(), 0);
    GetCache()->AddResourceDir(GetResourcePath(), 1);
    if (!GetEngine()->IsHeadless())
    {
        assetConverter_.SetCachePath(GetCachePath());
        assetConverter_.AddAssetDirectory(GetResourcePath());
        assetConverter_.VerifyCacheAsync();
    }

    // Unregister engine dirs
    auto enginePrefixPath = GetSubsystem<Editor>()->GetCoreResourcePrefixPath();
    auto pathsCopy = GetCache()->GetResourceDirs();
    cachedEngineResourcePaths_.Clear();
    for (const auto& path : pathsCopy)
    {
        if (path.StartsWith(enginePrefixPath) && !path.EndsWith("/EditorData/"))
        {
            cachedEngineResourcePaths_.EmplaceBack(path);
            GetCache()->RemoveResourceDir(path);
        }
    }

    if (GetSystemUI())
    {
        uiConfigPath_ = projectFileDir_ + ".ui.ini";
        ui::GetIO().IniFilename = uiConfigPath_.CString();

        ImGuiSettingsHandler handler;
        handler.TypeName = "Project";
        handler.TypeHash = ImHash(handler.TypeName, 0, 0);
        handler.ReadOpenFn = [](ImGuiContext* context, ImGuiSettingsHandler* handler, const char* name) -> void*
        {
            if (strcmp(name, "Window") == 0)
                return (void*) 1;
            return (void*) name;
        };
        handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
        {
            SystemUI* systemUI = ui::GetSystemUI();
            auto* editor = systemUI->GetSubsystem<Editor>();

            if (entry == (void*) 1)
            {
                auto* project = systemUI->GetSubsystem<Project>();
                project->isNewProject_ = false;
                editor->CreateDefaultTabs();

                int x, y, w, h;
                if (sscanf(line, "Rect=%d,%d,%d,%d", &x, &y, &w, &h) == 4)
                {
                    w = Max(w, 100);            // Foot-shooting prevention
                    h = Max(h, 100);
                    systemUI->GetGraphics()->SetWindowPosition(x, y);
                    systemUI->GetGraphics()->SetMode(w, h);
                }
                else
                    return;
            }
            else
            {
                const char* name = static_cast<const char*>(entry);

                Tab* tab = editor->GetTabByName(name);
                if (tab == nullptr)
                {
                    StringVector parts = String(name).Split('#');
                    tab = editor->CreateTab(parts.Front());
                }
                tab->OnLoadUISettings(name, line);
            }
        };
        handler.WriteAllFn = [](ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
        {
            buf->appendf("[Project][Window]\n");
            auto* systemUI = ui::GetSystemUI();
            auto* editor = systemUI->GetSubsystem<Editor>();
            auto* project = systemUI->GetSubsystem<Project>();
            IntVector2
            wSize = systemUI->GetGraphics()->GetSize();
            IntVector2
            wPos = systemUI->GetGraphics()->GetWindowPosition();
            buf->appendf("Rect=%d,%d,%d,%d\n", wPos.x_, wPos.y_, wSize.x_, wSize.y_);

            // Save tabs
            for (auto& tab : editor->GetContentTabs())
                tab->OnSaveUISettings(buf);
        };
        ui::GetCurrentContext()->SettingsHandlers.push_back(handler);
    }

#if URHO3D_HASH_DEBUG
    // StringHashNames.json
    {
        String filePath(projectFileDir_ + "StringHashNames.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            for (const auto& value : file.GetRoot().GetArray())
            {
                // Seed global string hash to name map.
                StringHash hash(value.GetString());
                (void) (hash);
            }
        }
    }
#endif

    // Project.json
    {
        String filePath(projectFileDir_ + "Project.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            const auto& root = file.GetRoot().GetObject();
#if URHO3D_PLUGINS
            if (root.Contains("plugins"))
            {
                const auto& plugins = root["plugins"]->GetArray();
                for (const auto& pluginInfoValue : plugins)
                {
                    const JSONObject& pluginInfo = pluginInfoValue.GetObject();
                    const String& pluginName = pluginInfo["name"]->GetString();
                    if (Plugin* plugin = plugins_.Load(pluginName))
                    {
                        if (pluginInfo["private"]->GetBool())
                            plugin->SetFlags(plugin->GetFlags() | PLUGIN_PRIVATE);
                    }
                    else
                        URHO3D_LOGERRORF("Loading plugin '%s' failed.", pluginName.CString());
                }
                // Tick plugins once to ensure plugins are loaded before loading any possibly open scenes. This makes
                // plugins register themselves with the engine so that loaded scenes can properly load components
                // provided by plugins. Not doing this would cause scenes to load these components as UnknownComponent.
                plugins_.OnEndFrame();
            }
#endif
            if (root.Contains("default-scene"))
                defaultScene_ = root["default-scene"]->GetString();

            using namespace EditorProjectLoading;
            SendEvent(E_EDITORPROJECTLOADING, P_ROOT, (void*)&root);
        }
    }

    // Settings.json
    {
        String filePath(projectFileDir_ + "Settings.json");
        if (GetFileSystem()->Exists(filePath))
        {
            JSONFile file(context_);
            if (!file.LoadFile(filePath))
                return false;

            for (auto& pair : file.GetRoot().GetObject())
                engineParameters_[pair.first_] = pair.second_.GetVariant();
        }
    }

    return true;
}

bool Project::SaveProject()
{
    if (GetEngine()->IsHeadless())
    {
        URHO3D_LOGERROR("Headless instance is supposed to use project as read-only.");
        return false;
    }

    // Saving project data of tabs may trigger saving resources, which in turn triggers saving editor project. Avoid
    // that loop.
    UnsubscribeFromEvent(E_EDITORRESOURCESAVED);

    if (projectFileDir_.Empty())
    {
        URHO3D_LOGERROR("Unable to save project. Project path is empty.");
        return false;
    }

    // Project.json
    {
        JSONFile file(context_);
        JSONValue& root = file.GetRoot();

        root["version"] = 0;

        // Plugins
#if URHO3D_PLUGINS
        {
            JSONArray plugins{};
            for (const auto& plugin : plugins_.GetPlugins())
            {
                plugins.Push(JSONObject{{"name",    plugin->GetName()},
                                        {"private", plugin->GetFlags() & PLUGIN_PRIVATE ? true : false}});
            }
            Sort(plugins.Begin(), plugins.End(), [](JSONValue& a, JSONValue& b) {
                const String& nameA = a.GetObject()["name"]->GetString();
                const String& nameB = b.GetObject()["name"]->GetString();
                return nameA.Compare(nameB);
            });
            root["plugins"] = plugins;
        }
#endif
        root["default-scene"] = defaultScene_;

        String filePath(projectFileDir_ + "Project.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving project to '%s' failed", filePath.CString());
            return false;
        }

        using namespace EditorProjectSaving;
        SendEvent(E_EDITORPROJECTSAVING, P_ROOT, (void*)&root);
    }

    // Settings.json
    {
        JSONFile file(context_);
        JSONValue& root = file.GetRoot();

        for (const auto& pair : engineParameters_)
            root[pair.first_].SetVariant(pair.second_, context_);

        String filePath(projectFileDir_ + "Settings.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving project to '%s' failed", filePath.CString());
            return false;
        }
    }

#if URHO3D_HASH_DEBUG
    // StringHashNames.json
    {
        auto hashNames = StringHash::GetGlobalStringHashRegister()->GetInternalMap().Values();
        Sort(hashNames.Begin(), hashNames.End());
        JSONFile file(context_);
        JSONArray names;
        for (const auto& string : hashNames)
            names.Push(string);
        file.GetRoot() = names;

        String filePath(projectFileDir_ + "StringHashNames.json");
        if (!file.SaveFile(filePath))
        {
            projectFileDir_.Clear();
            URHO3D_LOGERRORF("Saving StringHash names to '%s' failed", filePath.CString());
            return false;
        }
    }
#endif

    ui::SaveIniSettingsToDisk(uiConfigPath_.CString());

    SubscribeToEvent(E_EDITORRESOURCESAVED, std::bind(&Project::SaveProject, this));

    return true;
}

String Project::GetCachePath() const
{
    if (projectFileDir_.Empty())
        return String::EMPTY;
    return projectFileDir_ + "Cache/";
}

String Project::GetResourcePath() const
{
    if (projectFileDir_.Empty())
        return String::EMPTY;
    return projectFileDir_ + "Resources/";
}

}
<|endoftext|>


<commit_before><commit_msg>testing<commit_after><|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief simple arango importer
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "BasicsC/common.h"

#include <stdio.h>
#include <fstream>

#include "ArangoShell/ArangoClient.h"
#include "Basics/FileUtils.h"
#include "Basics/ProgramOptions.h"
#include "Basics/ProgramOptionsDescription.h"
#include "Basics/StringUtils.h"
#include "BasicsC/files.h"
#include "BasicsC/init.h"
#include "BasicsC/logging.h"
#include "BasicsC/tri-strings.h"
#include "BasicsC/terminal-utils.h"
#include "ImportHelper.h"
#include "Rest/Endpoint.h"
#include "Rest/InitialiseRest.h"
#include "Rest/HttpResponse.h"
#include "SimpleHttpClient/SimpleHttpClient.h"
#include "SimpleHttpClient/SimpleHttpResult.h"
#include "V8Client/V8ClientConnection.h"

using namespace std;
using namespace triagens::basics;
using namespace triagens::httpclient;
using namespace triagens::rest;
using namespace triagens::v8client;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                                 private variables
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief base class for clients
////////////////////////////////////////////////////////////////////////////////

ArangoClient BaseClient;

////////////////////////////////////////////////////////////////////////////////
/// @brief the initial default connection
////////////////////////////////////////////////////////////////////////////////

V8ClientConnection* ClientConnection = 0;

////////////////////////////////////////////////////////////////////////////////
/// @brief max size body size (used for imports)
////////////////////////////////////////////////////////////////////////////////

static uint64_t ChunkSize = 1024 * 1024 * 4;

////////////////////////////////////////////////////////////////////////////////
/// @brief quote character(s)
////////////////////////////////////////////////////////////////////////////////

static string Quote = "\"";

////////////////////////////////////////////////////////////////////////////////
/// @brief separator
////////////////////////////////////////////////////////////////////////////////

static string Separator = ",";

////////////////////////////////////////////////////////////////////////////////
/// @brief file-name
////////////////////////////////////////////////////////////////////////////////

static string FileName = "";

////////////////////////////////////////////////////////////////////////////////
/// @brief collection-name
////////////////////////////////////////////////////////////////////////////////

static string CollectionName = "";

////////////////////////////////////////////////////////////////////////////////
/// @brief import type
////////////////////////////////////////////////////////////////////////////////

static string TypeImport = "json";

////////////////////////////////////////////////////////////////////////////////
/// @brief create collection if necessary
////////////////////////////////////////////////////////////////////////////////

static bool CreateCollection = false;

////////////////////////////////////////////////////////////////////////////////
/// @brief progress
////////////////////////////////////////////////////////////////////////////////

static bool Progress = true;

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief parses the program options
////////////////////////////////////////////////////////////////////////////////

static void ParseProgramOptions (int argc, char* argv[]) {
  ProgramOptionsDescription deprecatedOptions("DEPRECATED options");

  deprecatedOptions
    ("max-upload-size", &ChunkSize, "size for individual data batches (in bytes)")
  ;

  ProgramOptionsDescription description("STANDARD options");

  description
    ("file", &FileName, "file name (\"-\" for STDIN)")
    ("batch-size", &ChunkSize, "size for individual data batches (in bytes)")
    ("collection", &CollectionName, "collection name")
    ("create-collection", &CreateCollection, "create collection if it does not yet exist")
    ("type", &TypeImport, "type of file (\"csv\", \"tsv\", or \"json\")")
    ("quote", &Quote, "quote character(s)")
    ("separator", &Separator, "separator")
    ("progress", &Progress, "show progress")
    (deprecatedOptions, true)
  ;

  BaseClient.setupGeneral(description);
  BaseClient.setupServer(description);

  vector<string> arguments;
  description.arguments(&arguments);

  ProgramOptions options;
  BaseClient.parse(options, description, argc, argv, "arangoimp.conf");

  if (FileName == "" && arguments.size() > 0) {
    FileName = arguments[0];
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief startup and exit functions
////////////////////////////////////////////////////////////////////////////////

static void arangoimpEntryFunction ();
static void arangoimpExitFunction (int, void*);

#ifdef _WIN32

// .............................................................................
// Call this function to do various initialistions for windows only
// .............................................................................
void arangoimpEntryFunction() {
  int maxOpenFiles = 1024;
  int res = 0;

  // ...........................................................................
  // Uncomment this to call this for extended debug information.
  // If you familiar with valgrind ... then this is not like that, however
  // you do get some similar functionality.
  // ...........................................................................
  //res = initialiseWindows(TRI_WIN_INITIAL_SET_DEBUG_FLAG, 0);

  res = initialiseWindows(TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER, 0);
  if (res != 0) {
    _exit(1);
  }

  res = initialiseWindows(TRI_WIN_INITIAL_SET_MAX_STD_IO,(const char*)(&maxOpenFiles));
  if (res != 0) {
    _exit(1);
  }

  res = initialiseWindows(TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL, 0);
  if (res != 0) {
    _exit(1);
  }

  TRI_Application_Exit_SetExit(arangoimpExitFunction);

}

static void arangoimpExitFunction(int exitCode, void* data) {
  int res = 0;
  // ...........................................................................
  // TODO: need a terminate function for windows to be called and cleanup
  // any windows specific stuff.
  // ...........................................................................

  res = finaliseWindows(TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL, 0);

  if (res != 0) {
    exit(1);
  }

  exit(exitCode);
}
#else

static void arangoimpEntryFunction() {
}

static void arangoimpExitFunction(int exitCode, void* data) {
}

#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief main
////////////////////////////////////////////////////////////////////////////////

int main (int argc, char* argv[]) {

  int ret = EXIT_SUCCESS;

  arangoimpEntryFunction();

  TRIAGENS_C_INITIALISE(argc, argv);
  TRIAGENS_REST_INITIALISE(argc, argv);

  TRI_InitialiseLogging(false);

  BaseClient.setEndpointString(Endpoint::getDefaultEndpoint());

  // .............................................................................
  // parse the program options
  // .............................................................................

  ParseProgramOptions(argc, argv);

  // .............................................................................
  // set-up client connection
  // .............................................................................

  BaseClient.createEndpoint();

  if (BaseClient.endpointServer() == 0) {
    cerr << "invalid value for --server.endpoint ('" << BaseClient.endpointString() << "')" << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  ClientConnection = new V8ClientConnection(BaseClient.endpointServer(),
                                            BaseClient.databaseName(),
                                            BaseClient.username(),
                                            BaseClient.password(),
                                            BaseClient.requestTimeout(),
                                            BaseClient.connectTimeout(),
                                            ArangoClient::DEFAULT_RETRIES,
                                            BaseClient.sslProtocol(),
                                            false);

  if (! ClientConnection->isConnected() || ClientConnection->getLastHttpReturnCode() != HttpResponse::OK) {
    cerr << "Could not connect to endpoint '" << BaseClient.endpointServer()->getSpecification()
         << "', database: '" << BaseClient.databaseName() << "'" << endl;
    cerr << "Error message: '" << ClientConnection->getErrorMessage() << "'" << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // successfully connected
  cout << "Connected to ArangoDB '" << BaseClient.endpointServer()->getSpecification()
       << "', version " << ClientConnection->getVersion() << ", database: '"
       << BaseClient.databaseName() << "', username: '" << BaseClient.username() << "'" << endl;

  cout << "----------------------------------------" << endl;
  cout << "database:         " << BaseClient.databaseName() << endl;
  cout << "collection:       " << CollectionName << endl;
  cout << "create:           " << (CreateCollection ? "yes" : "no") << endl;
  cout << "file:             " << FileName << endl;
  cout << "type:             " << TypeImport << endl;

  if (TypeImport == "csv") {
    cout << "quote:            " << Quote << endl;
    cout << "separator:        " << Separator << endl;
  }

  cout << "connect timeout:  " << BaseClient.connectTimeout() << endl;
  cout << "request timeout:  " << BaseClient.requestTimeout() << endl;
  cout << "----------------------------------------" << endl;

  ImportHelper ih(ClientConnection->getHttpClient(), ChunkSize);

  // create colletion
  if (CreateCollection) {
    ih.setCreateCollection(true);
  }

  // quote
  if (Quote.length() <= 1) {
    ih.setQuote(Quote);
  }
  else {
    cerr << "Wrong length of quote character." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // separator
  if (Separator.length() == 1) {
    ih.setSeparator(Separator);
  }
  else {
    cerr << "Separator must be exactly one character." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // collection name
  if (CollectionName == "") {
    cerr << "collection name is missing." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // filename
  if (FileName == "") {
    cerr << "file name is missing." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  if (FileName != "-" && ! FileUtils::isRegularFile(FileName)) {
    cerr << "file '" << FileName << "' is not a regular file." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // progress
  if (Progress) {
    ih.setProgress(true);
  }

  // import type
  bool ok = false;

  if (TypeImport == "csv") {
    cout << "Starting CSV import..." << endl;
    ok = ih.importDelimited(CollectionName, FileName, ImportHelper::CSV);
  }

  else if (TypeImport == "tsv") {
    cout << "Starting TSV import..." << endl;
    ih.setQuote("");
    ih.setSeparator("\\t");
    ok = ih.importDelimited(CollectionName, FileName, ImportHelper::TSV);
  }

  else if (TypeImport == "json") {
    cout << "Starting JSON import..." << endl;
    ok = ih.importJson(CollectionName, FileName);
  }

  else {
    cerr << "Wrong type '" << TypeImport << "'." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  cout << endl;

  // give information about import
  if (ok) {
    cout << "created:          " << ih.getImportedLines() << endl;
    cout << "errors:           " << ih.getErrorLines() << endl;
    cout << "total:            " << ih.getReadLines() << endl;
  }
  else {
    cerr << "error message:    " << ih.getErrorMessage() << endl;
  }

  // calling dispose in V8 3.10.x causes a segfault. the v8 docs says its not necessary to call it upon program termination
  // v8::V8::Dispose();

  TRIAGENS_REST_SHUTDOWN;

  arangoimpExitFunction(ret, NULL);

  return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<commit_msg>slightly updated error messages<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief simple arango importer
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "BasicsC/common.h"

#include <stdio.h>
#include <fstream>

#include "ArangoShell/ArangoClient.h"
#include "Basics/FileUtils.h"
#include "Basics/ProgramOptions.h"
#include "Basics/ProgramOptionsDescription.h"
#include "Basics/StringUtils.h"
#include "BasicsC/files.h"
#include "BasicsC/init.h"
#include "BasicsC/logging.h"
#include "BasicsC/tri-strings.h"
#include "BasicsC/terminal-utils.h"
#include "ImportHelper.h"
#include "Rest/Endpoint.h"
#include "Rest/InitialiseRest.h"
#include "Rest/HttpResponse.h"
#include "SimpleHttpClient/SimpleHttpClient.h"
#include "SimpleHttpClient/SimpleHttpResult.h"
#include "V8Client/V8ClientConnection.h"

using namespace std;
using namespace triagens::basics;
using namespace triagens::httpclient;
using namespace triagens::rest;
using namespace triagens::v8client;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                                 private variables
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief base class for clients
////////////////////////////////////////////////////////////////////////////////

ArangoClient BaseClient;

////////////////////////////////////////////////////////////////////////////////
/// @brief the initial default connection
////////////////////////////////////////////////////////////////////////////////

V8ClientConnection* ClientConnection = 0;

////////////////////////////////////////////////////////////////////////////////
/// @brief max size body size (used for imports)
////////////////////////////////////////////////////////////////////////////////

static uint64_t ChunkSize = 1024 * 1024 * 4;

////////////////////////////////////////////////////////////////////////////////
/// @brief quote character(s)
////////////////////////////////////////////////////////////////////////////////

static string Quote = "\"";

////////////////////////////////////////////////////////////////////////////////
/// @brief separator
////////////////////////////////////////////////////////////////////////////////

static string Separator = ",";

////////////////////////////////////////////////////////////////////////////////
/// @brief file-name
////////////////////////////////////////////////////////////////////////////////

static string FileName = "";

////////////////////////////////////////////////////////////////////////////////
/// @brief collection-name
////////////////////////////////////////////////////////////////////////////////

static string CollectionName = "";

////////////////////////////////////////////////////////////////////////////////
/// @brief import type
////////////////////////////////////////////////////////////////////////////////

static string TypeImport = "json";

////////////////////////////////////////////////////////////////////////////////
/// @brief create collection if necessary
////////////////////////////////////////////////////////////////////////////////

static bool CreateCollection = false;

////////////////////////////////////////////////////////////////////////////////
/// @brief progress
////////////////////////////////////////////////////////////////////////////////

static bool Progress = true;

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief parses the program options
////////////////////////////////////////////////////////////////////////////////

static void ParseProgramOptions (int argc, char* argv[]) {
  ProgramOptionsDescription deprecatedOptions("DEPRECATED options");

  deprecatedOptions
    ("max-upload-size", &ChunkSize, "size for individual data batches (in bytes)")
  ;

  ProgramOptionsDescription description("STANDARD options");

  description
    ("file", &FileName, "file name (\"-\" for STDIN)")
    ("batch-size", &ChunkSize, "size for individual data batches (in bytes)")
    ("collection", &CollectionName, "collection name")
    ("create-collection", &CreateCollection, "create collection if it does not yet exist")
    ("type", &TypeImport, "type of file (\"csv\", \"tsv\", or \"json\")")
    ("quote", &Quote, "quote character(s)")
    ("separator", &Separator, "separator")
    ("progress", &Progress, "show progress")
    (deprecatedOptions, true)
  ;

  BaseClient.setupGeneral(description);
  BaseClient.setupServer(description);

  vector<string> arguments;
  description.arguments(&arguments);

  ProgramOptions options;
  BaseClient.parse(options, description, argc, argv, "arangoimp.conf");

  if (FileName == "" && arguments.size() > 0) {
    FileName = arguments[0];
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Shell
/// @{
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @brief startup and exit functions
////////////////////////////////////////////////////////////////////////////////

static void arangoimpEntryFunction ();
static void arangoimpExitFunction (int, void*);

#ifdef _WIN32

// .............................................................................
// Call this function to do various initialistions for windows only
// .............................................................................
void arangoimpEntryFunction() {
  int maxOpenFiles = 1024;
  int res = 0;

  // ...........................................................................
  // Uncomment this to call this for extended debug information.
  // If you familiar with valgrind ... then this is not like that, however
  // you do get some similar functionality.
  // ...........................................................................
  //res = initialiseWindows(TRI_WIN_INITIAL_SET_DEBUG_FLAG, 0);

  res = initialiseWindows(TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER, 0);
  if (res != 0) {
    _exit(1);
  }

  res = initialiseWindows(TRI_WIN_INITIAL_SET_MAX_STD_IO,(const char*)(&maxOpenFiles));
  if (res != 0) {
    _exit(1);
  }

  res = initialiseWindows(TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL, 0);
  if (res != 0) {
    _exit(1);
  }

  TRI_Application_Exit_SetExit(arangoimpExitFunction);

}

static void arangoimpExitFunction(int exitCode, void* data) {
  int res = 0;
  // ...........................................................................
  // TODO: need a terminate function for windows to be called and cleanup
  // any windows specific stuff.
  // ...........................................................................

  res = finaliseWindows(TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL, 0);

  if (res != 0) {
    exit(1);
  }

  exit(exitCode);
}
#else

static void arangoimpEntryFunction() {
}

static void arangoimpExitFunction(int exitCode, void* data) {
}

#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief main
////////////////////////////////////////////////////////////////////////////////

int main (int argc, char* argv[]) {

  int ret = EXIT_SUCCESS;

  arangoimpEntryFunction();

  TRIAGENS_C_INITIALISE(argc, argv);
  TRIAGENS_REST_INITIALISE(argc, argv);

  TRI_InitialiseLogging(false);

  BaseClient.setEndpointString(Endpoint::getDefaultEndpoint());

  // .............................................................................
  // parse the program options
  // .............................................................................

  ParseProgramOptions(argc, argv);

  // .............................................................................
  // set-up client connection
  // .............................................................................

  BaseClient.createEndpoint();

  if (BaseClient.endpointServer() == 0) {
    cerr << "invalid value for --server.endpoint ('" << BaseClient.endpointString() << "')" << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  ClientConnection = new V8ClientConnection(BaseClient.endpointServer(),
                                            BaseClient.databaseName(),
                                            BaseClient.username(),
                                            BaseClient.password(),
                                            BaseClient.requestTimeout(),
                                            BaseClient.connectTimeout(),
                                            ArangoClient::DEFAULT_RETRIES,
                                            BaseClient.sslProtocol(),
                                            false);

  if (! ClientConnection->isConnected() ||
      ClientConnection->getLastHttpReturnCode() != HttpResponse::OK) {
    cerr << "Could not connect to endpoint '" << BaseClient.endpointServer()->getSpecification()
         << "', database: '" << BaseClient.databaseName() << "'" << endl;
    cerr << "Error message: '" << ClientConnection->getErrorMessage() << "'" << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // successfully connected
  cout << "Connected to ArangoDB '" << BaseClient.endpointServer()->getSpecification()
       << "', version " << ClientConnection->getVersion() << ", database: '"
       << BaseClient.databaseName() << "', username: '" << BaseClient.username() << "'" << endl;

  cout << "----------------------------------------" << endl;
  cout << "database:         " << BaseClient.databaseName() << endl;
  cout << "collection:       " << CollectionName << endl;
  cout << "create:           " << (CreateCollection ? "yes" : "no") << endl;
  cout << "file:             " << FileName << endl;
  cout << "type:             " << TypeImport << endl;

  if (TypeImport == "csv") {
    cout << "quote:            " << Quote << endl;
    cout << "separator:        " << Separator << endl;
  }

  cout << "connect timeout:  " << BaseClient.connectTimeout() << endl;
  cout << "request timeout:  " << BaseClient.requestTimeout() << endl;
  cout << "----------------------------------------" << endl;

  ImportHelper ih(ClientConnection->getHttpClient(), ChunkSize);

  // create colletion
  if (CreateCollection) {
    ih.setCreateCollection(true);
  }

  // quote
  if (Quote.length() <= 1) {
    ih.setQuote(Quote);
  }
  else {
    cerr << "Wrong length of quote character." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // separator
  if (Separator.length() == 1) {
    ih.setSeparator(Separator);
  }
  else {
    cerr << "Separator must be exactly one character." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // collection name
  if (CollectionName == "") {
    cerr << "Collection name is missing." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // filename
  if (FileName == "") {
    cerr << "File name is missing." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  if (FileName != "-" && ! FileUtils::isRegularFile(FileName)) {
    cerr << "Cannot open file '" << FileName << "'" << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  // progress
  if (Progress) {
    ih.setProgress(true);
  }

  // import type
  bool ok = false;

  if (TypeImport == "csv") {
    cout << "Starting CSV import..." << endl;
    ok = ih.importDelimited(CollectionName, FileName, ImportHelper::CSV);
  }

  else if (TypeImport == "tsv") {
    cout << "Starting TSV import..." << endl;
    ih.setQuote("");
    ih.setSeparator("\\t");
    ok = ih.importDelimited(CollectionName, FileName, ImportHelper::TSV);
  }

  else if (TypeImport == "json") {
    cout << "Starting JSON import..." << endl;
    ok = ih.importJson(CollectionName, FileName);
  }

  else {
    cerr << "Wrong type '" << TypeImport << "'." << endl;
    TRI_EXIT_FUNCTION(EXIT_FAILURE, NULL);
  }

  cout << endl;

  // give information about import
  if (ok) {
    cout << "created:          " << ih.getImportedLines() << endl;
    cout << "errors:           " << ih.getErrorLines() << endl;
    cout << "total:            " << ih.getReadLines() << endl;
  }
  else {
    cerr << "error message:    " << ih.getErrorMessage() << endl;
  }

  TRIAGENS_REST_SHUTDOWN;

  arangoimpExitFunction(ret, NULL);

  return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This is a port of ManifestParser.cc from WebKit/WebCore/loader/appcache.

/*
 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "webkit/appcache/manifest_parser.h"

#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"

namespace appcache {

enum Mode {
  EXPLICIT,
  FALLBACK,
  ONLINE_WHITELIST,
  UNKNOWN,
};

Manifest::Manifest() : online_whitelist_all(false) {}

Manifest::~Manifest() {}

bool ParseManifest(const GURL& manifest_url, const char* data, int length,
                   Manifest& manifest) {
  static const std::wstring kSignature(L"CACHE MANIFEST");

  DCHECK(manifest.explicit_urls.empty());
  DCHECK(manifest.fallback_namespaces.empty());
  DCHECK(manifest.online_whitelist_namespaces.empty());
  DCHECK(!manifest.online_whitelist_all);

  Mode mode = EXPLICIT;

  std::wstring data_string;
  // TODO(jennb): cannot do UTF8ToWide(data, length, &data_string);
  // until UTF8ToWide uses 0xFFFD Unicode replacement character.
  base::CodepageToWide(std::string(data, length), base::kCodepageUTF8,
                       base::OnStringConversionError::SUBSTITUTE, &data_string);
  const wchar_t* p = data_string.c_str();
  const wchar_t* end = p + data_string.length();

  // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?"
  // Example: "CACHE MANIFEST #comment" is a valid signature.
  // Example: "CACHE MANIFEST;V2" is not.

  // When the input data starts with a UTF-8 Byte-Order-Mark
  // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a
  // Unicode BOM (U+FEFF). Skip a converted Unicode BOM if it exists.
  int bom_offset = 0;
  if (!data_string.empty() && data_string[0] == 0xFEFF) {
    bom_offset = 1;
    ++p;
  }

  if (p >= end ||
      data_string.compare(bom_offset, kSignature.length(), kSignature)) {
    return false;
  }

  p += kSignature.length();     // Skip past "CACHE MANIFEST"

  // Character after "CACHE MANIFEST" must be whitespace.
  if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
    return false;

  // Skip to the end of the line.
  while (p < end && *p != '\r' && *p != '\n')
    ++p;

  while (1) {
    // Skip whitespace
    while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
      ++p;

    if (p == end)
      break;

    const wchar_t* line_start = p;

    // Find the end of the line
    while (p < end && *p != '\r' && *p != '\n')
      ++p;

    // Check if we have a comment
    if (*line_start == '#')
      continue;

    // Get rid of trailing whitespace
    const wchar_t* tmp = p - 1;
    while (tmp > line_start && (*tmp == ' ' || *tmp == '\t'))
      --tmp;

    std::wstring line(line_start, tmp - line_start + 1);

    if (line == L"CACHE:") {
      mode = EXPLICIT;
    } else if (line == L"FALLBACK:") {
      mode = FALLBACK;
    } else if (line == L"NETWORK:") {
      mode = ONLINE_WHITELIST;
    } else if (*(line.end() - 1) == ':') {
      mode = UNKNOWN;
    } else if (mode == UNKNOWN) {
      continue;
    } else if (line == L"*" && mode == ONLINE_WHITELIST) {
      manifest.online_whitelist_all = true;
      continue;
    } else if (mode == EXPLICIT || mode == ONLINE_WHITELIST) {
      const wchar_t *line_p = line.c_str();
      const wchar_t *line_end = line_p + line.length();

      // Look for whitespace separating the URL from subsequent ignored tokens.
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      string16 url16;
      WideToUTF16(line.c_str(), line_p - line.c_str(), &url16);
      GURL url = manifest_url.Resolve(url16);
      if (!url.is_valid())
        continue;
      if (url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        url = url.ReplaceComponents(replacements);
      }

      // Scheme component must be the same as the manifest URL's.
      if (url.scheme() != manifest_url.scheme()) {
        continue;
      }

      // If the manifest's scheme is https:, then manifest URL must have same
      // origin as resulting absolute URL.
      if (mode == EXPLICIT && manifest_url.SchemeIsSecure() &&
          manifest_url.GetOrigin() != url.GetOrigin()) {
        continue;
      }

      if (mode == EXPLICIT) {
        manifest.explicit_urls.insert(url.spec());
      } else {
        manifest.online_whitelist_namespaces.push_back(url);
      }
    } else if (mode == FALLBACK) {
      const wchar_t* line_p = line.c_str();
      const wchar_t* line_end = line_p + line.length();

      // Look for whitespace separating the two URLs
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      if (line_p == line_end) {
        // There was no whitespace separating the URLs.
        continue;
      }

      string16 namespace_url16;
      WideToUTF16(line.c_str(), line_p - line.c_str(), &namespace_url16);
      GURL namespace_url = manifest_url.Resolve(namespace_url16);
      if (!namespace_url.is_valid())
        continue;
      if (namespace_url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        namespace_url = namespace_url.ReplaceComponents(replacements);
      }

      // Fallback namespace URL must have the same scheme, host and port
      // as the manifest's URL.
      if (manifest_url.GetOrigin() != namespace_url.GetOrigin()) {
        continue;
      }

      // Skip whitespace separating fallback namespace from URL.
      while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
        ++line_p;

      // Look for whitespace separating the URL from subsequent ignored tokens.
      const wchar_t* fallback_start = line_p;
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      string16 fallback_url16;
      WideToUTF16(fallback_start, line_p - fallback_start, &fallback_url16);
      GURL fallback_url = manifest_url.Resolve(fallback_url16);
      if (!fallback_url.is_valid())
        continue;
      if (fallback_url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        fallback_url = fallback_url.ReplaceComponents(replacements);
      }

      // Fallback entry URL must have the same scheme, host and port
      // as the manifest's URL.
      if (manifest_url.GetOrigin() != fallback_url.GetOrigin()) {
        continue;
      }

      // Store regardless of duplicate namespace URL. Only first match
      // will ever be used.
      manifest.fallback_namespaces.push_back(
          FallbackNamespace(namespace_url, fallback_url));
    } else {
      NOTREACHED();
    }
  }

  return true;
}

}  // namespace appcache
<commit_msg>appcache: document that the manifest parsing algorithm is fixed<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This is a port of ManifestParser.cc from WebKit/WebCore/loader/appcache.

/*
 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "webkit/appcache/manifest_parser.h"

#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"

namespace appcache {

enum Mode {
  EXPLICIT,
  FALLBACK,
  ONLINE_WHITELIST,
  UNKNOWN,
};

Manifest::Manifest() : online_whitelist_all(false) {}

Manifest::~Manifest() {}

bool ParseManifest(const GURL& manifest_url, const char* data, int length,
                   Manifest& manifest) {
  // This is an implementation of the parsing algorithm specified in
  // the HTML5 offline web application docs:
  //   http://www.w3.org/TR/html5/offline.html
  // Do not modify it without consulting those docs.
  // Though you might be tempted to convert these wstrings to UTF-8 or
  // string16, this implementation seems simpler given the constraints.

  static const std::wstring kSignature(L"CACHE MANIFEST");

  DCHECK(manifest.explicit_urls.empty());
  DCHECK(manifest.fallback_namespaces.empty());
  DCHECK(manifest.online_whitelist_namespaces.empty());
  DCHECK(!manifest.online_whitelist_all);

  Mode mode = EXPLICIT;

  std::wstring data_string;
  // TODO(jennb): cannot do UTF8ToWide(data, length, &data_string);
  // until UTF8ToWide uses 0xFFFD Unicode replacement character.
  base::CodepageToWide(std::string(data, length), base::kCodepageUTF8,
                       base::OnStringConversionError::SUBSTITUTE, &data_string);
  const wchar_t* p = data_string.c_str();
  const wchar_t* end = p + data_string.length();

  // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?"
  // Example: "CACHE MANIFEST #comment" is a valid signature.
  // Example: "CACHE MANIFEST;V2" is not.

  // When the input data starts with a UTF-8 Byte-Order-Mark
  // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a
  // Unicode BOM (U+FEFF). Skip a converted Unicode BOM if it exists.
  int bom_offset = 0;
  if (!data_string.empty() && data_string[0] == 0xFEFF) {
    bom_offset = 1;
    ++p;
  }

  if (p >= end ||
      data_string.compare(bom_offset, kSignature.length(), kSignature)) {
    return false;
  }

  p += kSignature.length();     // Skip past "CACHE MANIFEST"

  // Character after "CACHE MANIFEST" must be whitespace.
  if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
    return false;

  // Skip to the end of the line.
  while (p < end && *p != '\r' && *p != '\n')
    ++p;

  while (1) {
    // Skip whitespace
    while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
      ++p;

    if (p == end)
      break;

    const wchar_t* line_start = p;

    // Find the end of the line
    while (p < end && *p != '\r' && *p != '\n')
      ++p;

    // Check if we have a comment
    if (*line_start == '#')
      continue;

    // Get rid of trailing whitespace
    const wchar_t* tmp = p - 1;
    while (tmp > line_start && (*tmp == ' ' || *tmp == '\t'))
      --tmp;

    std::wstring line(line_start, tmp - line_start + 1);

    if (line == L"CACHE:") {
      mode = EXPLICIT;
    } else if (line == L"FALLBACK:") {
      mode = FALLBACK;
    } else if (line == L"NETWORK:") {
      mode = ONLINE_WHITELIST;
    } else if (*(line.end() - 1) == ':') {
      mode = UNKNOWN;
    } else if (mode == UNKNOWN) {
      continue;
    } else if (line == L"*" && mode == ONLINE_WHITELIST) {
      manifest.online_whitelist_all = true;
      continue;
    } else if (mode == EXPLICIT || mode == ONLINE_WHITELIST) {
      const wchar_t *line_p = line.c_str();
      const wchar_t *line_end = line_p + line.length();

      // Look for whitespace separating the URL from subsequent ignored tokens.
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      string16 url16;
      WideToUTF16(line.c_str(), line_p - line.c_str(), &url16);
      GURL url = manifest_url.Resolve(url16);
      if (!url.is_valid())
        continue;
      if (url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        url = url.ReplaceComponents(replacements);
      }

      // Scheme component must be the same as the manifest URL's.
      if (url.scheme() != manifest_url.scheme()) {
        continue;
      }

      // If the manifest's scheme is https:, then manifest URL must have same
      // origin as resulting absolute URL.
      if (mode == EXPLICIT && manifest_url.SchemeIsSecure() &&
          manifest_url.GetOrigin() != url.GetOrigin()) {
        continue;
      }

      if (mode == EXPLICIT) {
        manifest.explicit_urls.insert(url.spec());
      } else {
        manifest.online_whitelist_namespaces.push_back(url);
      }
    } else if (mode == FALLBACK) {
      const wchar_t* line_p = line.c_str();
      const wchar_t* line_end = line_p + line.length();

      // Look for whitespace separating the two URLs
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      if (line_p == line_end) {
        // There was no whitespace separating the URLs.
        continue;
      }

      string16 namespace_url16;
      WideToUTF16(line.c_str(), line_p - line.c_str(), &namespace_url16);
      GURL namespace_url = manifest_url.Resolve(namespace_url16);
      if (!namespace_url.is_valid())
        continue;
      if (namespace_url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        namespace_url = namespace_url.ReplaceComponents(replacements);
      }

      // Fallback namespace URL must have the same scheme, host and port
      // as the manifest's URL.
      if (manifest_url.GetOrigin() != namespace_url.GetOrigin()) {
        continue;
      }

      // Skip whitespace separating fallback namespace from URL.
      while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
        ++line_p;

      // Look for whitespace separating the URL from subsequent ignored tokens.
      const wchar_t* fallback_start = line_p;
      while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
        ++line_p;

      string16 fallback_url16;
      WideToUTF16(fallback_start, line_p - fallback_start, &fallback_url16);
      GURL fallback_url = manifest_url.Resolve(fallback_url16);
      if (!fallback_url.is_valid())
        continue;
      if (fallback_url.has_ref()) {
        GURL::Replacements replacements;
        replacements.ClearRef();
        fallback_url = fallback_url.ReplaceComponents(replacements);
      }

      // Fallback entry URL must have the same scheme, host and port
      // as the manifest's URL.
      if (manifest_url.GetOrigin() != fallback_url.GetOrigin()) {
        continue;
      }

      // Store regardless of duplicate namespace URL. Only first match
      // will ever be used.
      manifest.fallback_namespaces.push_back(
          FallbackNamespace(namespace_url, fallback_url));
    } else {
      NOTREACHED();
    }
  }

  return true;
}

}  // namespace appcache
<|endoftext|>


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

/* Author: Sachin Chitta */

// Original code from pr2_moveit_tutorials::motion_planning_api_tutorial.cpp
#include <pluginlib/class_loader.h>
#include <ros/ros.h>

// MoveIt!
#include <moveit/robot_model_loader/robot_model_loader.h>
#include <moveit/planning_interface/planning_interface.h>
#include <moveit/planning_scene/planning_scene.h>
#include <moveit/kinematic_constraints/utils.h>
#include <moveit_msgs/DisplayTrajectory.h>
#include <moveit_msgs/PlanningScene.h>

int main(int argc, char **argv)
{
  ros::init(argc, argv, "move_itomp");
  ros::AsyncSpinner spinner(1);
  spinner.start();
  ros::NodeHandle node_handle("~");

  // Setting up to start using a planner is pretty easy. Planners are
  // setup as plugins in MoveIt! and you can use the ROS pluginlib
  // interface to load any planner that you want to use. Before we
  // can load the planner, we need two objects, a RobotModel
  // and a PlanningScene.
  // We will start by instantiating a
  // `RobotModelLoader`_
  // object, which will look up
  // the robot description on the ROS parameter server and construct a
  // :moveit_core:`RobotModel` for us to use.
  //
  // .. _RobotModelLoader: http://docs.ros.org/api/moveit_ros_planning/html/classrobot__model__loader_1_1RobotModelLoader.html
  robot_model_loader::RobotModelLoader robot_model_loader("robot_description");
  robot_model::RobotModelPtr robot_model = robot_model_loader.getModel();

  // Using the :moveit_core:`RobotModel`, we can construct a
  // :planning_scene:`PlanningScene` that maintains the state of
  // the world (including the robot).
  planning_scene::PlanningScenePtr planning_scene(new planning_scene::PlanningScene(robot_model));

  // We will now construct a loader to load a planner, by name.
  // Note that we are using the ROS pluginlib library here.
  boost::scoped_ptr<pluginlib::ClassLoader<planning_interface::PlannerManager> > planner_plugin_loader;
  planning_interface::PlannerManagerPtr planner_instance;
  std::string planner_plugin_name;

  // We will get the name of planning plugin we want to load
  // from the ROS param server, and then load the planner
  // making sure to catch all exceptions.
  if (!node_handle.getParam("planning_plugin", planner_plugin_name))
    ROS_FATAL_STREAM("Could not find planner plugin name");
  try
  {
    planner_plugin_loader.reset(
        new pluginlib::ClassLoader<planning_interface::PlannerManager>("moveit_core",
            "planning_interface::PlannerManager"));
  }
  catch (pluginlib::PluginlibException& ex)
  {
    ROS_FATAL_STREAM("Exception while creating planning plugin loader " << ex.what());
  }
  try
  {
    planner_instance.reset(planner_plugin_loader->createUnmanagedInstance(planner_plugin_name));
    if (!planner_instance->initialize(robot_model, node_handle.getNamespace()))
      ROS_FATAL_STREAM("Could not initialize planner instance");
    ROS_INFO_STREAM("Using planning interface '" << planner_instance->getDescription() << "'");
  }
  catch (pluginlib::PluginlibException& ex)
  {
    const std::vector<std::string> &classes = planner_plugin_loader->getDeclaredClasses();
    std::stringstream ss;
    for (std::size_t i = 0; i < classes.size(); ++i)
      ss << classes[i] << " ";
    ROS_ERROR_STREAM(
        "Exception while loading planner '" << planner_plugin_name << "': " << ex.what() << std::endl << "Available plugins: " << ss.str());
  }

  /* Sleep a little to allow time to startup rviz, etc. */
  ros::WallDuration sleep_time(1.0);
  sleep_time.sleep();

  // We will now create a motion plan request
  // specifying the desired pose of the end-effector as input.
  planning_interface::MotionPlanRequest req;
  planning_interface::MotionPlanResponse res;
  robot_state::RobotState& start_state = planning_scene->getCurrentStateNonConst();

  // Set start_state
  const robot_state::JointModelGroup* joint_model_group = start_state.getJointModelGroup("whole_body");
  std::map<std::string, double> values;
  joint_model_group->getVariableDefaultPositions("pressup", values);
  //joint_model_group->getVariableDefaultPositions("standup", values);
  start_state.setVariablePositions(values);

  // Copy from start_state to req.start_state
  unsigned int num_joints = start_state.getVariableCount();
  req.start_state.joint_state.name = start_state.getVariableNames();
  req.start_state.joint_state.position.resize(num_joints);
  req.start_state.joint_state.velocity.resize(num_joints);
  req.start_state.joint_state.effort.resize(num_joints);
  memcpy(&req.start_state.joint_state.position[0], start_state.getVariablePositions(), sizeof(double) * num_joints);
  if (start_state.hasVelocities())
    memcpy(&req.start_state.joint_state.velocity[0], start_state.getVariableVelocities(), sizeof(double) * num_joints);
  else
    memset(&req.start_state.joint_state.velocity[0], 0, sizeof(double) * num_joints);
  if (start_state.hasAccelerations())
    memcpy(&req.start_state.joint_state.effort[0], start_state.getVariableAccelerations(), sizeof(double) * num_joints);
  else
    memset(&req.start_state.joint_state.effort[0], 0, sizeof(double) * num_joints);

  // Now, setup a goal state
  robot_state::RobotState goal_state(start_state);
  joint_model_group->getVariableDefaultPositions("pressup2", values);
  //joint_model_group->getVariableDefaultPositions("standup", values);
  goal_state.setVariablePositions(values);
  double jointValue = 1.0;
  //goal_state.setJointPositions("base_prismatic_joint_y", &jointValue);
  req.group_name = "whole_body";
  moveit_msgs::Constraints joint_goal = kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group);
  req.goal_constraints.push_back(joint_goal);

  // We now construct a planning context that encapsulate the scene,
  // the request and the response. We call the planner using this
  // planning context
  planning_interface::PlanningContextPtr context = planner_instance->getPlanningContext(planning_scene, req,
      res.error_code_);
  context->solve(res);
  if (res.error_code_.val != res.error_code_.SUCCESS)
  {
    ROS_ERROR("Could not compute plan successfully");
    return 0;
  }

  // Visualize the result
  // ^^^^^^^^^^^^^^^^^^^^
  ros::Publisher display_publisher = node_handle.advertise<moveit_msgs::DisplayTrajectory>(
      "/move_group/display_planned_path", 1, true);
  moveit_msgs::DisplayTrajectory display_trajectory;

  /* Visualize the trajectory */

  ROS_INFO("Visualizing the trajectory");
  moveit_msgs::MotionPlanResponse response;
  res.getMessage(response);

  display_trajectory.trajectory_start = response.trajectory_start;
  display_trajectory.trajectory.push_back(response.trajectory);
  display_publisher.publish(display_trajectory);

  sleep_time.sleep();
  ROS_INFO("Done");
  planner_instance.reset();

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

/* Author: Sachin Chitta */

// Original code from pr2_moveit_tutorials::motion_planning_api_tutorial.cpp
#include <pluginlib/class_loader.h>
#include <ros/ros.h>

// MoveIt!
#include <moveit/robot_model_loader/robot_model_loader.h>
#include <moveit/planning_interface/planning_interface.h>
#include <moveit/planning_scene/planning_scene.h>
#include <moveit/kinematic_constraints/utils.h>
#include <moveit_msgs/DisplayTrajectory.h>
#include <moveit_msgs/DisplayRobotState.h>
#include <moveit_msgs/PlanningScene.h>

int main(int argc, char **argv)
{
  ros::init(argc, argv, "move_itomp");
  ros::AsyncSpinner spinner(1);
  spinner.start();
  ros::NodeHandle node_handle("~");

  // Setting up to start using a planner is pretty easy. Planners are
  // setup as plugins in MoveIt! and you can use the ROS pluginlib
  // interface to load any planner that you want to use. Before we
  // can load the planner, we need two objects, a RobotModel
  // and a PlanningScene.
  // We will start by instantiating a
  // `RobotModelLoader`_
  // object, which will look up
  // the robot description on the ROS parameter server and construct a
  // :moveit_core:`RobotModel` for us to use.
  //
  // .. _RobotModelLoader: http://docs.ros.org/api/moveit_ros_planning/html/classrobot__model__loader_1_1RobotModelLoader.html
  robot_model_loader::RobotModelLoader robot_model_loader("robot_description");
  robot_model::RobotModelPtr robot_model = robot_model_loader.getModel();

  // Using the :moveit_core:`RobotModel`, we can construct a
  // :planning_scene:`PlanningScene` that maintains the state of
  // the world (including the robot).
  planning_scene::PlanningScenePtr planning_scene(new planning_scene::PlanningScene(robot_model));

  // We will now construct a loader to load a planner, by name.
  // Note that we are using the ROS pluginlib library here.
  boost::scoped_ptr<pluginlib::ClassLoader<planning_interface::PlannerManager> > planner_plugin_loader;
  planning_interface::PlannerManagerPtr planner_instance;
  std::string planner_plugin_name;

  // We will get the name of planning plugin we want to load
  // from the ROS param server, and then load the planner
  // making sure to catch all exceptions.
  if (!node_handle.getParam("planning_plugin", planner_plugin_name))
    ROS_FATAL_STREAM("Could not find planner plugin name");
  try
  {
    planner_plugin_loader.reset(
        new pluginlib::ClassLoader<planning_interface::PlannerManager>("moveit_core",
            "planning_interface::PlannerManager"));
  } catch (pluginlib::PluginlibException& ex)
  {
    ROS_FATAL_STREAM("Exception while creating planning plugin loader " << ex.what());
  }
  try
  {
    planner_instance.reset(planner_plugin_loader->createUnmanagedInstance(planner_plugin_name));
    if (!planner_instance->initialize(robot_model, node_handle.getNamespace()))
      ROS_FATAL_STREAM("Could not initialize planner instance");
    ROS_INFO_STREAM("Using planning interface '" << planner_instance->getDescription() << "'");
  } catch (pluginlib::PluginlibException& ex)
  {
    const std::vector<std::string> &classes = planner_plugin_loader->getDeclaredClasses();
    std::stringstream ss;
    for (std::size_t i = 0; i < classes.size(); ++i)
      ss << classes[i] << " ";
    ROS_ERROR_STREAM(
        "Exception while loading planner '" << planner_plugin_name << "': " << ex.what() << std::endl << "Available plugins: " << ss.str());
  }

  /* Sleep a little to allow time to startup rviz, etc. */
  ros::WallDuration sleep_time(1.0);
  sleep_time.sleep();

  // We will now create a motion plan request
  // specifying the desired pose of the end-effector as input.
  planning_interface::MotionPlanRequest req;
  planning_interface::MotionPlanResponse res;
  robot_state::RobotState& start_state = planning_scene->getCurrentStateNonConst();

  // Set start_state
  const robot_state::JointModelGroup* joint_model_group = start_state.getJointModelGroup("whole_body");
  std::map<std::string, double> values;
  joint_model_group->getVariableDefaultPositions("pressup", values);
  //joint_model_group->getVariableDefaultPositions("standup", values);
  start_state.setVariablePositions(values);

  // Copy from start_state to req.start_state
  unsigned int num_joints = start_state.getVariableCount();
  req.start_state.joint_state.name = start_state.getVariableNames();
  req.start_state.joint_state.position.resize(num_joints);
  req.start_state.joint_state.velocity.resize(num_joints);
  req.start_state.joint_state.effort.resize(num_joints);
  memcpy(&req.start_state.joint_state.position[0], start_state.getVariablePositions(), sizeof(double) * num_joints);
  if (start_state.hasVelocities())
    memcpy(&req.start_state.joint_state.velocity[0], start_state.getVariableVelocities(), sizeof(double) * num_joints);
  else
    memset(&req.start_state.joint_state.velocity[0], 0, sizeof(double) * num_joints);
  if (start_state.hasAccelerations())
    memcpy(&req.start_state.joint_state.effort[0], start_state.getVariableAccelerations(), sizeof(double) * num_joints);
  else
    memset(&req.start_state.joint_state.effort[0], 0, sizeof(double) * num_joints);

  // Now, setup a goal state
  robot_state::RobotState goal_state(start_state);
  joint_model_group->getVariableDefaultPositions("pressup2", values);
  //joint_model_group->getVariableDefaultPositions("standup", values);
  goal_state.setVariablePositions(values);
  double jointValue = 1.0;
  //goal_state.setJointPositions("base_prismatic_joint_y", &jointValue);
  req.group_name = "whole_body";
  moveit_msgs::Constraints joint_goal = kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group);
  req.goal_constraints.push_back(joint_goal);

  int num_variables = start_state.getVariableNames().size();
  ros::Publisher start_state_display_publisher = node_handle.advertise<moveit_msgs::DisplayRobotState>(
      "/move_itomp/display_start_state", 1, true);
  moveit_msgs::DisplayRobotState disp_start_state;
  disp_start_state.state.joint_state.header.frame_id = robot_model->getModelFrame();
  disp_start_state.state.joint_state.name = start_state.getVariableNames();
  disp_start_state.state.joint_state.position.resize(num_variables);
  memcpy(&disp_start_state.state.joint_state.position[0], start_state.getVariablePositions(),
      sizeof(double) * num_variables);
  disp_start_state.highlight_links.clear();
  const std::vector<std::string>& link_model_names = robot_model->getLinkModelNames();
  for (int i = 0; i < link_model_names.size(); ++i)
  {
    std_msgs::ColorRGBA color;
    color.a = 0.5;
    color.r = 0.0;
    color.g = 1.0;
    color.b = 0.5;
    moveit_msgs::ObjectColor obj_color;
    obj_color.id = link_model_names[i];
    obj_color.color = color;
    disp_start_state.highlight_links.push_back(obj_color);
  }
  start_state_display_publisher.publish(disp_start_state);

  ros::Publisher goal_state_display_publisher = node_handle.advertise<moveit_msgs::DisplayRobotState>(
      "/move_itomp/display_goal_state", 1, true);
  moveit_msgs::DisplayRobotState disp_goal_state;
  disp_goal_state.state.joint_state.header.frame_id = robot_model->getModelFrame();
  disp_goal_state.state.joint_state.name = goal_state.getVariableNames();
  disp_goal_state.state.joint_state.position.resize(num_variables);
  memcpy(&disp_goal_state.state.joint_state.position[0], goal_state.getVariablePositions(),
      sizeof(double) * num_variables);
  disp_goal_state.highlight_links.clear();
  for (int i = 0; i < link_model_names.size(); ++i)
  {
    std_msgs::ColorRGBA color;
    color.a = 0.5;
    color.r = 0.0;
    color.g = 0.5;
    color.b = 1.0;
    moveit_msgs::ObjectColor obj_color;
    obj_color.id = link_model_names[i];
    obj_color.color = color;
    disp_goal_state.highlight_links.push_back(obj_color);
  }
  goal_state_display_publisher.publish(disp_goal_state);

  // We now construct a planning context that encapsulate the scene,
  // the request and the response. We call the planner using this
  // planning context
  planning_interface::PlanningContextPtr context = planner_instance->getPlanningContext(planning_scene, req,
      res.error_code_);
  context->solve(res);
  if (res.error_code_.val != res.error_code_.SUCCESS)
  {
    ROS_ERROR("Could not compute plan successfully");
    return 0;
  }

  // Visualize the result
  // ^^^^^^^^^^^^^^^^^^^^
  ros::Publisher display_publisher = node_handle.advertise<moveit_msgs::DisplayTrajectory>(
      "/move_group/display_planned_path", 1, true);
  moveit_msgs::DisplayTrajectory display_trajectory;

  /* Visualize the trajectory */

  ROS_INFO("Visualizing the trajectory");
  moveit_msgs::MotionPlanResponse response;
  res.getMessage(response);

  display_trajectory.trajectory_start = response.trajectory_start;
  display_trajectory.trajectory.push_back(response.trajectory);
  display_publisher.publish(display_trajectory);

  sleep_time.sleep();
  ROS_INFO("Done");
  planner_instance.reset();

  return 0;
}
<|endoftext|>


<commit_before>//
//  Game.cpp
//  Phantasma
//
//  Created by Thomas Harte on 17/12/2013.
//  Copyright (c) 2013 Thomas Harte. All rights reserved.
//

#include "Game.h"
#include "GeometricObject.h"
#include "Matrix.h"
#include "VertexBuffer.h"

static float angle = 180.0f;

Game::Game(AreaMap *_areasByAreaID)
{
	hasReceivedTime = false;
	areasByAreaID = _areasByAreaID;
}

Game::~Game()
{
	delete areasByAreaID;
}

void Game::setAspectRatio(float aspectRatio)
{
	// create a projection matrix
	Matrix projectionMatrix = Matrix::projectionMatrix(60.0f, aspectRatio, 1.0f, 1000.0f);
	GeometricObject::setProjectionMatrix(projectionMatrix.contents);
}

void Game::draw()
{
	// just clear the display to a salmon colour
	glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	Matrix rotationMatrix = Matrix::rotationMatrix(angle, 1.0f, 1.0f, 0.0f);
	Matrix translationMatrix = Matrix::translationMatrix(0.0f, 0.0f, -5.0f);
	GeometricObject::setViewMatrix((translationMatrix * rotationMatrix).contents);

	GeometricObject::drawTestObject(vertexBuffer);
}

void Game::setupOpenGL()
{
	GeometricObject::setupOpenGL();

	if(!vertexBuffer)
		vertexBuffer = GeometricObject::newVertexBuffer();
}

void Game::advanceToTime(uint32_t millisecondsSinceArbitraryMoment)
{
	if(!hasReceivedTime)
	{
		timeOfLastTick = millisecondsSinceArbitraryMoment;
		hasReceivedTime = true;
		return;
	}

	// so how many milliseconds is that since we last paid attention?
	uint32_t timeDifference = millisecondsSinceArbitraryMoment - timeOfLastTick;

	// TODO: player movement updates out here
	angle += (float)timeDifference / 10.0f;

	// we'll advance at 50hz, which makes for some easy integer arithmetic here
	while(timeDifference > 20)
	{
		timeOfLastTick += 20;
		timeDifference -= 20;

		// TODO: in-game timed events here
	}
}
<commit_msg>Made sure vertexBuffer is initially NULL.<commit_after>//
//  Game.cpp
//  Phantasma
//
//  Created by Thomas Harte on 17/12/2013.
//  Copyright (c) 2013 Thomas Harte. All rights reserved.
//

#include "Game.h"
#include "GeometricObject.h"
#include "Matrix.h"
#include "VertexBuffer.h"

static float angle = 180.0f;

Game::Game(AreaMap *_areasByAreaID)
{
	hasReceivedTime = false;
	areasByAreaID = _areasByAreaID;
	vertexBuffer = nullptr;
}

Game::~Game()
{
	delete areasByAreaID;
}

void Game::setAspectRatio(float aspectRatio)
{
	// create a projection matrix
	Matrix projectionMatrix = Matrix::projectionMatrix(60.0f, aspectRatio, 1.0f, 1000.0f);
	GeometricObject::setProjectionMatrix(projectionMatrix.contents);
}

void Game::draw()
{
	// just clear the display to a salmon colour
	glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	Matrix rotationMatrix = Matrix::rotationMatrix(angle, 1.0f, 1.0f, 0.0f);
	Matrix translationMatrix = Matrix::translationMatrix(0.0f, 0.0f, -5.0f);
	GeometricObject::setViewMatrix((translationMatrix * rotationMatrix).contents);

	GeometricObject::drawTestObject(vertexBuffer);
}

void Game::setupOpenGL()
{
	GeometricObject::setupOpenGL();

	if(!vertexBuffer)
		vertexBuffer = GeometricObject::newVertexBuffer();
}

void Game::advanceToTime(uint32_t millisecondsSinceArbitraryMoment)
{
	if(!hasReceivedTime)
	{
		timeOfLastTick = millisecondsSinceArbitraryMoment;
		hasReceivedTime = true;
		return;
	}

	// so how many milliseconds is that since we last paid attention?
	uint32_t timeDifference = millisecondsSinceArbitraryMoment - timeOfLastTick;

	// TODO: player movement updates out here
	angle += (float)timeDifference / 10.0f;

	// we'll advance at 50hz, which makes for some easy integer arithmetic here
	while(timeDifference > 20)
	{
		timeOfLastTick += 20;
		timeDifference -= 20;

		// TODO: in-game timed events here
	}
}
<|endoftext|>