<commit_before>/*
* Copyright (c)2015 Oasis LMF Limited
* 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 original author of this software 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.
*/

#pragma once

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#if defined(_MSC_VER) || defined(__MINGW32__)
#include <fcntl.h>
#include <io.h>
#define flseek _fseeki64
#define fltell _ftelli64
#else
#define flseek fseek
#define fltell ftell
#endif

//const int mean_idx = 1 << 24;
//const int std_dev_idx = -1;
const int mean_idx = -1;
const int std_dev_idx = -2;

// Stream types
const unsigned int cdfstream_id = 0;		// high byte is zero
const unsigned int gulstream_id = 1 << 24;
const unsigned int fmstream_id = 2 << 24;
const unsigned int outputstream_id = 3 << 24;

const unsigned int streamno_mask = 0x00FFFFFF;

#pragma pack(push, 1)
struct damagecdfrec {
        int event_id;
        int areaperil_id;
        int vulnerabilty_id;
};

struct damagebindictionary {
        int bin_index;
        float bin_from;
        float bin_to;
        float interpolation;
        int interval_type;
};

struct gulSampleslevel {
	int event_id;
	int item_id;
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};

struct gulSampleslevelEventRec {
	int item_id;
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};
struct gulSampleslevelHeader {
	int event_id;
	int item_id;
};

struct gulSampleslevelRec {
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};

struct fmlevelhdr {
	int event_id;
	int prog_id;
	int layer_id;
	int output_id;
};

struct fmlevelrec {
	int sidx;
	float loss;
};

struct fmdata {
	int item_id;
	int agg_id;
	int prog_id;
	int level_id;
	int policytc_id;
	int layer_id;
	int calcrule_id;
	int allocrule_id;
	float deductible;
	float limits;
	float share_prop_of_lim;
	float deductible_prop_of_loss;
	float limit_prop_of_loss;
	float deductible_prop_of_tiv;
	float limit_prop_of_tiv;
	float deductible_prop_of_limit;
};

struct exposure{
	int item_id;
	int areaperil_id;
	int vulnerability_id;
	int group_id;
	float tiv;
};

#pragma pack(pop)
// -- UTILITY inline functions

inline void initstreams(std::string inFile_, std::string outFile_)
{

   if (inFile_.length() > 0){
        if (freopen(inFile_.c_str(), "rb", stdin) == NULL) {
            std::cerr << "Error opening " << inFile_ << "\n";
            exit(-1);
         }
   }

   if (outFile_.length() > 0){
       if (freopen(outFile_.c_str(), "wb", stdout) == NULL) {
           std::cerr << "Error opening " << outFile_ << "\n";
           exit(-1);
        }
   }

#if defined(_MSC_VER) || defined(__MINGW32__)
	_setmode(_fileno(stdout), O_BINARY);
	_setmode(_fileno(stdin), O_BINARY);
#else
	freopen(NULL, "rb", stdin);
	freopen(NULL, "wb", stdout);
#endif

}
<commit_msg>added header to oasis.hpp<commit_after>/*
* Copyright (c)2015 Oasis LMF Limited
* 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 original author of this software 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.
*/

#pragma once

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <stdlib.h>

#if defined(_MSC_VER) || defined(__MINGW32__)
#include <fcntl.h>
#include <io.h>
#define flseek _fseeki64
#define fltell _ftelli64
#else
#define flseek fseek
#define fltell ftell
#endif

//const int mean_idx = 1 << 24;
//const int std_dev_idx = -1;
const int mean_idx = -1;
const int std_dev_idx = -2;

// Stream types
const unsigned int cdfstream_id = 0;		// high byte is zero
const unsigned int gulstream_id = 1 << 24;
const unsigned int fmstream_id = 2 << 24;
const unsigned int outputstream_id = 3 << 24;

const unsigned int streamno_mask = 0x00FFFFFF;

#pragma pack(push, 1)
struct damagecdfrec {
        int event_id;
        int areaperil_id;
        int vulnerabilty_id;
};

struct damagebindictionary {
        int bin_index;
        float bin_from;
        float bin_to;
        float interpolation;
        int interval_type;
};

struct gulSampleslevel {
	int event_id;
	int item_id;
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};

struct gulSampleslevelEventRec {
	int item_id;
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};
struct gulSampleslevelHeader {
	int event_id;
	int item_id;
};

struct gulSampleslevelRec {
	int sidx;		// This has be stored for thresholds cannot be implied
	float gul;		// may want to cut down to singe this causes 4 byte padding for allignment
};

struct fmlevelhdr {
	int event_id;
	int prog_id;
	int layer_id;
	int output_id;
};

struct fmlevelrec {
	int sidx;
	float loss;
};

struct fmdata {
	int item_id;
	int agg_id;
	int prog_id;
	int level_id;
	int policytc_id;
	int layer_id;
	int calcrule_id;
	int allocrule_id;
	float deductible;
	float limits;
	float share_prop_of_lim;
	float deductible_prop_of_loss;
	float limit_prop_of_loss;
	float deductible_prop_of_tiv;
	float limit_prop_of_tiv;
	float deductible_prop_of_limit;
};

struct exposure{
	int item_id;
	int areaperil_id;
	int vulnerability_id;
	int group_id;
	float tiv;
};

#pragma pack(pop)
// -- UTILITY inline functions

inline void initstreams(std::string inFile_, std::string outFile_)
{

   if (inFile_.length() > 0){
        if (freopen(inFile_.c_str(), "rb", stdin) == NULL) {
            std::cerr << "Error opening " << inFile_ << "\n";
            exit(-1);
         }
   }

   if (outFile_.length() > 0){
       if (freopen(outFile_.c_str(), "wb", stdout) == NULL) {
           std::cerr << "Error opening " << outFile_ << "\n";
           exit(-1);
        }
   }

#if defined(_MSC_VER) || defined(__MINGW32__)
	_setmode(_fileno(stdout), O_BINARY);
	_setmode(_fileno(stdin), O_BINARY);
#else
	freopen(NULL, "rb", stdin);
	freopen(NULL, "wb", stdout);
#endif

}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <QDialogButtonBox>

#include <nitroshare/application.h>
#include <nitroshare/setting.h>
#include <nitroshare/settingsregistry.h>

#include "booleansettingwidget.h"
#include "settingsdialog.h"
#include "stringsettingwidget.h"

SettingsDialog::SettingsDialog(Application *application)
    : mApplication(application),
      mLayout(new QVBoxLayout),
      mSpacer(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding))
{
    setWindowTitle(tr("Settings"));

    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingAdded, this, &SettingsDialog::onSettingAdded);
    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingRemoved, this, &SettingsDialog::onSettingRemoved);

    // Add exisiting settings
    foreach (Setting *setting, mApplication->settingsRegistry()->settings()) {
        onSettingAdded(setting);
    }

    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &SettingsDialog::reject);

    mLayout->addItem(mSpacer);
    mLayout->addWidget(buttonBox);
    setLayout(mLayout);
}

void SettingsDialog::accept()
{
    // Set the value for all settings by retrieving the value from the widget
    mApplication->settingsRegistry()->begin();
    for (QHash<Setting*, SettingWidget*>::const_iterator i = mWidgets.constBegin();
            i != mWidgets.constEnd(); ++i) {
        mApplication->settingsRegistry()->setValue(
            i.key()->name(),
            i.value()->value()
        );
    }
    mApplication->settingsRegistry()->end();

    // Close the dialog
    QDialog::accept();
}

void SettingsDialog::onSettingAdded(Setting *setting)
{
    // Only add settings that are not hidden
    if (setting->isHidden()) {
        return;
    }

    SettingWidget *widget = nullptr;

    // Create the widget of the appropriate type
    switch (setting->type()) {
    case Setting::String:
    case Setting::Integer:
        widget = new StringSettingWidget(setting);
        break;
    case Setting::Boolean:
        widget = new BooleanSettingWidget(setting);
        break;
    }

    if (widget) {

        // Set the initial value
        widget->setValue(mApplication->settingsRegistry()->value(setting->name()));

        // Add the widget
        mLayout->insertWidget(mWidgets.count(), widget);
        mWidgets.insert(setting, widget);
    }
}

void SettingsDialog::onSettingRemoved(Setting *setting)
{
    SettingWidget *widget = mWidgets.take(setting);
    mLayout->removeWidget(widget);
    delete widget;
}
<commit_msg>Add frame above buttons in settings dialog.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <QDialogButtonBox>
#include <QFrame>

#include <nitroshare/application.h>
#include <nitroshare/setting.h>
#include <nitroshare/settingsregistry.h>

#include "booleansettingwidget.h"
#include "settingsdialog.h"
#include "stringsettingwidget.h"

SettingsDialog::SettingsDialog(Application *application)
    : mApplication(application),
      mLayout(new QVBoxLayout),
      mSpacer(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding))
{
    setWindowTitle(tr("Settings"));

    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingAdded, this, &SettingsDialog::onSettingAdded);
    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingRemoved, this, &SettingsDialog::onSettingRemoved);

    // Add exisiting settings
    foreach (Setting *setting, mApplication->settingsRegistry()->settings()) {
        onSettingAdded(setting);
    }

    QFrame *frame = new QFrame;
    frame->setFrameShape(QFrame::HLine);
    frame->setFrameShadow(QFrame::Sunken);

    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &SettingsDialog::reject);

    mLayout->addItem(mSpacer);
    mLayout->addWidget(frame);
    mLayout->addWidget(buttonBox);
    setLayout(mLayout);
}

void SettingsDialog::accept()
{
    // Set the value for all settings by retrieving the value from the widget
    mApplication->settingsRegistry()->begin();
    for (QHash<Setting*, SettingWidget*>::const_iterator i = mWidgets.constBegin();
            i != mWidgets.constEnd(); ++i) {
        mApplication->settingsRegistry()->setValue(
            i.key()->name(),
            i.value()->value()
        );
    }
    mApplication->settingsRegistry()->end();

    // Close the dialog
    QDialog::accept();
}

void SettingsDialog::onSettingAdded(Setting *setting)
{
    // Only add settings that are not hidden
    if (setting->isHidden()) {
        return;
    }

    SettingWidget *widget = nullptr;

    // Create the widget of the appropriate type
    switch (setting->type()) {
    case Setting::String:
    case Setting::Integer:
        widget = new StringSettingWidget(setting);
        break;
    case Setting::Boolean:
        widget = new BooleanSettingWidget(setting);
        break;
    }

    if (widget) {

        // Set the initial value
        widget->setValue(mApplication->settingsRegistry()->value(setting->name()));

        // Add the widget
        mLayout->insertWidget(mWidgets.count(), widget);
        mWidgets.insert(setting, widget);
    }
}

void SettingsDialog::onSettingRemoved(Setting *setting)
{
    SettingWidget *widget = mWidgets.take(setting);
    mLayout->removeWidget(widget);
    delete widget;
}
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2022 Inviwo Foundation
 * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/basegl/processors/imageprocessing/imageoverlaygl.h>

#include <inviwo/core/datastructures/image/imagetypes.h>   // for ImageType, ImageType::ColorDep...
#include <inviwo/core/interaction/events/event.h>          // for Event
#include <inviwo/core/interaction/events/resizeevent.h>    // for ResizeEvent
#include <inviwo/core/ports/imageport.h>                   // for ImageInport, ImageOutport
#include <inviwo/core/processors/processor.h>              // for Processor
#include <inviwo/core/processors/processorinfo.h>          // for ProcessorInfo
#include <inviwo/core/processors/processorstate.h>         // for CodeState, CodeState::Experime...
#include <inviwo/core/processors/processortags.h>          // for Tags, Tags::GL
#include <inviwo/core/properties/boolcompositeproperty.h>  // for BoolCompositeProperty
#include <inviwo/core/properties/boolproperty.h>           // for BoolProperty
#include <inviwo/core/properties/compositeproperty.h>      // for CompositeProperty
#include <inviwo/core/properties/invalidationlevel.h>      // for InvalidationLevel, Invalidatio...
#include <inviwo/core/properties/optionproperty.h>         // for OptionPropertyOption, OptionPr...
#include <inviwo/core/properties/ordinalproperty.h>        // for FloatVec2Property, IntVec2Prop...
#include <inviwo/core/properties/propertysemantics.h>      // for PropertySemantics, PropertySem...
#include <inviwo/core/util/glmvec.h>                       // for vec2, ivec2, ivec4, vec4, uvec2
#include <inviwo/core/util/staticstring.h>                 // for operator+
#include <modules/basegl/viewmanager.h>                    // for ViewManager, ViewManager::View
#include <modules/opengl/openglutils.h>                    // for BlendModeState, DepthFuncState
#include <modules/opengl/shader/shader.h>                  // for Shader, Shader::Build
#include <modules/opengl/shader/shaderobject.h>            // for ShaderObject
#include <modules/opengl/texture/textureunit.h>            // for TextureUnit
#include <modules/opengl/texture/textureutils.h>           // for activateTargetAndCopySource

#include <cstddef>  // for size_t

#include <glm/vec2.hpp>  // for operator*, vec<>::(anonymous)

namespace inviwo {
class Deserializer;
class Outport;

const ProcessorInfo ImageOverlayGL::processorInfo_{
    "org.inviwo.ImageOverlayGL",  // Class identifier
    "Image Overlay",              // Display name
    "Image Operation",            // Category
    CodeState::Experimental,      // Code state
    Tags::GL,                     // Tags
};
const ProcessorInfo ImageOverlayGL::getProcessorInfo() const { return processorInfo_; }

OverlayProperty::OverlayProperty(std::string identifier, std::string displayName,
                                 InvalidationLevel invalidationLevel, PropertySemantics semantics)
    : CompositeProperty(identifier, displayName, invalidationLevel, semantics)
    , pos_("position", "Position", vec2(0.25f), vec2(0.0f), vec2(1.0f), vec2(0.01f),
           InvalidationLevel::Valid)
    , size_("size", "Size", vec2(0.48f), vec2(0.0f), vec2(1.0f), vec2(0.01f),
            InvalidationLevel::Valid)
    , absolutePos_("absolutePos", "Position (absolute)", ivec2(10), ivec2(0), ivec2(2048), ivec2(1),
                   InvalidationLevel::Valid)
    , absoluteSize_("absoluteSize", "Size (absolute)", ivec2(10), ivec2(0), ivec2(2048), ivec2(1),
                    InvalidationLevel::Valid)
    , anchorPos_("anchor", "Anchor", vec2(0.0f), vec2(-1.0f), vec2(1.0f), vec2(0.01f),
                 InvalidationLevel::Valid)
    , blendMode_("blendMode", "Blending Mode",
                 {{"replace", "Replace", BlendMode::Replace}, {"over", "Blend", BlendMode::Over}},
                 1, InvalidationLevel::InvalidResources)
    , positioningMode_("positioningMode", "Positioning Mode",
                       {{"absolute", "Absolute", Positioning::Absolute},
                        {"relative", "Relative", Positioning::Relative}},
                       1, InvalidationLevel::Valid)
    , sizeMode_("sizeMode", "Size Mode",
                {{"absolute", "Absolute", Positioning::Absolute},
                 {"relative", "Relative", Positioning::Relative}},
                1, InvalidationLevel::Valid)
    , viewDimensions_(-1, -1)
    , viewport_(0, 0, 1, 1)
    , isDeserializing_(false) {

    auto updateCallback = [&]() { updateViewport(); };

    positioningMode_.onChange([&]() {
        pos_.setVisible(positioningMode_.get() == Positioning::Relative);
        absolutePos_.setVisible(positioningMode_.get() == Positioning::Absolute);
        updateViewport();
    });
    pos_.onChange(updateCallback);
    absolutePos_.onChange(updateCallback);
    sizeMode_.onChange([&]() {
        size_.setVisible(sizeMode_.get() == Positioning::Relative);
        absoluteSize_.setVisible(sizeMode_.get() == Positioning::Absolute);
        updateViewport();
    });
    size_.onChange(updateCallback);
    absoluteSize_.onChange(updateCallback);
    anchorPos_.onChange(updateCallback);
    blendMode_.onChange(updateCallback);

    addProperty(positioningMode_);
    addProperty(pos_);
    addProperty(absolutePos_);

    addProperty(sizeMode_);
    addProperty(size_);
    addProperty(absoluteSize_);

    addProperty(anchorPos_);
    addProperty(blendMode_);

    updateVisibilityState();
}

void OverlayProperty::setViewDimensions(ivec2 viewDim) {
    viewDimensions_ = viewDim;
    updateViewport();
}

const ivec4& OverlayProperty::getViewport() const { return viewport_; }

auto OverlayProperty::getBlendMode() const -> BlendMode { return blendMode_; }

GLint OverlayProperty::getBlendModeGL() const { return static_cast<GLint>(blendMode_.get()); }

void OverlayProperty::deserialize(Deserializer& d) {
    isDeserializing_ = true;
    CompositeProperty::deserialize(d);
    isDeserializing_ = false;
    updateVisibilityState();
}

void OverlayProperty::updateViewport() {
    if (isDeserializing_) return;

    vec2 viewDim(viewDimensions_);

    // determine size and center position first (absolute coords)
    vec2 pos(viewDim * 0.25f);
    vec2 size(viewDim * 0.5f);

    if (positioningMode_.get() == Positioning::Absolute) {
        pos = vec2(absolutePos_.get());
    } else {
        pos = pos_.get() * viewDim;
    }
    if (sizeMode_.get() == Positioning::Absolute) {
        size = vec2(absoluteSize_.get());
    } else {
        size = size_.get() * viewDim;
    }

    // consider anchor position
    vec2 shift = 0.5f * size * (anchorPos_.get() + vec2(1.0f, 1.0f));
    pos.x -= shift.x;
    // negate y axis
    pos.y = viewDim.y - (pos.y + shift.y);

    // use pixel aligned positions for best results
    viewport_ = ivec4(pos.x, pos.y, size.x, size.y);
    // mark the composite property as modified. This will in turn trigger a
    // resize event in ImageOverlayGL::onStatusChange().
    CompositeProperty::propertyModified();
    // If an ImagePort with a non-resizeable image is connected, there
    // will be no change in the inport and the ImageOverlay processor
    // is not re-evaluated.
    // Thus we need to invalidate the property as well.
    CompositeProperty::invalidate(InvalidationLevel::InvalidOutput, this);
}

void OverlayProperty::updateVisibilityState() {
    size_.setVisible(sizeMode_.get() == Positioning::Relative);
    absoluteSize_.setVisible(sizeMode_.get() == Positioning::Absolute);

    pos_.setVisible(positioningMode_.get() == Positioning::Relative);
    absolutePos_.setVisible(positioningMode_.get() == Positioning::Absolute);
}

ImageOverlayGL::ImageOverlayGL()
    : Processor()
    , inport_("inport")
    , overlayPort_("overlay")
    , outport_("outport")
    , enabled_("enabled", "Overlay Enabled", true)
    , overlayInteraction_("overlayInteraction", "Overlay Interaction", false)
    , passThroughEvent_("passTroughEvent", "Pass Events on to Main View", false)
    , overlayProperty_("overlay", "Overlay")
    , border_("border", "Border", true)
    , borderColor_("borderColor", "Color", vec4(0.0f, 0.0f, 0.0f, 1.0f), vec4(0.0f), vec4(1.0f))
    , borderWidth_("borderWidth", "Width", 2, 0, 100)
    , shader_("img_identity.vert", "img_overlay.frag", Shader::Build::No)
    , viewManager_()
    , currentDim_(0u, 0u) {
    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });
    overlayPort_.setOptional(true);
    addPort(inport_);
    addPort(overlayPort_);

    overlayPort_.onConnect([this]() {
        viewManager_.push_back(overlayProperty_.getViewport());
        onStatusChange();
    });

    overlayPort_.onDisconnect([this]() { viewManager_.clear(); });

    addPort(outport_);

    addProperty(enabled_);
    addProperty(overlayInteraction_);
    addProperty(passThroughEvent_);
    addProperty(overlayProperty_);

    borderColor_.setSemantics(PropertySemantics::Color);

    border_.addProperty(borderColor_);
    border_.addProperty(borderWidth_);
    addProperty(border_);

    overlayProperty_.onChange([this]() { onStatusChange(); });
}

ImageOverlayGL::~ImageOverlayGL() = default;

void ImageOverlayGL::propagateEvent(Event* event, Outport* source) {
    if (event->hasVisitedProcessor(this)) return;
    event->markAsVisited(this);

    invokeEvent(event);
    if (event->hasBeenUsed()) return;

    if (event->hash() == ResizeEvent::chash()) {
        auto resizeEvent = static_cast<ResizeEvent*>(event);

        updateViewports(resizeEvent->size(), true);
        inport_.propagateEvent(resizeEvent);

        if (overlayPort_.isConnected()) {
            ResizeEvent e(uvec2(viewManager_[0].size));
            overlayPort_.propagateEvent(&e);
        }
    } else {
        if (overlayInteraction_.get() && overlayPort_.isConnected()) {
            bool overlayHandlesEvent =
                (viewManager_.propagateEvent(event, [&](Event* newEvent, size_t /*ind*/) {
                    overlayPort_.propagateEvent(newEvent);
                }));

            if ((overlayHandlesEvent && !passThroughEvent_.get()) || event->hasBeenUsed()) {
                return;
            }
        }

        if (event->shouldPropagateTo(&inport_, this, source)) {
            inport_.propagateEvent(event);
        }
    }
}

void ImageOverlayGL::onStatusChange() {
    if (overlayPort_.isConnected()) {
        // update viewport stored in view manager
        viewManager_.replace(0, overlayProperty_.getViewport());

        ResizeEvent e(uvec2(viewManager_[0].size));
        overlayPort_.propagateEvent(&e, overlayPort_.getConnectedOutport());
    }
}

void ImageOverlayGL::initializeResources() {
    if (overlayProperty_.getBlendMode() == OverlayProperty::BlendMode::Replace) {
        shader_.getFragmentShaderObject()->addShaderDefine("BLENDMODE_REPLACE");
    } else {
        shader_.getFragmentShaderObject()->removeShaderDefine("BLENDMODE_REPLACE");
    }

    shader_.build();
}

void ImageOverlayGL::process() {
    if (!enabled_.get()) {
        outport_.setData(inport_.getData());
        return;
    }

    utilgl::activateTargetAndCopySource(outport_, inport_, inviwo::ImageType::ColorDepthPicking);

    if (overlayPort_.isReady()) {  // draw overlay
        utilgl::DepthFuncState depthFunc(GL_ALWAYS);

        ivec4 viewport = overlayProperty_.getViewport();
        int borderWidth = 0;
        if (border_.isChecked()) {
            // adjust viewport by border width
            borderWidth = borderWidth_.get();
            viewport += ivec4(-borderWidth, -borderWidth, 2 * borderWidth, 2 * borderWidth);
        }

        utilgl::BlendModeState blendMode(overlayProperty_.getBlendModeGL(), GL_ONE_MINUS_SRC_ALPHA);

        TextureUnit colorUnit, depthUnit, pickingUnit;
        shader_.activate();
        shader_.setUniform("color_", colorUnit.getUnitNumber());
        shader_.setUniform("depth_", depthUnit.getUnitNumber());
        shader_.setUniform("picking_", pickingUnit.getUnitNumber());
        shader_.setUniform("borderWidth_", borderWidth);
        shader_.setUniform("borderColor_", borderColor_.get());
        shader_.setUniform("viewport_", vec4(viewport));

        utilgl::bindTextures(overlayPort_, colorUnit, depthUnit, pickingUnit);

        utilgl::ViewportState viewportState(viewport);
        utilgl::singleDrawImagePlaneRect();
        shader_.deactivate();
    }
    utilgl::deactivateCurrentTarget();
}

void ImageOverlayGL::updateViewports(ivec2 dim, bool force) {
    if (!force && (currentDim_ == dim)) return;  // no changes

    overlayProperty_.setViewDimensions(dim);
    currentDim_ = dim;
}

}  // namespace inviwo
<commit_msg>BaseGL: ImageOverlayGL rendercontext fix<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2022 Inviwo Foundation
 * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/basegl/processors/imageprocessing/imageoverlaygl.h>

#include <inviwo/core/datastructures/image/imagetypes.h>   // for ImageType, ImageType::ColorDep...
#include <inviwo/core/interaction/events/event.h>          // for Event
#include <inviwo/core/interaction/events/resizeevent.h>    // for ResizeEvent
#include <inviwo/core/ports/imageport.h>                   // for ImageInport, ImageOutport
#include <inviwo/core/processors/processor.h>              // for Processor
#include <inviwo/core/processors/processorinfo.h>          // for ProcessorInfo
#include <inviwo/core/processors/processorstate.h>         // for CodeState, CodeState::Experime...
#include <inviwo/core/processors/processortags.h>          // for Tags, Tags::GL
#include <inviwo/core/properties/boolcompositeproperty.h>  // for BoolCompositeProperty
#include <inviwo/core/properties/boolproperty.h>           // for BoolProperty
#include <inviwo/core/properties/compositeproperty.h>      // for CompositeProperty
#include <inviwo/core/properties/invalidationlevel.h>      // for InvalidationLevel, Invalidatio...
#include <inviwo/core/properties/optionproperty.h>         // for OptionPropertyOption, OptionPr...
#include <inviwo/core/properties/ordinalproperty.h>        // for FloatVec2Property, IntVec2Prop...
#include <inviwo/core/properties/propertysemantics.h>      // for PropertySemantics, PropertySem...
#include <inviwo/core/util/glmvec.h>                       // for vec2, ivec2, ivec4, vec4, uvec2
#include <inviwo/core/util/staticstring.h>                 // for operator+
#include <inviwo/core/util/rendercontext.h>                // for RenderContext
#include <modules/basegl/viewmanager.h>                    // for ViewManager, ViewManager::View
#include <modules/opengl/openglutils.h>                    // for BlendModeState, DepthFuncState
#include <modules/opengl/shader/shader.h>                  // for Shader, Shader::Build
#include <modules/opengl/shader/shaderobject.h>            // for ShaderObject
#include <modules/opengl/texture/textureunit.h>            // for TextureUnit
#include <modules/opengl/texture/textureutils.h>           // for activateTargetAndCopySource

#include <cstddef>  // for size_t

#include <glm/vec2.hpp>  // for operator*, vec<>::(anonymous)

namespace inviwo {
class Deserializer;
class Outport;

const ProcessorInfo ImageOverlayGL::processorInfo_{
    "org.inviwo.ImageOverlayGL",  // Class identifier
    "Image Overlay",              // Display name
    "Image Operation",            // Category
    CodeState::Experimental,      // Code state
    Tags::GL,                     // Tags
};
const ProcessorInfo ImageOverlayGL::getProcessorInfo() const { return processorInfo_; }

OverlayProperty::OverlayProperty(std::string identifier, std::string displayName,
                                 InvalidationLevel invalidationLevel, PropertySemantics semantics)
    : CompositeProperty(identifier, displayName, invalidationLevel, semantics)
    , pos_("position", "Position", vec2(0.25f), vec2(0.0f), vec2(1.0f), vec2(0.01f),
           InvalidationLevel::Valid)
    , size_("size", "Size", vec2(0.48f), vec2(0.0f), vec2(1.0f), vec2(0.01f),
            InvalidationLevel::Valid)
    , absolutePos_("absolutePos", "Position (absolute)", ivec2(10), ivec2(0), ivec2(2048), ivec2(1),
                   InvalidationLevel::Valid)
    , absoluteSize_("absoluteSize", "Size (absolute)", ivec2(10), ivec2(0), ivec2(2048), ivec2(1),
                    InvalidationLevel::Valid)
    , anchorPos_("anchor", "Anchor", vec2(0.0f), vec2(-1.0f), vec2(1.0f), vec2(0.01f),
                 InvalidationLevel::Valid)
    , blendMode_("blendMode", "Blending Mode",
                 {{"replace", "Replace", BlendMode::Replace}, {"over", "Blend", BlendMode::Over}},
                 1, InvalidationLevel::InvalidResources)
    , positioningMode_("positioningMode", "Positioning Mode",
                       {{"absolute", "Absolute", Positioning::Absolute},
                        {"relative", "Relative", Positioning::Relative}},
                       1, InvalidationLevel::Valid)
    , sizeMode_("sizeMode", "Size Mode",
                {{"absolute", "Absolute", Positioning::Absolute},
                 {"relative", "Relative", Positioning::Relative}},
                1, InvalidationLevel::Valid)
    , viewDimensions_(-1, -1)
    , viewport_(0, 0, 1, 1)
    , isDeserializing_(false) {

    auto updateCallback = [&]() { updateViewport(); };

    positioningMode_.onChange([&]() {
        pos_.setVisible(positioningMode_.get() == Positioning::Relative);
        absolutePos_.setVisible(positioningMode_.get() == Positioning::Absolute);
        updateViewport();
    });
    pos_.onChange(updateCallback);
    absolutePos_.onChange(updateCallback);
    sizeMode_.onChange([&]() {
        size_.setVisible(sizeMode_.get() == Positioning::Relative);
        absoluteSize_.setVisible(sizeMode_.get() == Positioning::Absolute);
        updateViewport();
    });
    size_.onChange(updateCallback);
    absoluteSize_.onChange(updateCallback);
    anchorPos_.onChange(updateCallback);
    blendMode_.onChange(updateCallback);

    addProperty(positioningMode_);
    addProperty(pos_);
    addProperty(absolutePos_);

    addProperty(sizeMode_);
    addProperty(size_);
    addProperty(absoluteSize_);

    addProperty(anchorPos_);
    addProperty(blendMode_);

    updateVisibilityState();
}

void OverlayProperty::setViewDimensions(ivec2 viewDim) {
    viewDimensions_ = viewDim;
    updateViewport();
}

const ivec4& OverlayProperty::getViewport() const { return viewport_; }

auto OverlayProperty::getBlendMode() const -> BlendMode { return blendMode_; }

GLint OverlayProperty::getBlendModeGL() const { return static_cast<GLint>(blendMode_.get()); }

void OverlayProperty::deserialize(Deserializer& d) {
    isDeserializing_ = true;
    CompositeProperty::deserialize(d);
    isDeserializing_ = false;
    updateVisibilityState();
}

void OverlayProperty::updateViewport() {
    if (isDeserializing_) return;

    vec2 viewDim(viewDimensions_);

    // determine size and center position first (absolute coords)
    vec2 pos(viewDim * 0.25f);
    vec2 size(viewDim * 0.5f);

    if (positioningMode_.get() == Positioning::Absolute) {
        pos = vec2(absolutePos_.get());
    } else {
        pos = pos_.get() * viewDim;
    }
    if (sizeMode_.get() == Positioning::Absolute) {
        size = vec2(absoluteSize_.get());
    } else {
        size = size_.get() * viewDim;
    }

    // consider anchor position
    vec2 shift = 0.5f * size * (anchorPos_.get() + vec2(1.0f, 1.0f));
    pos.x -= shift.x;
    // negate y axis
    pos.y = viewDim.y - (pos.y + shift.y);

    // use pixel aligned positions for best results
    viewport_ = ivec4(pos.x, pos.y, size.x, size.y);
    // mark the composite property as modified. This will in turn trigger a
    // resize event in ImageOverlayGL::onStatusChange().
    CompositeProperty::propertyModified();
    // If an ImagePort with a non-resizeable image is connected, there
    // will be no change in the inport and the ImageOverlay processor
    // is not re-evaluated.
    // Thus we need to invalidate the property as well.
    CompositeProperty::invalidate(InvalidationLevel::InvalidOutput, this);
}

void OverlayProperty::updateVisibilityState() {
    size_.setVisible(sizeMode_.get() == Positioning::Relative);
    absoluteSize_.setVisible(sizeMode_.get() == Positioning::Absolute);

    pos_.setVisible(positioningMode_.get() == Positioning::Relative);
    absolutePos_.setVisible(positioningMode_.get() == Positioning::Absolute);
}

ImageOverlayGL::ImageOverlayGL()
    : Processor()
    , inport_("inport")
    , overlayPort_("overlay")
    , outport_("outport")
    , enabled_("enabled", "Overlay Enabled", true)
    , overlayInteraction_("overlayInteraction", "Overlay Interaction", false)
    , passThroughEvent_("passTroughEvent", "Pass Events on to Main View", false)
    , overlayProperty_("overlay", "Overlay")
    , border_("border", "Border", true)
    , borderColor_("borderColor", "Color", vec4(0.0f, 0.0f, 0.0f, 1.0f), vec4(0.0f), vec4(1.0f))
    , borderWidth_("borderWidth", "Width", 2, 0, 100)
    , shader_("img_identity.vert", "img_overlay.frag", Shader::Build::No)
    , viewManager_()
    , currentDim_(0u, 0u) {
    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });
    overlayPort_.setOptional(true);
    addPort(inport_);
    addPort(overlayPort_);

    overlayPort_.onConnect([this]() {
        viewManager_.push_back(overlayProperty_.getViewport());
        onStatusChange();
    });

    overlayPort_.onDisconnect([this]() { viewManager_.clear(); });

    addPort(outport_);

    addProperty(enabled_);
    addProperty(overlayInteraction_);
    addProperty(passThroughEvent_);
    addProperty(overlayProperty_);

    borderColor_.setSemantics(PropertySemantics::Color);

    border_.addProperty(borderColor_);
    border_.addProperty(borderWidth_);
    addProperty(border_);

    overlayProperty_.onChange([this]() { onStatusChange(); });
}

ImageOverlayGL::~ImageOverlayGL() = default;

void ImageOverlayGL::propagateEvent(Event* event, Outport* source) {
    if (event->hasVisitedProcessor(this)) return;
    event->markAsVisited(this);

    invokeEvent(event);
    if (event->hasBeenUsed()) return;

    if (event->hash() == ResizeEvent::chash()) {
        auto resizeEvent = static_cast<ResizeEvent*>(event);

        updateViewports(resizeEvent->size(), true);
        inport_.propagateEvent(resizeEvent);

        if (overlayPort_.isConnected()) {
            ResizeEvent e(uvec2(viewManager_[0].size));
            overlayPort_.propagateEvent(&e);
        }
    } else {
        if (overlayInteraction_.get() && overlayPort_.isConnected()) {
            bool overlayHandlesEvent =
                (viewManager_.propagateEvent(event, [&](Event* newEvent, size_t /*ind*/) {
                    overlayPort_.propagateEvent(newEvent);
                }));

            if ((overlayHandlesEvent && !passThroughEvent_.get()) || event->hasBeenUsed()) {
                return;
            }
        }

        if (event->shouldPropagateTo(&inport_, this, source)) {
            inport_.propagateEvent(event);
        }
    }
}

void ImageOverlayGL::onStatusChange() {
    if (overlayPort_.isConnected()) {
        // update viewport stored in view manager
        viewManager_.replace(0, overlayProperty_.getViewport());

        RenderContext::getPtr()->activateDefaultRenderContext();

        ResizeEvent e(uvec2(viewManager_[0].size));
        overlayPort_.propagateEvent(&e, overlayPort_.getConnectedOutport());
    }
}

void ImageOverlayGL::initializeResources() {
    if (overlayProperty_.getBlendMode() == OverlayProperty::BlendMode::Replace) {
        shader_.getFragmentShaderObject()->addShaderDefine("BLENDMODE_REPLACE");
    } else {
        shader_.getFragmentShaderObject()->removeShaderDefine("BLENDMODE_REPLACE");
    }

    shader_.build();
}

void ImageOverlayGL::process() {
    if (!enabled_.get()) {
        outport_.setData(inport_.getData());
        return;
    }

    utilgl::activateTargetAndCopySource(outport_, inport_, inviwo::ImageType::ColorDepthPicking);

    if (overlayPort_.isReady()) {  // draw overlay
        utilgl::DepthFuncState depthFunc(GL_ALWAYS);

        ivec4 viewport = overlayProperty_.getViewport();
        int borderWidth = 0;
        if (border_.isChecked()) {
            // adjust viewport by border width
            borderWidth = borderWidth_.get();
            viewport += ivec4(-borderWidth, -borderWidth, 2 * borderWidth, 2 * borderWidth);
        }

        utilgl::BlendModeState blendMode(overlayProperty_.getBlendModeGL(), GL_ONE_MINUS_SRC_ALPHA);

        TextureUnit colorUnit, depthUnit, pickingUnit;
        shader_.activate();
        shader_.setUniform("color_", colorUnit.getUnitNumber());
        shader_.setUniform("depth_", depthUnit.getUnitNumber());
        shader_.setUniform("picking_", pickingUnit.getUnitNumber());
        shader_.setUniform("borderWidth_", borderWidth);
        shader_.setUniform("borderColor_", borderColor_.get());
        shader_.setUniform("viewport_", vec4(viewport));

        utilgl::bindTextures(overlayPort_, colorUnit, depthUnit, pickingUnit);

        utilgl::ViewportState viewportState(viewport);
        utilgl::singleDrawImagePlaneRect();
        shader_.deactivate();
    }
    utilgl::deactivateCurrentTarget();
}

void ImageOverlayGL::updateViewports(ivec2 dim, bool force) {
    if (!force && (currentDim_ == dim)) return;  // no changes

    overlayProperty_.setViewDimensions(dim);
    currentDim_ = dim;
}

}  // namespace inviwo
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

#define DEBUG
#define DEBUG2

#include <net/tcp/tcp.hpp>
#include <net/tcp/packet.hpp>
#include <statman>

using namespace std;
using namespace net;
using namespace net::tcp;

TCP::TCP(IPStack& inet) :
  bytes_rx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.bytes_rx").get_uint64()},
  bytes_tx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.bytes_tx").get_uint64()},
  packets_rx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.packets_rx").get_uint64()},
  packets_tx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.packets_tx").get_uint64()},
  incoming_connections_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.incoming_connections").get_uint64()},
  outgoing_connections_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.outgoing_connections").get_uint64()},
  connection_attempts_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.connection_attempts").get_uint64()},
  packets_dropped_{Statman::get().create(Stat::UINT32, inet.ifname() + ".tcp.packets_dropped").get_uint32()},
  inet_(inet),
  listeners_(),
  connections_(),
  writeq(),
  MAX_SEG_LIFETIME(30s)
{
  inet.on_transmit_queue_available({this, &TCP::process_writeq});
  // TODO: RFC 6056
  current_ephemeral_ = 1024 + rand() % (UINT_MAX-1024);
}

/*
  Note: There is different approaches to how to handle listeners & connections.
  Need to discuss and decide for the best one.

  Best solution(?):
  Preallocate a pool with listening connections.
  When threshold is reach, remove/add new ones, similar to TCP window.

  Current solution:
  Simple.
*/
Listener& TCP::bind(const port_t port) {
  // Already a listening socket.
  Listeners::const_iterator it = listeners_.find(port);
  if(it != listeners_.cend()) {
    throw TCPException{"Port is already taken."};
  }
  auto& listener = listeners_.emplace(port,
    std::make_unique<tcp::Listener>(*this, port)
    ).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);
  return *listener;

  /*auto& listener = (listeners_.emplace(std::piecewise_construct,
    std::forward_as_tuple(port),
    std::forward_as_tuple(*this, port))
    ).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);

  return listener;*/
}

bool TCP::unbind(const port_t port) {
  auto it = listeners_.find(port);
  if(LIKELY(it != listeners_.end())) {
    auto listener = std::move(it->second);
    listener->close();
    Ensures(listeners_.find(port) == listeners_.end());
    return true;
  }
  return false;
}

/*
  Active open a new connection to the given remote.

  @WARNING: Callback is added when returned (TCP::connect(...).onSuccess(...)),
  and open() is called before callback is added.
*/
Connection_ptr TCP::connect(Socket remote) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->open(true);
  return connection;
}

/*
  Active open a new connection to the given remote.
*/
void TCP::connect(Socket remote, Connection::ConnectCallback callback) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->on_connect(callback).open(true);
}

void TCP::insert_connection(Connection_ptr conn)
{
  connections_.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(conn->local_port(), conn->remote()),
      std::forward_as_tuple(conn));
}

seq_t TCP::generate_iss() {
  // Do something to get a iss.
  return rand();
}

/*
  TODO: Check if there is any ports free.
*/
port_t TCP::next_free_port() {

  current_ephemeral_ = (current_ephemeral_ == 0) ? current_ephemeral_ + 1025 : current_ephemeral_ + 1;

  // Avoid giving a port that is bound to a service.
  while(listeners_.find(current_ephemeral_) != listeners_.end())
    current_ephemeral_++;

  return current_ephemeral_;
}

/*
  Expensive look up if port is in use.
*/
bool TCP::port_in_use(const port_t port) const {
  if(listeners_.find(port) != listeners_.end())
    return true;

  for(auto conn : connections_) {
    if(conn.first.first == port)
      return true;
  }
  return false;
}

uint16_t TCP::checksum(const tcp::Packet& packet)
{
  short tcp_length = packet.tcp_length();

  Pseudo_header pseudo_hdr;
  pseudo_hdr.saddr.whole = packet.src().whole;
  pseudo_hdr.daddr.whole = packet.dst().whole;
  pseudo_hdr.zero = 0;
  pseudo_hdr.proto = IP4::IP4_TCP;
  pseudo_hdr.tcp_length = htons(tcp_length);

  union Sum{
    uint32_t whole;
    uint16_t part[2];
  } sum;

  sum.whole = 0;

  // Compute sum of pseudo header
  for (uint16_t* it = (uint16_t*)&pseudo_hdr; it < (uint16_t*)&pseudo_hdr + sizeof(pseudo_hdr)/2; it++)
    sum.whole += *it;

  // Compute sum of header and data
  Header* tcp_hdr = &packet.tcp_header();
  for (uint16_t* it = (uint16_t*)tcp_hdr; it < (uint16_t*)tcp_hdr + tcp_length/2; it++)
    sum.whole+= *it;

  // The odd-numbered case
  bool odd = (tcp_length & 1);
  sum.whole += (odd) ? ((uint8_t*)tcp_hdr)[tcp_length - 1] << 16 : 0;

  sum.whole = (uint32_t)sum.part[0] + sum.part[1];
  sum.part[0] += sum.part[1];

  return ~sum.whole;
}

void TCP::bottom(net::Packet_ptr packet_ptr) {
  // Stat increment packets received
  packets_rx_++;

  // Translate into a TCP::Packet. This will be used inside the TCP-scope.
  auto packet = static_unique_ptr_cast<net::tcp::Packet>(std::move(packet_ptr));
  debug2("<TCP::bottom> TCP Packet received - Source: %s, Destination: %s \n",
        packet->source().to_string().c_str(), packet->destination().to_string().c_str());

  // Stat increment bytes received
  bytes_rx_ += packet->tcp_data_length();

  // Validate checksum
  if (UNLIKELY(checksum(*packet) != 0)) {
    debug("<TCP::bottom> TCP Packet Checksum != 0 \n");
    drop(*packet);
    return;
  }

  Connection::Tuple tuple { packet->dst_port(), packet->source() };

  // Try to find the receiver
  auto conn_it = connections_.find(tuple);

  // Connection found
  if (conn_it != connections_.end()) {
    debug("<TCP::bottom> Connection found: %s \n", conn_it->second->to_string().c_str());
    conn_it->second->segment_arrived(std::move(packet));
    return;
  }

  // No open connection found, find listener on port
  Listeners::iterator listener_it = listeners_.find(packet->dst_port());
  debug("<TCP::bottom> No connection found - looking for listener..\n");
  // Listener found => Create listening Connection
  if (LIKELY(listener_it != listeners_.end())) {
    auto& listener = listener_it->second;
    debug("<TCP::bottom> Listener found: %s\n", listener->to_string().c_str());
    listener->segment_arrived(std::move(packet));
    debug2("<TCP::bottom> Listener done with packet\n");
    return;
  }

  drop(*packet);
}

void TCP::process_writeq(size_t packets) {
  debug2("<TCP::process_writeq> size=%u p=%u\n", writeq.size(), packets);
  // foreach connection who wants to write
  while(packets and !writeq.empty()) {
    debug("<TCP::process_writeq> Processing writeq size=%u, p=%u\n", writeq.size(), packets);
    auto conn = writeq.front();
    // remove from writeq
    writeq.pop_back();
    conn->set_queued(false);
    // ...
    conn->offer(packets);
  }
}

size_t TCP::send(Connection_ptr conn, const char* buffer, size_t n) {
  size_t written{0};
  auto packets = inet_.transmit_queue_available();

  debug2("<TCP::send> Send request for %u bytes\n", n);

  if(packets > 0) {
    written += conn->send(buffer, n, packets);
  }

  // requeue remaining if not already queued
  if(conn->sendq_remaining()) {
    if (conn->is_queued() == false) {
      debug("<TCP::send> %s queued\n", conn->to_string().c_str());
      writeq.push_back(conn);
      conn->set_queued(true);
    }
  }
  return written;
}

/*
  Show all connections for TCP as a string.

  Format:
  [Protocol][Recv][Send][Local][Remote][State]

  TODO: Make sure Recv, Send, In, Out is correct and add them to output. Also, alignment?
*/
string TCP::to_string() const {
  // Write all connections in a cute list.
  stringstream ss;
  ss << "LISTENERS:\n" << "Port\t" << "Queued\n";
  for(auto& listen_it : listeners_) {
    auto& l = listen_it.second;
    ss << l->port() << "\t" << l->syn_queue_size() << "\n";
  }
  ss << "\nCONNECTIONS:\n" <<  "Proto\tRecv\tSend\tIn\tOut\tLocal\t\t\tRemote\t\t\tState\n";
  for(auto& con_it : connections_) {
    auto& c = *(con_it.second);
    ss << "tcp4\t"
       << " " << "\t" << " " << "\t"
       << " " << "\t" << " " << "\t"
       << c.local().to_string() << "\t\t" << c.remote().to_string() << "\t\t"
       << c.state().to_string() << "\n";
  }
  return ss.str();
}

Connection_ptr TCP::add_connection(port_t local_port, Socket remote) {
  // Stat increment number of outgoing connections
  outgoing_connections_++;

  auto& conn = (connections_.emplace(
      Connection::Tuple{ local_port, remote },
      std::make_shared<Connection>(*this, local_port, remote))
    ).first->second;
  conn->_on_cleanup({this, &TCP::close_connection});
  return conn;
}

void TCP::add_connection(tcp::Connection_ptr conn) {
  // Stat increment number of incoming connections
  incoming_connections_++;

  debug("<TCP::add_connection> Connection added %s \n", conn->to_string().c_str());
  conn->_on_cleanup({this, &TCP::close_connection});
  connections_.emplace(conn->tuple(), conn);
}

void TCP::close_connection(tcp::Connection_ptr conn) {
  debug("<TCP::close_connection> Closing connection: %s \n", conn->to_string().c_str());
  connections_.erase(conn->tuple());
}

void TCP::close_listener(Listener& listener) {
  listeners_.erase(listener.port());
}

void TCP::drop(const tcp::Packet&) {
  // Stat increment packets dropped
  packets_dropped_++;

  debug("<TCP::drop> Packet dropped\n");
  //debug("<TCP::drop> Packet was dropped - no recipient: %s \n", packet->destination().to_string().c_str());
}

void TCP::transmit(tcp::Packet_ptr packet) {
  // Generate checksum.
  packet->set_checksum(TCP::checksum(*packet));
  debug2("<TCP::transmit> %s\n", packet->to_string().c_str());

  // Stat increment bytes transmitted and packets transmitted
  bytes_tx_ += packet->tcp_data_length();
  packets_tx_++;

  _network_layer_out(std::move(packet));
}
<commit_msg>tcp: Fixes to writeq<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

#define DEBUG
#define DEBUG2

#include <net/tcp/tcp.hpp>
#include <net/tcp/packet.hpp>
#include <statman>

using namespace std;
using namespace net;
using namespace net::tcp;

TCP::TCP(IPStack& inet) :
  bytes_rx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.bytes_rx").get_uint64()},
  bytes_tx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.bytes_tx").get_uint64()},
  packets_rx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.packets_rx").get_uint64()},
  packets_tx_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.packets_tx").get_uint64()},
  incoming_connections_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.incoming_connections").get_uint64()},
  outgoing_connections_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.outgoing_connections").get_uint64()},
  connection_attempts_{Statman::get().create(Stat::UINT64, inet.ifname() + ".tcp.connection_attempts").get_uint64()},
  packets_dropped_{Statman::get().create(Stat::UINT32, inet.ifname() + ".tcp.packets_dropped").get_uint32()},
  inet_(inet),
  listeners_(),
  connections_(),
  writeq(),
  MAX_SEG_LIFETIME(30s)
{
  inet.on_transmit_queue_available({this, &TCP::process_writeq});
  // TODO: RFC 6056
  current_ephemeral_ = 1024 + rand() % (UINT_MAX-1024);
}

/*
  Note: There is different approaches to how to handle listeners & connections.
  Need to discuss and decide for the best one.

  Best solution(?):
  Preallocate a pool with listening connections.
  When threshold is reach, remove/add new ones, similar to TCP window.

  Current solution:
  Simple.
*/
Listener& TCP::bind(const port_t port) {
  // Already a listening socket.
  Listeners::const_iterator it = listeners_.find(port);
  if(it != listeners_.cend()) {
    throw TCPException{"Port is already taken."};
  }
  auto& listener = listeners_.emplace(port,
    std::make_unique<tcp::Listener>(*this, port)
    ).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);
  return *listener;

  /*auto& listener = (listeners_.emplace(std::piecewise_construct,
    std::forward_as_tuple(port),
    std::forward_as_tuple(*this, port))
    ).first->second;
  debug("<TCP::bind> Bound to port %i \n", port);

  return listener;*/
}

bool TCP::unbind(const port_t port) {
  auto it = listeners_.find(port);
  if(LIKELY(it != listeners_.end())) {
    auto listener = std::move(it->second);
    listener->close();
    Ensures(listeners_.find(port) == listeners_.end());
    return true;
  }
  return false;
}

/*
  Active open a new connection to the given remote.

  @WARNING: Callback is added when returned (TCP::connect(...).onSuccess(...)),
  and open() is called before callback is added.
*/
Connection_ptr TCP::connect(Socket remote) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->open(true);
  return connection;
}

/*
  Active open a new connection to the given remote.
*/
void TCP::connect(Socket remote, Connection::ConnectCallback callback) {
  auto port = next_free_port();
  auto connection = add_connection(port, remote);
  connection->on_connect(callback).open(true);
}

void TCP::insert_connection(Connection_ptr conn)
{
  connections_.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(conn->local_port(), conn->remote()),
      std::forward_as_tuple(conn));
}

seq_t TCP::generate_iss() {
  // Do something to get a iss.
  return rand();
}

/*
  TODO: Check if there is any ports free.
*/
port_t TCP::next_free_port() {

  current_ephemeral_ = (current_ephemeral_ == 0) ? current_ephemeral_ + 1025 : current_ephemeral_ + 1;

  // Avoid giving a port that is bound to a service.
  while(listeners_.find(current_ephemeral_) != listeners_.end())
    current_ephemeral_++;

  return current_ephemeral_;
}

/*
  Expensive look up if port is in use.
*/
bool TCP::port_in_use(const port_t port) const {
  if(listeners_.find(port) != listeners_.end())
    return true;

  for(auto conn : connections_) {
    if(conn.first.first == port)
      return true;
  }
  return false;
}

uint16_t TCP::checksum(const tcp::Packet& packet)
{
  short tcp_length = packet.tcp_length();

  Pseudo_header pseudo_hdr;
  pseudo_hdr.saddr.whole = packet.src().whole;
  pseudo_hdr.daddr.whole = packet.dst().whole;
  pseudo_hdr.zero = 0;
  pseudo_hdr.proto = IP4::IP4_TCP;
  pseudo_hdr.tcp_length = htons(tcp_length);

  union Sum{
    uint32_t whole;
    uint16_t part[2];
  } sum;

  sum.whole = 0;

  // Compute sum of pseudo header
  for (uint16_t* it = (uint16_t*)&pseudo_hdr; it < (uint16_t*)&pseudo_hdr + sizeof(pseudo_hdr)/2; it++)
    sum.whole += *it;

  // Compute sum of header and data
  Header* tcp_hdr = &packet.tcp_header();
  for (uint16_t* it = (uint16_t*)tcp_hdr; it < (uint16_t*)tcp_hdr + tcp_length/2; it++)
    sum.whole+= *it;

  // The odd-numbered case
  bool odd = (tcp_length & 1);
  sum.whole += (odd) ? ((uint8_t*)tcp_hdr)[tcp_length - 1] << 16 : 0;

  sum.whole = (uint32_t)sum.part[0] + sum.part[1];
  sum.part[0] += sum.part[1];

  return ~sum.whole;
}

void TCP::bottom(net::Packet_ptr packet_ptr) {
  // Stat increment packets received
  packets_rx_++;

  // Translate into a TCP::Packet. This will be used inside the TCP-scope.
  auto packet = static_unique_ptr_cast<net::tcp::Packet>(std::move(packet_ptr));
  debug2("<TCP::bottom> TCP Packet received - Source: %s, Destination: %s \n",
        packet->source().to_string().c_str(), packet->destination().to_string().c_str());

  // Stat increment bytes received
  bytes_rx_ += packet->tcp_data_length();

  // Validate checksum
  if (UNLIKELY(checksum(*packet) != 0)) {
    debug("<TCP::bottom> TCP Packet Checksum != 0 \n");
    drop(*packet);
    return;
  }

  Connection::Tuple tuple { packet->dst_port(), packet->source() };

  // Try to find the receiver
  auto conn_it = connections_.find(tuple);

  // Connection found
  if (conn_it != connections_.end()) {
    debug("<TCP::bottom> Connection found: %s \n", conn_it->second->to_string().c_str());
    conn_it->second->segment_arrived(std::move(packet));
    return;
  }

  // No open connection found, find listener on port
  Listeners::iterator listener_it = listeners_.find(packet->dst_port());
  debug("<TCP::bottom> No connection found - looking for listener..\n");
  // Listener found => Create listening Connection
  if (LIKELY(listener_it != listeners_.end())) {
    auto& listener = listener_it->second;
    debug("<TCP::bottom> Listener found: %s\n", listener->to_string().c_str());
    listener->segment_arrived(std::move(packet));
    debug2("<TCP::bottom> Listener done with packet\n");
    return;
  }

  drop(*packet);
}

void TCP::process_writeq(size_t packets) {
  debug2("<TCP::process_writeq> size=%u p=%u\n", writeq.size(), packets);
  // foreach connection who wants to write
  while(packets and !writeq.empty()) {
    debug("<TCP::process_writeq> Processing writeq size=%u, p=%u\n", writeq.size(), packets);
    auto conn = writeq.front();
    // remove from writeq
    writeq.pop_front();
    conn->set_queued(false);
    // ...
    conn->offer(packets);
  }
}

size_t TCP::send(Connection_ptr conn, const char* buffer, size_t n) {
  size_t written{0};
  auto packets = inet_.transmit_queue_available();

  debug2("<TCP::send> Send request for %u bytes\n", n);

  if(packets > 0) {
    written += conn->send(buffer, n, packets);
  }

  // requeue remaining if not already queued
  if(written == 0 || conn->can_send()) {
    if (conn->is_queued() == false) {
      debug("<TCP::send> %s queued\n", conn->to_string().c_str());
      writeq.push_back(conn);
      conn->set_queued(true);
    }
  }
  return written;
}

/*
  Show all connections for TCP as a string.

  Format:
  [Protocol][Recv][Send][Local][Remote][State]

  TODO: Make sure Recv, Send, In, Out is correct and add them to output. Also, alignment?
*/
string TCP::to_string() const {
  // Write all connections in a cute list.
  stringstream ss;
  ss << "LISTENERS:\n" << "Port\t" << "Queued\n";
  for(auto& listen_it : listeners_) {
    auto& l = listen_it.second;
    ss << l->port() << "\t" << l->syn_queue_size() << "\n";
  }
  ss << "\nCONNECTIONS:\n" <<  "Proto\tRecv\tSend\tIn\tOut\tLocal\t\t\tRemote\t\t\tState\n";
  for(auto& con_it : connections_) {
    auto& c = *(con_it.second);
    ss << "tcp4\t"
       << " " << "\t" << " " << "\t"
       << " " << "\t" << " " << "\t"
       << c.local().to_string() << "\t\t" << c.remote().to_string() << "\t\t"
       << c.state().to_string() << "\n";
  }
  return ss.str();
}

Connection_ptr TCP::add_connection(port_t local_port, Socket remote) {
  // Stat increment number of outgoing connections
  outgoing_connections_++;

  auto& conn = (connections_.emplace(
      Connection::Tuple{ local_port, remote },
      std::make_shared<Connection>(*this, local_port, remote))
    ).first->second;
  conn->_on_cleanup({this, &TCP::close_connection});
  return conn;
}

void TCP::add_connection(tcp::Connection_ptr conn) {
  // Stat increment number of incoming connections
  incoming_connections_++;

  debug("<TCP::add_connection> Connection added %s \n", conn->to_string().c_str());
  conn->_on_cleanup({this, &TCP::close_connection});
  connections_.emplace(conn->tuple(), conn);
}

void TCP::close_connection(tcp::Connection_ptr conn) {
  debug("<TCP::close_connection> Closing connection: %s \n", conn->to_string().c_str());
  connections_.erase(conn->tuple());
}

void TCP::close_listener(Listener& listener) {
  listeners_.erase(listener.port());
}

void TCP::drop(const tcp::Packet&) {
  // Stat increment packets dropped
  packets_dropped_++;

  debug("<TCP::drop> Packet dropped\n");
  //debug("<TCP::drop> Packet was dropped - no recipient: %s \n", packet->destination().to_string().c_str());
}

void TCP::transmit(tcp::Packet_ptr packet) {
  // Generate checksum.
  packet->set_checksum(TCP::checksum(*packet));
  debug2("<TCP::transmit> %s\n", packet->to_string().c_str());

  // Stat increment bytes transmitted and packets transmitted
  bytes_tx_ += packet->tcp_data_length();
  packets_tx_++;

  _network_layer_out(std::move(packet));
}
<|endoftext|>


<commit_before>// This file is all the code that used to be in one file.
// TODO: split into modules, delete this file.

#include "ninja.h"

#include "build_log.h"

#include <errno.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

int ReadFile(const string& path, string* contents, string* err) {
  FILE* f = fopen(path.c_str(), "r");
  if (!f) {
    err->assign(strerror(errno));
    return -errno;
  }

  char buf[64 << 10];
  size_t len;
  while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
    contents->append(buf, len);
  }
  if (ferror(f)) {
    err->assign(strerror(errno));  // XXX errno?
    contents->clear();
    fclose(f);
    return -errno;
  }
  fclose(f);
  return 0;
}

int RealDiskInterface::Stat(const string& path) {
  struct stat st;
  if (stat(path.c_str(), &st) < 0) {
    if (errno == ENOENT) {
      return 0;
    } else {
      fprintf(stderr, "stat(%s): %s\n", path.c_str(), strerror(errno));
      return -1;
    }
  }

  return st.st_mtime;
  return true;
}

string DirName(const string& path) {
  string::size_type slash_pos = path.rfind('/');
  if (slash_pos == string::npos)
    return "";  // Nothing to do.
  while (slash_pos > 0 && path[slash_pos - 1] == '/')
    --slash_pos;
  return path.substr(0, slash_pos);
}

bool DiskInterface::MakeDirs(const string& path) {
  string dir = DirName(path);
  if (dir.empty())
    return true;  // Reached root; assume it's there.
  int mtime = Stat(dir);
  if (mtime < 0)
    return false;  // Error.
  if (mtime > 0)
    return true;  // Exists already; we're done.

  // Directory doesn't exist.  Try creating its parent first.
  bool success = MakeDirs(dir);
  if (!success)
    return false;
  return MakeDir(dir);
}

string RealDiskInterface::ReadFile(const string& path, string* err) {
  string contents;
  int ret = ::ReadFile(path, &contents, err);
  if (ret == -ENOENT) {
    // Swallow ENOENT.
    err->clear();
  }
  return contents;
}

bool RealDiskInterface::MakeDir(const string& path) {
  if (mkdir(path.c_str(), 0777) < 0) {
    fprintf(stderr, "mkdir(%s): %s\n", path.c_str(), strerror(errno));
    return false;
  }
  return true;
}
void FileStat::Touch(int mtime) {
  mtime_ = mtime;
  if (node_)
    node_->MarkDirty();
}

bool FileStat::Stat(DiskInterface* disk_interface) {
  mtime_ = disk_interface->Stat(path_);
  return mtime_ > 0;
}

void Node::MarkDirty() {
  if (dirty_)
    return;  // We already know.

  dirty_ = true;
  MarkDependentsDirty();
}

void Node::MarkDependentsDirty() {
  for (vector<Edge*>::iterator i = out_edges_.begin(); i != out_edges_.end(); ++i)
    (*i)->MarkDirty(this);
}

bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
                          string* err) {
  bool dirty = false;

  if (!rule_->depfile_.empty()) {
    if (!LoadDepFile(state, disk_interface, err))
      return false;
  }

  time_t most_recent_input = 1;
  for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
    if ((*i)->file_->StatIfNecessary(disk_interface)) {
      if (Edge* edge = (*i)->in_edge_) {
        if (!edge->RecomputeDirty(state, disk_interface, err))
          return false;
      } else {
        (*i)->dirty_ = !(*i)->file_->exists();
      }
    }

    if (is_order_only(i - inputs_.begin())) {
      // Order-only deps only make us dirty if they're missing.
      if (!(*i)->file_->exists())
        dirty = true;
      continue;
    }

    // If a regular input is dirty (or missing), we're dirty.
    // Otherwise consider mtime.
    if ((*i)->dirty_) {
      dirty = true;
    } else {
      if ((*i)->file_->mtime_ > most_recent_input)
        most_recent_input = (*i)->file_->mtime_;
    }
  }

  assert(!outputs_.empty());
  for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
    // We may have other outputs, that our input-recursive traversal hasn't hit
    // yet (or never will).  Stat them if we haven't already.
    (*i)->file_->StatIfNecessary(disk_interface);

    // Output is dirty if we're dirty, we're missing the output,
    // or if it's older than the mostt recent input mtime.
    if (dirty || !(*i)->file_->exists() ||
        (*i)->file_->mtime_ < most_recent_input) {
      (*i)->dirty_ = true;
    }
  }
  return true;
}

void Edge::MarkDirty(Node* node) {
  if (rule_ == &State::kPhonyRule)
    return;

  vector<Node*>::iterator i = find(inputs_.begin(), inputs_.end(), node);
  if (i == inputs_.end())
    return;
  if (i - inputs_.begin() >= ((int)inputs_.size()) - order_only_deps_)
    return;  // Order-only deps don't cause us to become dirty.
  for (i = outputs_.begin(); i != outputs_.end(); ++i)
    (*i)->MarkDirty();
}

struct EdgeEnv : public Env {
  EdgeEnv(Edge* edge) : edge_(edge) {}
  virtual string LookupVariable(const string& var) {
    string result;
    if (var == "in") {
      int explicit_deps = edge_->inputs_.size() - edge_->implicit_deps_ -
          edge_->order_only_deps_;
      for (vector<Node*>::iterator i = edge_->inputs_.begin();
           i != edge_->inputs_.end() && explicit_deps; ++i, --explicit_deps) {
        if (!result.empty())
          result.push_back(' ');
        result.append((*i)->file_->path_);
      }
    } else if (var == "out") {
      result = edge_->outputs_[0]->file_->path_;
    } else if (edge_->env_) {
      return edge_->env_->LookupVariable(var);
    }
    return result;
  }
  Edge* edge_;
};

string Edge::EvaluateCommand() {
  EdgeEnv env(this);
  return rule_->command_.Evaluate(&env);
}

string Edge::GetDescription() {
  EdgeEnv env(this);
  return rule_->description_.Evaluate(&env);
}

FileStat* StatCache::GetFile(const string& path) {
  Paths::iterator i = paths_.find(path);
  if (i != paths_.end())
    return i->second;
  FileStat* file = new FileStat(path);
  paths_[path] = file;
  return file;
}

#include <stdio.h>

void StatCache::Dump() {
  for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
    FileStat* file = i->second;
    printf("%s %s\n",
           file->path_.c_str(),
           file->status_known()
           ? (file->node_->dirty_ ? "dirty" : "clean")
           : "unknown");
  }
}

#include "parsers.h"

bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err) {
  EdgeEnv env(this);
  string path = rule_->depfile_.Evaluate(&env);

  string content = disk_interface->ReadFile(path, err);
  if (!err->empty())
    return false;
  if (content.empty())
    return true;

  MakefileParser makefile;
  if (!makefile.Parse(content, err))
    return false;

  // Check that this depfile matches our output.
  if (outputs_.size() != 1) {
    *err = "expected only one output";
    return false;
  }
  if (outputs_[0]->file_->path_ != makefile.out_) {
    *err = "expected makefile to mention '" + outputs_[0]->file_->path_ + "', "
           "got '" + makefile.out_ + "'";
    return false;
  }

  // Add all its in-edges.
  for (vector<string>::iterator i = makefile.ins_.begin();
       i != makefile.ins_.end(); ++i) {
    Node* node = state->GetNode(*i);
    for (vector<Node*>::iterator j = inputs_.begin(); j != inputs_.end(); ++j) {
      if (*j == node) {
        node = NULL;
        break;
      }
    }
    if (node) {
      inputs_.insert(inputs_.end() - order_only_deps_, node);
      node->out_edges_.push_back(this);
      ++implicit_deps_;
    }
  }

  return true;
}

void Edge::Dump() {
  printf("[ ");
  for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
    printf("%s ", (*i)->file_->path_.c_str());
  }
  printf("--%s-> ", rule_->name_.c_str());
  for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
    printf("%s ", (*i)->file_->path_.c_str());
  }
  printf("]\n");
}

const Rule State::kPhonyRule("phony");

State::State() : build_log_(NULL) {
  AddRule(&kPhonyRule);
  build_log_ = new BuildLog;
}

const Rule* State::LookupRule(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i == rules_.end())
    return NULL;
  return i->second;
}

void State::AddRule(const Rule* rule) {
  assert(LookupRule(rule->name_) == NULL);
  rules_[rule->name_] = rule;
}

Edge* State::AddEdge(const Rule* rule) {
  Edge* edge = new Edge();
  edge->rule_ = rule;
  edge->env_ = &bindings_;
  edges_.push_back(edge);
  return edge;
}

Node* State::LookupNode(const string& path) {
  FileStat* file = stat_cache_.GetFile(path);
  if (!file->node_)
    return NULL;
  return file->node_;
}

Node* State::GetNode(const string& path) {
  FileStat* file = stat_cache_.GetFile(path);
  if (!file->node_)
    file->node_ = new Node(file);
  return file->node_;
}

void State::AddInOut(Edge* edge, Edge::InOut inout, const string& path) {
  Node* node = GetNode(path);
  if (inout == Edge::IN) {
    edge->inputs_.push_back(node);
    node->out_edges_.push_back(edge);
  } else {
    edge->outputs_.push_back(node);
    if (node->in_edge_) {
      fprintf(stderr, "WARNING: multiple rules generate %s. "
              "build will not be correct; continuing anyway\n", path.c_str());
    }
    node->in_edge_ = edge;
  }
}

bool EvalString::Parse(const string& input, string* err) {
  unparsed_ = input;

  string::size_type start, end;
  start = 0;
  do {
    end = input.find('$', start);
    if (end == string::npos) {
      end = input.size();
      break;
    }
    if (end > start)
      parsed_.push_back(make_pair(input.substr(start, end - start), RAW));
    start = end + 1;
    if (start < input.size() && input[start] == '{') {
      ++start;
      for (end = start + 1; end < input.size(); ++end) {
        if (input[end] == '}')
          break;
      }
      if (end >= input.size()) {
        *err = "expected closing curly after ${";
        return false;
      }
      parsed_.push_back(make_pair(input.substr(start, end - start), SPECIAL));
      ++end;
    } else {
      for (end = start; end < input.size(); ++end) {
        char c = input[end];
        if (!(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'))
          break;
      }
      if (end == start) {
        *err = "expected variable after $";
        return false;
      }
      parsed_.push_back(make_pair(input.substr(start, end - start), SPECIAL));
    }
    start = end;
  } while (end < input.size());
  if (end > start)
    parsed_.push_back(make_pair(input.substr(start, end - start), RAW));

  return true;
}

string EvalString::Evaluate(Env* env) const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
    if (i->second == RAW)
      result.append(i->first);
    else
      result.append(env->LookupVariable(i->first));
  }
  return result;
}
<commit_msg>rebuild when command lines change<commit_after>// This file is all the code that used to be in one file.
// TODO: split into modules, delete this file.

#include "ninja.h"

#include "build_log.h"

#include <errno.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

int ReadFile(const string& path, string* contents, string* err) {
  FILE* f = fopen(path.c_str(), "r");
  if (!f) {
    err->assign(strerror(errno));
    return -errno;
  }

  char buf[64 << 10];
  size_t len;
  while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
    contents->append(buf, len);
  }
  if (ferror(f)) {
    err->assign(strerror(errno));  // XXX errno?
    contents->clear();
    fclose(f);
    return -errno;
  }
  fclose(f);
  return 0;
}

int RealDiskInterface::Stat(const string& path) {
  struct stat st;
  if (stat(path.c_str(), &st) < 0) {
    if (errno == ENOENT) {
      return 0;
    } else {
      fprintf(stderr, "stat(%s): %s\n", path.c_str(), strerror(errno));
      return -1;
    }
  }

  return st.st_mtime;
  return true;
}

string DirName(const string& path) {
  string::size_type slash_pos = path.rfind('/');
  if (slash_pos == string::npos)
    return "";  // Nothing to do.
  while (slash_pos > 0 && path[slash_pos - 1] == '/')
    --slash_pos;
  return path.substr(0, slash_pos);
}

bool DiskInterface::MakeDirs(const string& path) {
  string dir = DirName(path);
  if (dir.empty())
    return true;  // Reached root; assume it's there.
  int mtime = Stat(dir);
  if (mtime < 0)
    return false;  // Error.
  if (mtime > 0)
    return true;  // Exists already; we're done.

  // Directory doesn't exist.  Try creating its parent first.
  bool success = MakeDirs(dir);
  if (!success)
    return false;
  return MakeDir(dir);
}

string RealDiskInterface::ReadFile(const string& path, string* err) {
  string contents;
  int ret = ::ReadFile(path, &contents, err);
  if (ret == -ENOENT) {
    // Swallow ENOENT.
    err->clear();
  }
  return contents;
}

bool RealDiskInterface::MakeDir(const string& path) {
  if (mkdir(path.c_str(), 0777) < 0) {
    fprintf(stderr, "mkdir(%s): %s\n", path.c_str(), strerror(errno));
    return false;
  }
  return true;
}
void FileStat::Touch(int mtime) {
  mtime_ = mtime;
  if (node_)
    node_->MarkDirty();
}

bool FileStat::Stat(DiskInterface* disk_interface) {
  mtime_ = disk_interface->Stat(path_);
  return mtime_ > 0;
}

void Node::MarkDirty() {
  if (dirty_)
    return;  // We already know.

  dirty_ = true;
  MarkDependentsDirty();
}

void Node::MarkDependentsDirty() {
  for (vector<Edge*>::iterator i = out_edges_.begin(); i != out_edges_.end(); ++i)
    (*i)->MarkDirty(this);
}

bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
                          string* err) {
  bool dirty = false;

  if (!rule_->depfile_.empty()) {
    if (!LoadDepFile(state, disk_interface, err))
      return false;
  }

  time_t most_recent_input = 1;
  for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
    if ((*i)->file_->StatIfNecessary(disk_interface)) {
      if (Edge* edge = (*i)->in_edge_) {
        if (!edge->RecomputeDirty(state, disk_interface, err))
          return false;
      } else {
        (*i)->dirty_ = !(*i)->file_->exists();
      }
    }

    if (is_order_only(i - inputs_.begin())) {
      // Order-only deps only make us dirty if they're missing.
      if (!(*i)->file_->exists())
        dirty = true;
      continue;
    }

    // If a regular input is dirty (or missing), we're dirty.
    // Otherwise consider mtime.
    if ((*i)->dirty_) {
      dirty = true;
    } else {
      if ((*i)->file_->mtime_ > most_recent_input)
        most_recent_input = (*i)->file_->mtime_;
    }
  }

  string command = EvaluateCommand();

  assert(!outputs_.empty());
  for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
    // We may have other outputs, that our input-recursive traversal hasn't hit
    // yet (or never will).  Stat them if we haven't already.
    (*i)->file_->StatIfNecessary(disk_interface);

    // Output is dirty if we're dirty, we're missing the output,
    // or if it's older than the mostt recent input mtime.
    if (dirty || !(*i)->file_->exists() ||
        (*i)->file_->mtime_ < most_recent_input) {
      (*i)->dirty_ = true;
    } else {
      // May also be dirty due to the command changing since the last build.
      BuildLog::LogEntry* entry;
      if (state->build_log_ &&
          (entry = state->build_log_->LookupByOutput((*i)->file_->path_))) {
        if (command != entry->command)
          (*i)->dirty_ = true;
      }
    }
  }
  return true;
}

void Edge::MarkDirty(Node* node) {
  if (rule_ == &State::kPhonyRule)
    return;

  vector<Node*>::iterator i = find(inputs_.begin(), inputs_.end(), node);
  if (i == inputs_.end())
    return;
  if (i - inputs_.begin() >= ((int)inputs_.size()) - order_only_deps_)
    return;  // Order-only deps don't cause us to become dirty.
  for (i = outputs_.begin(); i != outputs_.end(); ++i)
    (*i)->MarkDirty();
}

struct EdgeEnv : public Env {
  EdgeEnv(Edge* edge) : edge_(edge) {}
  virtual string LookupVariable(const string& var) {
    string result;
    if (var == "in") {
      int explicit_deps = edge_->inputs_.size() - edge_->implicit_deps_ -
          edge_->order_only_deps_;
      for (vector<Node*>::iterator i = edge_->inputs_.begin();
           i != edge_->inputs_.end() && explicit_deps; ++i, --explicit_deps) {
        if (!result.empty())
          result.push_back(' ');
        result.append((*i)->file_->path_);
      }
    } else if (var == "out") {
      result = edge_->outputs_[0]->file_->path_;
    } else if (edge_->env_) {
      return edge_->env_->LookupVariable(var);
    }
    return result;
  }
  Edge* edge_;
};

string Edge::EvaluateCommand() {
  EdgeEnv env(this);
  return rule_->command_.Evaluate(&env);
}

string Edge::GetDescription() {
  EdgeEnv env(this);
  return rule_->description_.Evaluate(&env);
}

FileStat* StatCache::GetFile(const string& path) {
  Paths::iterator i = paths_.find(path);
  if (i != paths_.end())
    return i->second;
  FileStat* file = new FileStat(path);
  paths_[path] = file;
  return file;
}

#include <stdio.h>

void StatCache::Dump() {
  for (Paths::iterator i = paths_.begin(); i != paths_.end(); ++i) {
    FileStat* file = i->second;
    printf("%s %s\n",
           file->path_.c_str(),
           file->status_known()
           ? (file->node_->dirty_ ? "dirty" : "clean")
           : "unknown");
  }
}

#include "parsers.h"

bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err) {
  EdgeEnv env(this);
  string path = rule_->depfile_.Evaluate(&env);

  string content = disk_interface->ReadFile(path, err);
  if (!err->empty())
    return false;
  if (content.empty())
    return true;

  MakefileParser makefile;
  if (!makefile.Parse(content, err))
    return false;

  // Check that this depfile matches our output.
  if (outputs_.size() != 1) {
    *err = "expected only one output";
    return false;
  }
  if (outputs_[0]->file_->path_ != makefile.out_) {
    *err = "expected makefile to mention '" + outputs_[0]->file_->path_ + "', "
           "got '" + makefile.out_ + "'";
    return false;
  }

  // Add all its in-edges.
  for (vector<string>::iterator i = makefile.ins_.begin();
       i != makefile.ins_.end(); ++i) {
    Node* node = state->GetNode(*i);
    for (vector<Node*>::iterator j = inputs_.begin(); j != inputs_.end(); ++j) {
      if (*j == node) {
        node = NULL;
        break;
      }
    }
    if (node) {
      inputs_.insert(inputs_.end() - order_only_deps_, node);
      node->out_edges_.push_back(this);
      ++implicit_deps_;
    }
  }

  return true;
}

void Edge::Dump() {
  printf("[ ");
  for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
    printf("%s ", (*i)->file_->path_.c_str());
  }
  printf("--%s-> ", rule_->name_.c_str());
  for (vector<Node*>::iterator i = outputs_.begin(); i != outputs_.end(); ++i) {
    printf("%s ", (*i)->file_->path_.c_str());
  }
  printf("]\n");
}

const Rule State::kPhonyRule("phony");

State::State() : build_log_(NULL) {
  AddRule(&kPhonyRule);
  build_log_ = new BuildLog;
}

const Rule* State::LookupRule(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i == rules_.end())
    return NULL;
  return i->second;
}

void State::AddRule(const Rule* rule) {
  assert(LookupRule(rule->name_) == NULL);
  rules_[rule->name_] = rule;
}

Edge* State::AddEdge(const Rule* rule) {
  Edge* edge = new Edge();
  edge->rule_ = rule;
  edge->env_ = &bindings_;
  edges_.push_back(edge);
  return edge;
}

Node* State::LookupNode(const string& path) {
  FileStat* file = stat_cache_.GetFile(path);
  if (!file->node_)
    return NULL;
  return file->node_;
}

Node* State::GetNode(const string& path) {
  FileStat* file = stat_cache_.GetFile(path);
  if (!file->node_)
    file->node_ = new Node(file);
  return file->node_;
}

void State::AddInOut(Edge* edge, Edge::InOut inout, const string& path) {
  Node* node = GetNode(path);
  if (inout == Edge::IN) {
    edge->inputs_.push_back(node);
    node->out_edges_.push_back(edge);
  } else {
    edge->outputs_.push_back(node);
    if (node->in_edge_) {
      fprintf(stderr, "WARNING: multiple rules generate %s. "
              "build will not be correct; continuing anyway\n", path.c_str());
    }
    node->in_edge_ = edge;
  }
}

bool EvalString::Parse(const string& input, string* err) {
  unparsed_ = input;

  string::size_type start, end;
  start = 0;
  do {
    end = input.find('$', start);
    if (end == string::npos) {
      end = input.size();
      break;
    }
    if (end > start)
      parsed_.push_back(make_pair(input.substr(start, end - start), RAW));
    start = end + 1;
    if (start < input.size() && input[start] == '{') {
      ++start;
      for (end = start + 1; end < input.size(); ++end) {
        if (input[end] == '}')
          break;
      }
      if (end >= input.size()) {
        *err = "expected closing curly after ${";
        return false;
      }
      parsed_.push_back(make_pair(input.substr(start, end - start), SPECIAL));
      ++end;
    } else {
      for (end = start; end < input.size(); ++end) {
        char c = input[end];
        if (!(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'))
          break;
      }
      if (end == start) {
        *err = "expected variable after $";
        return false;
      }
      parsed_.push_back(make_pair(input.substr(start, end - start), SPECIAL));
    }
    start = end;
  } while (end < input.size());
  if (end > start)
    parsed_.push_back(make_pair(input.substr(start, end - start), RAW));

  return true;
}

string EvalString::Evaluate(Env* env) const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
    if (i->second == RAW)
      result.append(i->first);
    else
      result.append(env->LookupVariable(i->first));
  }
  return result;
}
<|endoftext|>


<commit_before><commit_msg>Create kupasshpairingwizard.cpp<commit_after><|endoftext|>


<commit_before>#ifndef ARABICA_XSLT_VARIABLE_STACK_HPP
#define ARABICA_XSLT_VARIABLE_STACK_HPP

#include <XPath/XPath.hpp>
#include <map>
#include <vector>
#include "xslt_precedence.hpp"

namespace Arabica
{
namespace XSLT
{

class Variable_instance;
typedef boost::shared_ptr<Variable_instance> Variable_instance_ptr;
typedef std::map<std::string, Variable_instance_ptr> Scope;

class Variable_instance
{
public:
  Variable_instance() { }
  virtual ~Variable_instance() { }

  virtual const std::string& namespace_uri() const = 0;
  virtual const std::string& name() const = 0;
  virtual const Precedence& precedence() const = 0;
  virtual Arabica::XPath::XPathValue<std::string> value() const = 0;

  virtual void injectGlobalScope(const Scope& scope) const = 0;

private:
  Variable_instance(const Variable_instance&);
  Variable_instance& operator=(const Variable_instance&);
  bool operator==(const Variable_instance&) const;
}; // Variable_instance

class VariableStack : public Arabica::XPath::VariableResolver<std::string>
{
public:
  VariableStack()
  {
    stack_.push_back(Scope());

    params_.push_back(Scope());
    params_.push_back(Scope());
  } // VariableStack

  VariableStack(const VariableStack& rhs) :
    stack_(rhs.stack_),
    params_(rhs.params_)
  {
  } // VariableStack

  void pushScope()
  {
    stack_.push_back(Scope());
    params_.push_back(Scope());
  } // pushScope

  void chainScope()
  {
    stack_.push_back(Scope(stack_.back()));
    params_.push_back(Scope(params_.back()));
  } // chainsScope

  void popScope()
  {
    params_.pop_back();
    stack_.pop_back();
  } // popScope

  void topLevelParam(Variable_instance_ptr param)
  {
    params_.front()[clarkName(param)] = param;
  } // topLevelParam

  std::string passParam(Variable_instance_ptr param)
  {
    std::string clark_name = clarkName(param);
    Scope& params = params_.back();

    if(params.find(clark_name) != params.end())
      throw std::runtime_error("Duplicate parameter name in xsl:with-param - " + clark_name);
    params[clark_name] = param;
    return clark_name;
  } // passParam

  void unpassParam(const std::string& name)
  {
    params_.back().erase(name);
  } // unpassParam

  void declareParam(Variable_instance_ptr param)
  {
    ScopeStack::reverse_iterator p = params_.rbegin()+1;
    Scope::iterator i = p->find(clarkName(param));
    if(i == p->end())
      declareVariable(param);
    else
      declareVariable(i->second);
  } // declareParam

  void declareVariable(Variable_instance_ptr var)
  {
    std::string name = clarkName(var);
    Scope& stack = stack_.back();

    if(stack.find(name) != stack.end())
    {
      const Precedence& current_p = stack[name]->precedence();
      if(var->precedence() == current_p)
        throw std::runtime_error("Duplicate variable name : " + clarkName(var));
      if(current_p.is_descendant(var->precedence()))
        return;
    } // if ...

    if(var->precedence() == Precedence::FrozenPrecedence()) // we're running, so resolve immediately
      var->value();

    stack[name] = var;
  } // declareVariable

  void freezeTopLevel()
  {
    const Scope& top = stack_.front();
    for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v)
      v->second->injectGlobalScope(top);
    for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v)
      lookup(top, v->first);
  } // freezeTopLevel

  void injectGlobalScope(const Scope& scope)
  {
    stack_.front() = scope;
  } // injectGlobalScope

  virtual Arabica::XPath::XPathValue<std::string> resolveVariable(const std::string& namespace_uri,
                                                                  const std::string& name) const
  {
    std::string clarkName = "{" + namespace_uri + "}" + name;
    if(std::find(resolutionStack_.begin(), resolutionStack_.end(), clarkName) != resolutionStack_.end())
      throw std::runtime_error("Circular dependency: " + clarkName + " refers to itself directly or indirectly.");

    resolutionStack_.push_back(clarkName);
    Arabica::XPath::XPathValue<std::string> val = lookup(stack_.back(), clarkName);
    resolutionStack_.pop_back();

    if(val != 0)
      return val;

    val = lookup(stack_.front(), clarkName); // try our "global" scope
    if(val == 0)
      throw Arabica::XPath::UnboundVariableException(clarkName);

    return val;
  } // resolveVariable

private:
  typedef std::vector<Scope> ScopeStack;

  std::string clarkName(Variable_instance_ptr var)
  {
    return "{" + var->namespace_uri() + "}" + var->name();
  } // clarkName

  Arabica::XPath::XPathValue<std::string> lookup(const Scope& scope, const std::string& name) const
  {
    Scope::const_iterator i = scope.find(name);
    if(i == scope.end())
      return Arabica::XPath::XPathValue<std::string>(0);

    return i->second->value();
  } // lookup

  ScopeStack stack_;
  ScopeStack params_;
  mutable std::vector<std::string> resolutionStack_;
}; // class VariableStack

} // namespace XSLT
} // namespace Arabica
#endif // ARABICA_XSLT_VARIABLE_STACK_HPP

<commit_msg>fixed top level variable precedence<commit_after>#ifndef ARABICA_XSLT_VARIABLE_STACK_HPP
#define ARABICA_XSLT_VARIABLE_STACK_HPP

#include <XPath/XPath.hpp>
#include <map>
#include <vector>
#include "xslt_precedence.hpp"

namespace Arabica
{
namespace XSLT
{

class Variable_instance;
typedef boost::shared_ptr<Variable_instance> Variable_instance_ptr;
typedef std::map<std::string, Variable_instance_ptr> Scope;

class Variable_instance
{
public:
  Variable_instance() { }
  virtual ~Variable_instance() { }

  virtual const std::string& namespace_uri() const = 0;
  virtual const std::string& name() const = 0;
  virtual const Precedence& precedence() const = 0;
  virtual Arabica::XPath::XPathValue<std::string> value() const = 0;

  virtual void injectGlobalScope(const Scope& scope) const = 0;

private:
  Variable_instance(const Variable_instance&);
  Variable_instance& operator=(const Variable_instance&);
  bool operator==(const Variable_instance&) const;
}; // Variable_instance

class VariableStack : public Arabica::XPath::VariableResolver<std::string>
{
public:
  VariableStack()
  {
    stack_.push_back(Scope());

    params_.push_back(Scope());
    params_.push_back(Scope());
  } // VariableStack

  VariableStack(const VariableStack& rhs) :
    stack_(rhs.stack_),
    params_(rhs.params_)
  {
  } // VariableStack

  void pushScope()
  {
    stack_.push_back(Scope());
    params_.push_back(Scope());
  } // pushScope

  void chainScope()
  {
    stack_.push_back(Scope(stack_.back()));
    params_.push_back(Scope(params_.back()));
  } // chainsScope

  void popScope()
  {
    params_.pop_back();
    stack_.pop_back();
  } // popScope

  void topLevelParam(Variable_instance_ptr param)
  {
    params_.front()[clarkName(param)] = param;
  } // topLevelParam

  std::string passParam(Variable_instance_ptr param)
  {
    std::string clark_name = clarkName(param);
    Scope& params = params_.back();

    if(params.find(clark_name) != params.end())
      throw std::runtime_error("Duplicate parameter name in xsl:with-param - " + clark_name);
    params[clark_name] = param;
    return clark_name;
  } // passParam

  void unpassParam(const std::string& name)
  {
    params_.back().erase(name);
  } // unpassParam

  void declareParam(Variable_instance_ptr param)
  {
    ScopeStack::reverse_iterator p = params_.rbegin()+1;
    Scope::iterator i = p->find(clarkName(param));
    if(i == p->end())
      declareVariable(param);
    else
      declareVariable(i->second);
  } // declareParam

  void declareVariable(Variable_instance_ptr var)
  {
    std::string name = clarkName(var);
    Scope& stack = stack_.back();

    if(stack.find(name) != stack.end())
    {
      const Precedence& current_p = stack[name]->precedence();
      if(var->precedence() == current_p)
        throw std::runtime_error("Duplicate variable name : " + clarkName(var));
      if(current_p.is_descendant(var->precedence()))
        return;
      if(current_p > var->precedence())
        return;
    } // if ...

    if(var->precedence() == Precedence::FrozenPrecedence()) // we're running, so resolve immediately
      var->value();

    stack[name] = var;
  } // declareVariable

  void freezeTopLevel()
  {
    const Scope& top = stack_.front();
    for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v)
      v->second->injectGlobalScope(top);
    for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v)
      lookup(top, v->first);
  } // freezeTopLevel

  void injectGlobalScope(const Scope& scope)
  {
    stack_.front() = scope;
  } // injectGlobalScope

  virtual Arabica::XPath::XPathValue<std::string> resolveVariable(const std::string& namespace_uri,
                                                                  const std::string& name) const
  {
    std::string clarkName = "{" + namespace_uri + "}" + name;
    if(std::find(resolutionStack_.begin(), resolutionStack_.end(), clarkName) != resolutionStack_.end())
      throw std::runtime_error("Circular dependency: " + clarkName + " refers to itself directly or indirectly.");

    resolutionStack_.push_back(clarkName);
    Arabica::XPath::XPathValue<std::string> val = lookup(stack_.back(), clarkName);
    resolutionStack_.pop_back();

    if(val != 0)
      return val;

    val = lookup(stack_.front(), clarkName); // try our "global" scope
    if(val == 0)
      throw Arabica::XPath::UnboundVariableException(clarkName);

    return val;
  } // resolveVariable

private:
  typedef std::vector<Scope> ScopeStack;

  std::string clarkName(Variable_instance_ptr var)
  {
    return "{" + var->namespace_uri() + "}" + var->name();
  } // clarkName

  Arabica::XPath::XPathValue<std::string> lookup(const Scope& scope, const std::string& name) const
  {
    Scope::const_iterator i = scope.find(name);
    if(i == scope.end())
      return Arabica::XPath::XPathValue<std::string>(0);

    return i->second->value();
  } // lookup

  ScopeStack stack_;
  ScopeStack params_;
  mutable std::vector<std::string> resolutionStack_;
}; // class VariableStack

} // namespace XSLT
} // namespace Arabica
#endif // ARABICA_XSLT_VARIABLE_STACK_HPP

<|endoftext|>