<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
 * Modified by Cloudius Systems.
 * Copyright 2015 Cloudius Systems.
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "log.hh"
#include "streaming/stream_detail.hh"
#include "streaming/stream_transfer_task.hh"
#include "streaming/stream_session.hh"
#include "streaming/stream_manager.hh"
#include "streaming/messages/outgoing_file_message.hh"
#include "mutation_reader.hh"
#include "frozen_mutation.hh"
#include "mutation.hh"
#include "message/messaging_service.hh"

namespace streaming {

extern logging::logger sslog;

stream_transfer_task::stream_transfer_task(shared_ptr<stream_session> session, UUID cf_id)
    : stream_task(session, cf_id) {
}

stream_transfer_task::~stream_transfer_task() = default;

void stream_transfer_task::add_transfer_file(stream_detail detail) {
    assert(cf_id == detail.cf_id);
    auto message = messages::outgoing_file_message(sequence_number++, std::move(detail), session->keep_ss_table_level());
    auto size = message.header.size();
    auto seq = message.header.sequence_number;
    files.emplace(seq, std::move(message));
    total_size += size;
}

void stream_transfer_task::start() {
    using shard_id = net::messaging_service::shard_id;
    using net::messaging_verb;
    auto plan_id = session->plan_id();
    sslog.debug("[Stream #{}] stream_transfer_task: {} outgoing_file_message to send", plan_id, files.size());
    for (auto it = files.begin(); it != files.end();) {
        auto seq = it->first;
        auto& msg = it->second;
        auto id = shard_id{session->peer, session->dst_cpu_id};
        sslog.debug("[Stream #{}] stream_transfer_task: Sending outgoing_file_message seq={} msg.detail.cf_id={}", plan_id, seq, msg.detail.cf_id);
        it++;
        consume(*msg.detail.mr, [&msg, this, seq, id, plan_id] (mutation&& m) {
            msg.mutations_nr++;
            auto fm = make_lw_shared<const frozen_mutation>(m);
            return get_local_stream_manager().mutation_send_limiter().wait().then([&msg, this, fm, seq, plan_id, id] {
                auto cf_id = fm->column_family_id();
                // Skip sending the mutation if the cf is dropped after we
                // call to make_local_reader in stream_session::add_transfer_ranges()
                try {
                    session->get_local_db().find_column_family(cf_id);
                } catch (no_such_column_family&) {
                    sslog.info("[Stream #{}] SEND STREAM_MUTATION to {} skipped, cf_id={}", plan_id, id, cf_id);
                    msg.mutations_done.signal();
                    get_local_stream_manager().mutation_send_limiter().signal();
                    return stop_iteration::yes;
                }
                sslog.debug("[Stream #{}] SEND STREAM_MUTATION to {}, cf_id={}", plan_id, id, cf_id);
                session->ms().send_stream_mutation(id, session->plan_id(), *fm, session->dst_cpu_id).then_wrapped([&msg, this, plan_id, id, fm] (auto&& f) {
                    try {
                        f.get();
                        sslog.debug("[Stream #{}] GOT STREAM_MUTATION Reply", plan_id);
                        msg.mutations_done.signal();
                    } catch (...) {
                        sslog.error("[Stream #{}] stream_transfer_task: Fail to send STREAM_MUTATION to {}: {}", plan_id, id, std::current_exception());
                        msg.mutations_done.broken();
                    }
                }).finally([] {
                    get_local_stream_manager().mutation_send_limiter().signal();
                });
                return stop_iteration::no;
            });
        }).then([&msg] {
            return msg.mutations_done.wait(msg.mutations_nr);
        }).then_wrapped([this, seq, plan_id, id] (auto&& f){
            // TODO: Add retry and timeout logic
            try {
                f.get();
                this->complete(seq);
            } catch (...) {
                sslog.error("[Stream #{}] stream_transfer_task: Fail to send outgoing_file_message to {}: {}", plan_id, id, std::current_exception());
                this->session->on_error();
            }
        });
    }
}

void stream_transfer_task::complete(int sequence_number) {
    // ScheduledFuture timeout = timeoutTasks.remove(sequenceNumber);
    // if (timeout != null)
    //     timeout.cancel(false);

    files.erase(sequence_number);

    auto plan_id = session->plan_id();

    sslog.debug("[Stream #{}] stream_transfer_task: complete cf_id = {}, seq = {}", plan_id, this->cf_id, sequence_number);

    auto signal_complete = files.empty();
    // all file sent, notify session this task is complete.
    if (signal_complete) {
        using shard_id = net::messaging_service::shard_id;
        auto from = utils::fb_utilities::get_broadcast_address();
        auto id = shard_id{session->peer, session->dst_cpu_id};
        sslog.debug("[Stream #{}] SEND STREAM_MUTATION_DONE to {}, seq={}", plan_id, id, sequence_number);
        session->ms().send_stream_mutation_done(id, plan_id, this->cf_id, from, session->connecting, session->dst_cpu_id).then_wrapped([this, id, plan_id] (auto&& f) {
            try {
                f.get();
                sslog.debug("[Stream #{}] GOT STREAM_MUTATION_DONE Reply", plan_id);
                session->transfer_task_completed(this->cf_id);
            } catch (...) {
                sslog.error("[Stream #{}] stream_transfer_task: Fail to send STREAM_MUTATION_DONE to {}: {}", plan_id, id, std::current_exception());
                session->on_error();
            }
        });
    }
}

} // namespace streaming
<commit_msg>streaming: Ignore remote no_such_column_family for stream_transfer_task<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
 * Modified by Cloudius Systems.
 * Copyright 2015 Cloudius Systems.
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "log.hh"
#include "streaming/stream_detail.hh"
#include "streaming/stream_transfer_task.hh"
#include "streaming/stream_session.hh"
#include "streaming/stream_manager.hh"
#include "streaming/messages/outgoing_file_message.hh"
#include "mutation_reader.hh"
#include "frozen_mutation.hh"
#include "mutation.hh"
#include "message/messaging_service.hh"

namespace streaming {

extern logging::logger sslog;

stream_transfer_task::stream_transfer_task(shared_ptr<stream_session> session, UUID cf_id)
    : stream_task(session, cf_id) {
}

stream_transfer_task::~stream_transfer_task() = default;

void stream_transfer_task::add_transfer_file(stream_detail detail) {
    assert(cf_id == detail.cf_id);
    auto message = messages::outgoing_file_message(sequence_number++, std::move(detail), session->keep_ss_table_level());
    auto size = message.header.size();
    auto seq = message.header.sequence_number;
    files.emplace(seq, std::move(message));
    total_size += size;
}

void stream_transfer_task::start() {
    using shard_id = net::messaging_service::shard_id;
    using net::messaging_verb;
    auto plan_id = session->plan_id();
    sslog.debug("[Stream #{}] stream_transfer_task: {} outgoing_file_message to send", plan_id, files.size());
    for (auto it = files.begin(); it != files.end();) {
        auto seq = it->first;
        auto& msg = it->second;
        auto id = shard_id{session->peer, session->dst_cpu_id};
        sslog.debug("[Stream #{}] stream_transfer_task: Sending outgoing_file_message seq={} msg.detail.cf_id={}", plan_id, seq, msg.detail.cf_id);
        it++;
        consume(*msg.detail.mr, [&msg, this, seq, id, plan_id] (mutation&& m) {
            msg.mutations_nr++;
            auto fm = make_lw_shared<const frozen_mutation>(m);
            return get_local_stream_manager().mutation_send_limiter().wait().then([&msg, this, fm, seq, plan_id, id] {
                auto cf_id = fm->column_family_id();
                // Skip sending the mutation if the cf is dropped after we
                // call to make_local_reader in stream_session::add_transfer_ranges()
                try {
                    session->get_local_db().find_column_family(cf_id);
                } catch (no_such_column_family&) {
                    sslog.info("[Stream #{}] SEND STREAM_MUTATION to {} skipped, cf_id={}", plan_id, id, cf_id);
                    msg.mutations_done.signal();
                    get_local_stream_manager().mutation_send_limiter().signal();
                    return stop_iteration::yes;
                }
                sslog.debug("[Stream #{}] SEND STREAM_MUTATION to {}, cf_id={}", plan_id, id, cf_id);
                session->ms().send_stream_mutation(id, session->plan_id(), *fm, session->dst_cpu_id).then_wrapped([&msg, this, cf_id, plan_id, id, fm] (auto&& f) {
                    try {
                        f.get();
                        sslog.debug("[Stream #{}] GOT STREAM_MUTATION Reply", plan_id);
                        msg.mutations_done.signal();
                    } catch (std::exception& e) {
                        auto err = std::string(e.what());
                        // Seastar RPC does not provide exception type info, so we can not catch no_such_column_family here
                        // Need to compare the exception error msg
                        if (err.find("Can't find a column family with UUID") != std::string::npos) {
                            sslog.info("[Stream #{}] remote node {} does not have the cf_id = {}", plan_id, id, cf_id);
                            msg.mutations_done.signal();
                        } else {
                            sslog.error("[Stream #{}] stream_transfer_task: Fail to send STREAM_MUTATION to {}: {}", plan_id, id, err);
                            msg.mutations_done.broken();
                        }
                    }
                }).finally([] {
                    get_local_stream_manager().mutation_send_limiter().signal();
                });
                return stop_iteration::no;
            });
        }).then([&msg] {
            return msg.mutations_done.wait(msg.mutations_nr);
        }).then_wrapped([this, seq, plan_id, id] (auto&& f){
            // TODO: Add retry and timeout logic
            try {
                f.get();
                this->complete(seq);
            } catch (...) {
                sslog.error("[Stream #{}] stream_transfer_task: Fail to send outgoing_file_message to {}: {}", plan_id, id, std::current_exception());
                this->session->on_error();
            }
        });
    }
}

void stream_transfer_task::complete(int sequence_number) {
    // ScheduledFuture timeout = timeoutTasks.remove(sequenceNumber);
    // if (timeout != null)
    //     timeout.cancel(false);

    files.erase(sequence_number);

    auto plan_id = session->plan_id();

    sslog.debug("[Stream #{}] stream_transfer_task: complete cf_id = {}, seq = {}", plan_id, this->cf_id, sequence_number);

    auto signal_complete = files.empty();
    // all file sent, notify session this task is complete.
    if (signal_complete) {
        using shard_id = net::messaging_service::shard_id;
        auto from = utils::fb_utilities::get_broadcast_address();
        auto id = shard_id{session->peer, session->dst_cpu_id};
        sslog.debug("[Stream #{}] SEND STREAM_MUTATION_DONE to {}, seq={}", plan_id, id, sequence_number);
        session->ms().send_stream_mutation_done(id, plan_id, this->cf_id, from, session->connecting, session->dst_cpu_id).then_wrapped([this, id, plan_id] (auto&& f) {
            try {
                f.get();
                sslog.debug("[Stream #{}] GOT STREAM_MUTATION_DONE Reply", plan_id);
                session->transfer_task_completed(this->cf_id);
            } catch (...) {
                sslog.error("[Stream #{}] stream_transfer_task: Fail to send STREAM_MUTATION_DONE to {}: {}", plan_id, id, std::current_exception());
                session->on_error();
            }
        });
    }
}

} // namespace streaming
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2015 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "update_statement.hh"
#include "unimplemented.hh"

#include "cql3/operation_impl.hh"

namespace cql3 {

namespace statements {

update_statement::update_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr<attributes> attrs)
    : modification_statement{type, bound_terms, std::move(s), std::move(attrs)}
{ }

bool update_statement::require_full_clustering_key() const {
    return true;
}

void update_statement::add_update_for_key(mutation& m, const exploded_clustering_prefix& prefix, const update_parameters& params) {
    if (s->is_dense()) {
        if (!prefix || (prefix.size() == 1 && prefix.components().front().empty())) {
            throw exceptions::invalid_request_exception(sprint("Missing PRIMARY KEY part %s", s->clustering_key_columns().begin()->name_as_text()));
        }

        // An empty name for the compact value is what we use to recognize the case where there is not column
        // outside the PK, see CreateStatement.
        if (s->compact_column().name().empty()) {
            // There is no column outside the PK. So no operation could have passed through validation
            assert(_column_operations.empty());
            constants::setter(s->compact_column(), make_shared(constants::value(bytes()))).execute(m, prefix, params);
        } else {
            // dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648.
            if (_column_operations.empty()) {
                throw exceptions::invalid_request_exception(sprint("Column %s is mandatory for this COMPACT STORAGE table", s->compact_column().name_as_text()));
            }
        }
    } else {
        // If there are static columns, there also must be clustering columns, in which
        // case empty prefix can only refer to the static row.
        bool is_static_prefix = s->has_static_columns() && !prefix;
        if (type == statement_type::INSERT && !is_static_prefix) {
            auto& row = m.partition().clustered_row(clustering_key::from_clustering_prefix(*s, prefix));
            row.apply(row_marker(params.timestamp(), params.ttl(), params.expiry()));
        }
    }

    for (auto&& update : _column_operations) {
        update->execute(m, prefix, params);
    }

    warn(unimplemented::cause::INDEXES);
#if 0
        SecondaryIndexManager indexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfId).indexManager;
        if (indexManager.hasIndexes())
        {
            for (Cell cell : cf)
            {
                // Indexed values must be validated by any applicable index. See CASSANDRA-3057/4240/8081 for more details
                if (!indexManager.validate(cell))
                    throw new InvalidRequestException(String.format("Can't index column value of size %d for index %s on %s.%s",
                                                                    cell.value().remaining(),
                                                                    cfm.getColumnDefinition(cell.name()).getIndexName(),
                                                                    cfm.ksName,
                                                                    cfm.cfName));
            }
        }
    }
#endif
}

update_statement::parsed_insert::parsed_insert(::shared_ptr<cf_name> name,
                                               ::shared_ptr<attributes::raw> attrs,
                                               std::vector<::shared_ptr<column_identifier::raw>> column_names,
                                               std::vector<::shared_ptr<term::raw>> column_values,
                                               bool if_not_exists)
    : modification_statement::parsed{std::move(name), std::move(attrs), conditions_vector{}, if_not_exists, false}
    , _column_names{std::move(column_names)}
    , _column_values{std::move(column_values)}
{ }

::shared_ptr<modification_statement>
update_statement::parsed_insert::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::INSERT, bound_names->size(), schema, std::move(attrs));

    // Created from an INSERT
    if (stmt->is_counter()) {
        throw exceptions::invalid_request_exception("INSERT statement are not allowed on counter tables, use UPDATE instead");
    }

    if (_column_names.size() != _column_values.size()) {
        throw exceptions::invalid_request_exception("Unmatched column names/values");
    }

    if (_column_names.empty()) {
        throw exceptions::invalid_request_exception("No columns provided to INSERT");
    }

    for (size_t i = 0; i < _column_names.size(); i++) {
        auto id = _column_names[i]->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *id));
        }

        for (size_t j = 0; j < i; j++) {
            auto other_id = _column_names[j]->prepare_column_identifier(schema);
            if (*id == *other_id) {
                throw exceptions::invalid_request_exception(sprint("Multiple definitions found for column %s", *id));
            }
        }

        auto&& value = _column_values[i];

        if (def->is_primary_key()) {
            auto t = value->prepare(db, keyspace(), def->column_specification);
            t->collect_marker_specification(bound_names);
            stmt->add_key_value(*def, std::move(t));
        } else {
            auto operation = operation::set_value(value).prepare(db, keyspace(), *def);
            operation->collect_marker_specification(bound_names);
            stmt->add_operation(std::move(operation));
        };
    }
    return stmt;
}

update_statement::parsed_update::parsed_update(::shared_ptr<cf_name> name,
                                               ::shared_ptr<attributes::raw> attrs,
                                               std::vector<std::pair<::shared_ptr<column_identifier::raw>, ::shared_ptr<operation::raw_update>>> updates,
                                               std::vector<relation_ptr> where_clause,
                                               conditions_vector conditions)
    : modification_statement::parsed(std::move(name), std::move(attrs), std::move(conditions), false, false)
    , _updates(std::move(updates))
    , _where_clause(std::move(where_clause))
{ }

::shared_ptr<modification_statement>
update_statement::parsed_update::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::UPDATE, bound_names->size(), schema, std::move(attrs));

    for (auto&& entry : _updates) {
        auto id = entry.first->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *entry.first));
        }

        auto operation = entry.second->prepare(db, keyspace(), *def);
        operation->collect_marker_specification(bound_names);

        if (def->is_primary_key()) {
            throw exceptions::invalid_request_exception(sprint("PRIMARY KEY part %s found in SET part", *entry.first));
        }
        stmt->add_operation(std::move(operation));
    }

    stmt->process_where_clause(db, _where_clause, bound_names);
    return stmt;
}

}

}
<commit_msg>cql3: Fix quadratic behavior in update_statement::parsed_insert::prepare_internal()<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2015 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "update_statement.hh"
#include "unimplemented.hh"

#include "cql3/operation_impl.hh"

namespace cql3 {

namespace statements {

update_statement::update_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr<attributes> attrs)
    : modification_statement{type, bound_terms, std::move(s), std::move(attrs)}
{ }

bool update_statement::require_full_clustering_key() const {
    return true;
}

void update_statement::add_update_for_key(mutation& m, const exploded_clustering_prefix& prefix, const update_parameters& params) {
    if (s->is_dense()) {
        if (!prefix || (prefix.size() == 1 && prefix.components().front().empty())) {
            throw exceptions::invalid_request_exception(sprint("Missing PRIMARY KEY part %s", s->clustering_key_columns().begin()->name_as_text()));
        }

        // An empty name for the compact value is what we use to recognize the case where there is not column
        // outside the PK, see CreateStatement.
        if (s->compact_column().name().empty()) {
            // There is no column outside the PK. So no operation could have passed through validation
            assert(_column_operations.empty());
            constants::setter(s->compact_column(), make_shared(constants::value(bytes()))).execute(m, prefix, params);
        } else {
            // dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648.
            if (_column_operations.empty()) {
                throw exceptions::invalid_request_exception(sprint("Column %s is mandatory for this COMPACT STORAGE table", s->compact_column().name_as_text()));
            }
        }
    } else {
        // If there are static columns, there also must be clustering columns, in which
        // case empty prefix can only refer to the static row.
        bool is_static_prefix = s->has_static_columns() && !prefix;
        if (type == statement_type::INSERT && !is_static_prefix) {
            auto& row = m.partition().clustered_row(clustering_key::from_clustering_prefix(*s, prefix));
            row.apply(row_marker(params.timestamp(), params.ttl(), params.expiry()));
        }
    }

    for (auto&& update : _column_operations) {
        update->execute(m, prefix, params);
    }

    warn(unimplemented::cause::INDEXES);
#if 0
        SecondaryIndexManager indexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfId).indexManager;
        if (indexManager.hasIndexes())
        {
            for (Cell cell : cf)
            {
                // Indexed values must be validated by any applicable index. See CASSANDRA-3057/4240/8081 for more details
                if (!indexManager.validate(cell))
                    throw new InvalidRequestException(String.format("Can't index column value of size %d for index %s on %s.%s",
                                                                    cell.value().remaining(),
                                                                    cfm.getColumnDefinition(cell.name()).getIndexName(),
                                                                    cfm.ksName,
                                                                    cfm.cfName));
            }
        }
    }
#endif
}

update_statement::parsed_insert::parsed_insert(::shared_ptr<cf_name> name,
                                               ::shared_ptr<attributes::raw> attrs,
                                               std::vector<::shared_ptr<column_identifier::raw>> column_names,
                                               std::vector<::shared_ptr<term::raw>> column_values,
                                               bool if_not_exists)
    : modification_statement::parsed{std::move(name), std::move(attrs), conditions_vector{}, if_not_exists, false}
    , _column_names{std::move(column_names)}
    , _column_values{std::move(column_values)}
{ }

::shared_ptr<modification_statement>
update_statement::parsed_insert::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::INSERT, bound_names->size(), schema, std::move(attrs));

    // Created from an INSERT
    if (stmt->is_counter()) {
        throw exceptions::invalid_request_exception("INSERT statement are not allowed on counter tables, use UPDATE instead");
    }

    if (_column_names.size() != _column_values.size()) {
        throw exceptions::invalid_request_exception("Unmatched column names/values");
    }

    if (_column_names.empty()) {
        throw exceptions::invalid_request_exception("No columns provided to INSERT");
    }

    std::unordered_set<shared_ptr<column_identifier>> column_ids;
    for (size_t i = 0; i < _column_names.size(); i++) {
        auto id = _column_names[i]->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *id));
        }
        if (column_ids.count(id)) {
            throw exceptions::invalid_request_exception(sprint("Multiple definitions found for column %s", *id));
        }
        column_ids.emplace(id);

        auto&& value = _column_values[i];

        if (def->is_primary_key()) {
            auto t = value->prepare(db, keyspace(), def->column_specification);
            t->collect_marker_specification(bound_names);
            stmt->add_key_value(*def, std::move(t));
        } else {
            auto operation = operation::set_value(value).prepare(db, keyspace(), *def);
            operation->collect_marker_specification(bound_names);
            stmt->add_operation(std::move(operation));
        };
    }
    return stmt;
}

update_statement::parsed_update::parsed_update(::shared_ptr<cf_name> name,
                                               ::shared_ptr<attributes::raw> attrs,
                                               std::vector<std::pair<::shared_ptr<column_identifier::raw>, ::shared_ptr<operation::raw_update>>> updates,
                                               std::vector<relation_ptr> where_clause,
                                               conditions_vector conditions)
    : modification_statement::parsed(std::move(name), std::move(attrs), std::move(conditions), false, false)
    , _updates(std::move(updates))
    , _where_clause(std::move(where_clause))
{ }

::shared_ptr<modification_statement>
update_statement::parsed_update::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::UPDATE, bound_names->size(), schema, std::move(attrs));

    for (auto&& entry : _updates) {
        auto id = entry.first->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *entry.first));
        }

        auto operation = entry.second->prepare(db, keyspace(), *def);
        operation->collect_marker_specification(bound_names);

        if (def->is_primary_key()) {
            throw exceptions::invalid_request_exception(sprint("PRIMARY KEY part %s found in SET part", *entry.first));
        }
        stmt->add_operation(std::move(operation));
    }

    stmt->process_where_clause(db, _where_clause, bound_names);
    return stmt;
}

}

}
<|endoftext|>


<commit_before>#ifndef NO_ZLIB

#include "leveldb/zlib_compressor.h"

#include <zlib.h>
#include <algorithm>

namespace leveldb {

	void ZlibCompressor::compressImpl(const char* input, size_t length, ::std::string& buffer) const
	{
		const size_t BUFSIZE = 128 * 1024;
		unsigned char temp_buffer[BUFSIZE];

		//reserve enough memory to not reallocate on the fly
		buffer.reserve(compressBound(length));

		z_stream strm;
		strm.zalloc = 0;
		strm.zfree = 0;
		strm.next_in = (unsigned char *)(input);
		strm.avail_in = (uint32_t)length;
		strm.next_out = temp_buffer;
		strm.avail_out = BUFSIZE;

		deflateInit(&strm, compressionLevel);

		int deflate_res = Z_OK;
		while (strm.avail_in != 0)
		{
			int res = deflate(&strm, Z_NO_FLUSH);
			assert(res == Z_OK);
			if (strm.avail_out == 0)
			{
				buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
				strm.next_out = temp_buffer;
				strm.avail_out = BUFSIZE;
			}
		}

		while (deflate_res == Z_OK)
		{
			if (strm.avail_out == 0)
			{
				buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
				strm.next_out = temp_buffer;
				strm.avail_out = BUFSIZE;
			}
			deflate_res = deflate(&strm, Z_FINISH);
		}

		assert(deflate_res == Z_STREAM_END);
		buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE - strm.avail_out);
		deflateEnd(&strm);
	}

	int _zlibInflate(const char* input, size_t length, ::std::string &output) {
		const int CHUNK = 64 * 1024;

		int ret;
		size_t have;
		z_stream strm;
		unsigned char out[CHUNK];

		/* allocate inflate state */
		strm.zalloc = Z_NULL;
		strm.zfree = Z_NULL;
		strm.opaque = Z_NULL;
		strm.avail_in = (uint32_t)length;
		strm.next_in = (Bytef*)input;
		ret = inflateInit(&strm);
		if (ret != Z_OK)
			return ret;

		/* decompress until deflate stream ends or end of file */
		do {
			/* run inflate() on input until output buffer not full */
			do {

				strm.avail_out = CHUNK;
				strm.next_out = out;

				ret = inflate(&strm, Z_NO_FLUSH);
				assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
				switch (ret) {
				case Z_NEED_DICT:
					ret = Z_DATA_ERROR;     /* and fall through */
				case Z_DATA_ERROR:
				case Z_MEM_ERROR:
					(void)inflateEnd(&strm);
					return ret;
				}

				have = CHUNK - strm.avail_out;

				output.append((char*)out, have);

			} while (strm.avail_out == 0);

			/* done when inflate() says it's done */
		} while (ret != Z_STREAM_END);

		/* clean up and return */
		(void)inflateEnd(&strm);
		return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
	}

	bool ZlibCompressor::decompress(const char* input, size_t length, ::std::string &output) const {
		return _zlibInflate(input, length, output) == Z_OK;
	}

}

#endif<commit_msg>fixed include inconsistency<commit_after>#ifndef NO_ZLIB

#include "leveldb/zlib_compressor.h"

#include <zlib/zlib.h>
#include <algorithm>

namespace leveldb {

	void ZlibCompressor::compressImpl(const char* input, size_t length, ::std::string& buffer) const
	{
		const size_t BUFSIZE = 128 * 1024;
		unsigned char temp_buffer[BUFSIZE];

		//reserve enough memory to not reallocate on the fly
		buffer.reserve(compressBound(length));

		z_stream strm;
		strm.zalloc = 0;
		strm.zfree = 0;
		strm.next_in = (unsigned char *)(input);
		strm.avail_in = (uint32_t)length;
		strm.next_out = temp_buffer;
		strm.avail_out = BUFSIZE;

		deflateInit(&strm, compressionLevel);

		int deflate_res = Z_OK;
		while (strm.avail_in != 0)
		{
			int res = deflate(&strm, Z_NO_FLUSH);
			assert(res == Z_OK);
			if (strm.avail_out == 0)
			{
				buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
				strm.next_out = temp_buffer;
				strm.avail_out = BUFSIZE;
			}
		}

		while (deflate_res == Z_OK)
		{
			if (strm.avail_out == 0)
			{
				buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE);
				strm.next_out = temp_buffer;
				strm.avail_out = BUFSIZE;
			}
			deflate_res = deflate(&strm, Z_FINISH);
		}

		assert(deflate_res == Z_STREAM_END);
		buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE - strm.avail_out);
		deflateEnd(&strm);
	}

	int _zlibInflate(const char* input, size_t length, ::std::string &output) {
		const int CHUNK = 64 * 1024;

		int ret;
		size_t have;
		z_stream strm;
		unsigned char out[CHUNK];

		/* allocate inflate state */
		strm.zalloc = Z_NULL;
		strm.zfree = Z_NULL;
		strm.opaque = Z_NULL;
		strm.avail_in = (uint32_t)length;
		strm.next_in = (Bytef*)input;
		ret = inflateInit(&strm);
		if (ret != Z_OK)
			return ret;

		/* decompress until deflate stream ends or end of file */
		do {
			/* run inflate() on input until output buffer not full */
			do {

				strm.avail_out = CHUNK;
				strm.next_out = out;

				ret = inflate(&strm, Z_NO_FLUSH);
				assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
				switch (ret) {
				case Z_NEED_DICT:
					ret = Z_DATA_ERROR;     /* and fall through */
				case Z_DATA_ERROR:
				case Z_MEM_ERROR:
					(void)inflateEnd(&strm);
					return ret;
				}

				have = CHUNK - strm.avail_out;

				output.append((char*)out, have);

			} while (strm.avail_out == 0);

			/* done when inflate() says it's done */
		} while (ret != Z_STREAM_END);

		/* clean up and return */
		(void)inflateEnd(&strm);
		return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
	}

	bool ZlibCompressor::decompress(const char* input, size_t length, ::std::string &output) const {
		return _zlibInflate(input, length, output) == Z_OK;
	}

}

#endif<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/FloatConversion.h"
#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/String_Constant.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/Router.h"
#include "Stroika/Frameworks/WebService/Server/Basic.h"
#include "Stroika/Frameworks/WebService/Server/VariantValue.h"

#include "WebServer.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::IO::Network::HTTP;
using namespace Stroika::Frameworks::WebServer;
using namespace Stroika::Frameworks::WebService;
using namespace Stroika::Frameworks::WebService::Server;
using namespace Stroika::Frameworks::WebService::Server::VariantValue;

using Memory::BLOB;

using namespace StroikaSample::WebServices;

/*
 *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
 *  all the logic /options for HTTP interface.
 *
 *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
 *  and accesss them from the Route handler functions.
 */
class WebServer::Rep_ {
public:
    const Router       kRouter_;        // rules saying how to map urls to code
    shared_ptr<IWSAPI> fWSImpl_;        // application logic actually handling webservices
    ConnectionManager  fConnectionMgr_; // manage http connection objects, thread pool, etc

    static const WebServiceMethodDescription kVariables_;

    static const WebServiceMethodDescription kPlus_;
    static const WebServiceMethodDescription kMinus;
    static const WebServiceMethodDescription kTimes;
    static const WebServiceMethodDescription kDivide;

    Rep_ (uint16_t portNumber, const shared_ptr<IWSAPI>& wsImpl)
        : kRouter_{
              Sequence<Route>{
                  Route{
                      MethodsRegularExpressions::kOptions,
                      RegularExpression::kAny,
                      [] ([[maybe_unused]] Message* m) { Lambda_Arg_Unused_BWA (m); }},

                  Route{L""_RegEx, DefaultPage_},

                  Route{MethodsRegularExpressions::kPost, L"SetAppState"_RegEx, SetAppState_},

                  Route{MethodsRegularExpressions::kGet, L"FRED"_RegEx, [] (Request*, Response* response) {
                            response->write (L"FRED");
                            response->SetContentType (DataExchange::PredefinedInternetMediaType::kText);
                        }},

                  /*
                   * the 'variable' API demonstrates a typical REST style CRUD usage - where the 'arguments' mainly come from
                   * the URL itself.
                   */
                  Route{MethodsRegularExpressions::kGet, L"variables(/?)"_RegEx, [this] (Message* m) {
                            WriteResponse (m->PeekResponse (), kVariables_, kMapper.FromObject (fWSImpl_->Variables_GET ()));
                        }},
                  Route{MethodsRegularExpressions::kGet, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            WriteResponse (m->PeekResponse (), kVariables_, kMapper.FromObject (fWSImpl_->Variables_GET (varName)));
                        }},
                  Route{MethodsRegularExpressions::kPostOrPut, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            optional<Number> number;
                            // demo getting argument from the body
                            if (not number) {
                                // read if content-type is text (not json)
                                if (m->PeekRequest ()->GetContentType () and m->PeekRequest ()->GetContentType ()->IsA (Stroika::Foundation::DataExchange::PredefinedInternetMediaType::Text_CT ())) {
                                    String argsAsString = Streams::TextReader::New (m->PeekRequest ()->GetBody ()).ReadAll ();
                                    number              = kMapper.ToObject<Number> (DataExchange::VariantValue (argsAsString));
                                }
                            }
                            // demo getting argument from the query argument
                            if (not number) {
                                static const String                         kValueParamName_ = L"value"sv;
                                Mapping<String, DataExchange::VariantValue> args             = PickoutParamValuesFromURL (m->PeekRequest ());
                                number                                                       = Model::kMapper.ToObject<Number> (args.LookupValue (kValueParamName_));
                            }
                            // demo getting either query arg, or url encoded arg
                            if (not number) {
                                static const String kValueParamName_ = L"value"sv;
                                // NOTE - PickoutParamValues combines PickoutParamValuesFromURL, and PickoutParamValuesFromBody. You can use
                                // Either one of those instead. PickoutParamValuesFromURL assumes you know the name of the parameter, and its
                                // encoded in the query string. PickoutParamValuesFromBody assumes you have something equivilent you can parse ouf
                                // of the body, either json encoded or form-encoded (as of 2.1d23, only json encoded supported)
                                Mapping<String, DataExchange::VariantValue> args = PickoutParamValues (m->PeekRequest ());
                                number                                           = Model::kMapper.ToObject<Number> (args.LookupValue (kValueParamName_));
                            }
                            if (not number) {
                                Execution::Throw (ClientErrorException (L"Expected argument to PUT/POST variable"sv));
                            }
                            fWSImpl_->Variables_SET (varName, *number);
                            WriteResponse (m->PeekResponse (), kVariables_);
                        }},
                  Route{MethodsRegularExpressions::kDelete, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            fWSImpl_->Variables_DELETE (varName);
                            WriteResponse (m->PeekResponse (), kVariables_);
                        }},

                  /*
                   *    plus, minus, times, and divide, test-void-return all all demonstrate passing in variables through either the POST body, or query-arguments.
                   */
                  Route{L"plus"_RegEx, mkRequestHandler (kPlus_, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[=] (Number arg1, Number arg2) { return fWSImpl_->plus (arg1, arg2); }})},
                  Route{L"minus"_RegEx, mkRequestHandler (kMinus, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[=] (Number arg1, Number arg2) { return fWSImpl_->minus (arg1, arg2); }})},
                  Route{L"times"_RegEx, mkRequestHandler (kTimes, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[=] (Number arg1, Number arg2) { return fWSImpl_->times (arg1, arg2); }})},
                  Route{L"divide"_RegEx, mkRequestHandler (kDivide, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[=] (Number arg1, Number arg2) { return fWSImpl_->divide (arg1, arg2); }})},
                  Route{L"test-void-return"_RegEx, mkRequestHandler (WebServiceMethodDescription{}, Model::kMapper, Traversal::Iterable<String>{L"err-if-more-than-10"}, function<void (double)>{[=] (double check) {
                                    if (check > 10) {
                                        Execution::Throw (Execution::Exception (L"more than 10"sv));
                                    } }})},

              }}
        , fWSImpl_{wsImpl}
        , fConnectionMgr_{SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options{{}, Socket::BindFlags{}, String{L"Stroika-Sample-WebServices/"} + AppVersion::kVersion.AsMajorMinorString ()}}
    {
        // @todo - move this to some framework-specific regtests...
        using VariantValue         = DataExchange::VariantValue;
        Sequence<VariantValue> tmp = OrderParamValues (Iterable<String>{L"page", L"xxx"}, PickoutParamValuesFromURL (URI{L"http://www.sophist.com?page=5"}));
        Assert (tmp.size () == 2);
        Assert (tmp[0] == 5);
        Assert (tmp[1] == nullptr);
    }
    // Can declare arguments as Request*,Response*
    static void DefaultPage_ (Request*, Response* response)
    {
        WriteDocsPage (
            response,
            Sequence<WebServiceMethodDescription>{
                kVariables_,
                kPlus_,
                kMinus,
                kTimes,
                kDivide,

            },
            DocsOptions{L"Stroika Sample WebService - Web Methods"_k});
    }
    static void SetAppState_ (Message* message)
    {
        String argsAsString = Streams::TextReader::New (message->PeekRequest ()->GetBody ()).ReadAll ();
        message->PeekResponse ()->writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
        message->PeekResponse ()->SetContentType (DataExchange::PredefinedInternetMediaType::kText_HTML);
    }
};

/*
 *  Documentation on WSAPIs
 */
const WebServiceMethodDescription WebServer::Rep_::kVariables_{
    L"variables"_k,
    Set<String>{Methods::kGet, Methods::kPost, Methods::kDelete},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl http://localhost:8080/variables -v --output -",
        L"curl http://localhost:8080/variables/x -v --output -",
        L"curl  -X POST http://localhost:8080/variables/x -v --output -",
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"value\": 3}' http://localhost:8080/variables/x --output -",
        L"curl -H \"Content-Type: text/plain\" -X POST -d '3' http://localhost:8080/variables/x --output -"},
    Sequence<String>{L"@todo - this is a rough draft (but functional). It could use alot of cleanup and review to see WHICH way I recommend using, and just provide the recommended ways in samples"},
};

const WebServiceMethodDescription WebServer::Rep_::kPlus_{
    L"plus"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\": 3, \"arg2\": 5 }' http://localhost:8080/plus --output -",
    },
    Sequence<String>{L"add the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kMinus{
    L"minus"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\": 4.5, \"arg2\": -3.23 }' http://localhost:8080/minus --output -",
    },
    Sequence<String>{L"subtract the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kTimes{
    L"times"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + 4i\", \"arg2\": 3.2 }' http://localhost:8080/times --output -",
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + i\", \"arg2\": \"2 - i\" }' http://localhost:8080/times --output -",
    },
    Sequence<String>{L"multiply the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kDivide{
    L"divide"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + i\", \"arg2\": 0 }' http://localhost:8080/divide --output -",
    },
    Sequence<String>{L"divide the two argument numbers"},
};

WebServer::WebServer (uint16_t portNumber, const shared_ptr<IWSAPI>& wsImpl)
    : fRep_ (make_shared<Rep_> (portNumber, wsImpl))
{
}
<commit_msg>c++20 cleanup<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/FloatConversion.h"
#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/String_Constant.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/Router.h"
#include "Stroika/Frameworks/WebService/Server/Basic.h"
#include "Stroika/Frameworks/WebService/Server/VariantValue.h"

#include "WebServer.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::IO::Network::HTTP;
using namespace Stroika::Frameworks::WebServer;
using namespace Stroika::Frameworks::WebService;
using namespace Stroika::Frameworks::WebService::Server;
using namespace Stroika::Frameworks::WebService::Server::VariantValue;

using Memory::BLOB;

using namespace StroikaSample::WebServices;

/*
 *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
 *  all the logic /options for HTTP interface.
 *
 *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
 *  and accesss them from the Route handler functions.
 */
class WebServer::Rep_ {
public:
    const Router       kRouter_;        // rules saying how to map urls to code
    shared_ptr<IWSAPI> fWSImpl_;        // application logic actually handling webservices
    ConnectionManager  fConnectionMgr_; // manage http connection objects, thread pool, etc

    static const WebServiceMethodDescription kVariables_;

    static const WebServiceMethodDescription kPlus_;
    static const WebServiceMethodDescription kMinus;
    static const WebServiceMethodDescription kTimes;
    static const WebServiceMethodDescription kDivide;

    Rep_ (uint16_t portNumber, const shared_ptr<IWSAPI>& wsImpl)
        : kRouter_{
              Sequence<Route>{
                  Route{
                      MethodsRegularExpressions::kOptions,
                      RegularExpression::kAny,
                      [] ([[maybe_unused]] Message* m) { Lambda_Arg_Unused_BWA (m); }},

                  Route{L""_RegEx, DefaultPage_},

                  Route{MethodsRegularExpressions::kPost, L"SetAppState"_RegEx, SetAppState_},

                  Route{MethodsRegularExpressions::kGet, L"FRED"_RegEx, [] (Request*, Response* response) {
                            response->write (L"FRED");
                            response->SetContentType (DataExchange::PredefinedInternetMediaType::kText);
                        }},

                  /*
                   * the 'variable' API demonstrates a typical REST style CRUD usage - where the 'arguments' mainly come from
                   * the URL itself.
                   */
                  Route{MethodsRegularExpressions::kGet, L"variables(/?)"_RegEx, [this] (Message* m) {
                            WriteResponse (m->PeekResponse (), kVariables_, kMapper.FromObject (fWSImpl_->Variables_GET ()));
                        }},
                  Route{MethodsRegularExpressions::kGet, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            WriteResponse (m->PeekResponse (), kVariables_, kMapper.FromObject (fWSImpl_->Variables_GET (varName)));
                        }},
                  Route{MethodsRegularExpressions::kPostOrPut, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            optional<Number> number;
                            // demo getting argument from the body
                            if (not number) {
                                // read if content-type is text (not json)
                                if (m->PeekRequest ()->GetContentType () and m->PeekRequest ()->GetContentType ()->IsA (Stroika::Foundation::DataExchange::PredefinedInternetMediaType::Text_CT ())) {
                                    String argsAsString = Streams::TextReader::New (m->PeekRequest ()->GetBody ()).ReadAll ();
                                    number              = kMapper.ToObject<Number> (DataExchange::VariantValue (argsAsString));
                                }
                            }
                            // demo getting argument from the query argument
                            if (not number) {
                                static const String                         kValueParamName_ = L"value"sv;
                                Mapping<String, DataExchange::VariantValue> args             = PickoutParamValuesFromURL (m->PeekRequest ());
                                number                                                       = Model::kMapper.ToObject<Number> (args.LookupValue (kValueParamName_));
                            }
                            // demo getting either query arg, or url encoded arg
                            if (not number) {
                                static const String kValueParamName_ = L"value"sv;
                                // NOTE - PickoutParamValues combines PickoutParamValuesFromURL, and PickoutParamValuesFromBody. You can use
                                // Either one of those instead. PickoutParamValuesFromURL assumes you know the name of the parameter, and its
                                // encoded in the query string. PickoutParamValuesFromBody assumes you have something equivilent you can parse ouf
                                // of the body, either json encoded or form-encoded (as of 2.1d23, only json encoded supported)
                                Mapping<String, DataExchange::VariantValue> args = PickoutParamValues (m->PeekRequest ());
                                number                                           = Model::kMapper.ToObject<Number> (args.LookupValue (kValueParamName_));
                            }
                            if (not number) {
                                Execution::Throw (ClientErrorException (L"Expected argument to PUT/POST variable"sv));
                            }
                            fWSImpl_->Variables_SET (varName, *number);
                            WriteResponse (m->PeekResponse (), kVariables_);
                        }},
                  Route{MethodsRegularExpressions::kDelete, L"variables/(.+)"_RegEx, [this] (Message* m, const String& varName) {
                            fWSImpl_->Variables_DELETE (varName);
                            WriteResponse (m->PeekResponse (), kVariables_);
                        }},

                  /*
                   *    plus, minus, times, and divide, test-void-return all all demonstrate passing in variables through either the POST body, or query-arguments.
                   */
                  Route{L"plus"_RegEx, mkRequestHandler (kPlus_, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[this] (Number arg1, Number arg2) { return fWSImpl_->plus (arg1, arg2); }})},
                  Route{L"minus"_RegEx, mkRequestHandler (kMinus, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[this] (Number arg1, Number arg2) { return fWSImpl_->minus (arg1, arg2); }})},
                  Route{L"times"_RegEx, mkRequestHandler (kTimes, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[this] (Number arg1, Number arg2) { return fWSImpl_->times (arg1, arg2); }})},
                  Route{L"divide"_RegEx, mkRequestHandler (kDivide, Model::kMapper, Traversal::Iterable<String>{L"arg1", L"arg2"}, function<Number (Number, Number)>{[this] (Number arg1, Number arg2) { return fWSImpl_->divide (arg1, arg2); }})},
                  Route{L"test-void-return"_RegEx, mkRequestHandler (WebServiceMethodDescription{}, Model::kMapper, Traversal::Iterable<String>{L"err-if-more-than-10"}, function<void (double)>{[] (double check) {
                                    if (check > 10) {
                                        Execution::Throw (Execution::Exception (L"more than 10"sv));
                                    } }})},

              }}
        , fWSImpl_{wsImpl}
        , fConnectionMgr_{SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options{{}, Socket::BindFlags{}, String{L"Stroika-Sample-WebServices/"} + AppVersion::kVersion.AsMajorMinorString ()}}
    {
        // @todo - move this to some framework-specific regtests...
        using VariantValue         = DataExchange::VariantValue;
        Sequence<VariantValue> tmp = OrderParamValues (Iterable<String>{L"page", L"xxx"}, PickoutParamValuesFromURL (URI{L"http://www.sophist.com?page=5"}));
        Assert (tmp.size () == 2);
        Assert (tmp[0] == 5);
        Assert (tmp[1] == nullptr);
    }
    // Can declare arguments as Request*,Response*
    static void DefaultPage_ (Request*, Response* response)
    {
        WriteDocsPage (
            response,
            Sequence<WebServiceMethodDescription>{
                kVariables_,
                kPlus_,
                kMinus,
                kTimes,
                kDivide,

            },
            DocsOptions{L"Stroika Sample WebService - Web Methods"_k});
    }
    static void SetAppState_ (Message* message)
    {
        String argsAsString = Streams::TextReader::New (message->PeekRequest ()->GetBody ()).ReadAll ();
        message->PeekResponse ()->writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
        message->PeekResponse ()->SetContentType (DataExchange::PredefinedInternetMediaType::kText_HTML);
    }
};

/*
 *  Documentation on WSAPIs
 */
const WebServiceMethodDescription WebServer::Rep_::kVariables_{
    L"variables"_k,
    Set<String>{Methods::kGet, Methods::kPost, Methods::kDelete},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl http://localhost:8080/variables -v --output -",
        L"curl http://localhost:8080/variables/x -v --output -",
        L"curl  -X POST http://localhost:8080/variables/x -v --output -",
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"value\": 3}' http://localhost:8080/variables/x --output -",
        L"curl -H \"Content-Type: text/plain\" -X POST -d '3' http://localhost:8080/variables/x --output -"},
    Sequence<String>{L"@todo - this is a rough draft (but functional). It could use alot of cleanup and review to see WHICH way I recommend using, and just provide the recommended ways in samples"},
};

const WebServiceMethodDescription WebServer::Rep_::kPlus_{
    L"plus"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\": 3, \"arg2\": 5 }' http://localhost:8080/plus --output -",
    },
    Sequence<String>{L"add the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kMinus{
    L"minus"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\": 4.5, \"arg2\": -3.23 }' http://localhost:8080/minus --output -",
    },
    Sequence<String>{L"subtract the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kTimes{
    L"times"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + 4i\", \"arg2\": 3.2 }' http://localhost:8080/times --output -",
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + i\", \"arg2\": \"2 - i\" }' http://localhost:8080/times --output -",
    },
    Sequence<String>{L"multiply the two argument numbers"},
};
const WebServiceMethodDescription WebServer::Rep_::kDivide{
    L"divide"_k,
    Set<String>{String_Constant{Methods::kPost}},
    DataExchange::PredefinedInternetMediaType::kJSON,
    {},
    Sequence<String>{
        L"curl -H \"Content-Type: application/json\" -X POST -d '{\"arg1\":\"2 + i\", \"arg2\": 0 }' http://localhost:8080/divide --output -",
    },
    Sequence<String>{L"divide the two argument numbers"},
};

WebServer::WebServer (uint16_t portNumber, const shared_ptr<IWSAPI>& wsImpl)
    : fRep_ (make_shared<Rep_> (portNumber, wsImpl))
{
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: SpellAttrib.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2004-09-17 14:12:46 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#pragma hdrstop

#ifndef _SVX_SPELL_ATTRIB
#include <SpellAttrib.hxx>
#endif
#ifndef _SV_FONT_HXX
#include <vcl/font.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_REFERENCE_HXX_
#include <com/sun/star/uno/Reference.hxx>
#endif
#ifndef _COM_SUN_STAR_LINGUISTIC2_XSPELLALTERNATIVES_HPP_
#include <com/sun/star/linguistic2/XSpellAlternatives.hpp>
#endif
using namespace svx;
using namespace com::sun::star::linguistic2;
using namespace com::sun::star::uno;
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::SpellErrorAttrib(Reference<XSpellAlternatives> xAlt) :
    TextAttrib(TEXTATTR_SPELL_ERROR),
    m_xAlternatives(xAlt)
{
}
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::SpellErrorAttrib( const SpellErrorAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_ERROR),
    m_xAlternatives( rAttr.m_xAlternatives )
{
}
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::~SpellErrorAttrib()
{
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellErrorAttrib::SetFont( Font& rFont ) const
{
    //this attribute doesn't have a visual effect
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib*     SpellErrorAttrib::Clone() const
{
    return new SpellErrorAttrib(*this);
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
int SpellErrorAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
             m_xAlternatives.get() == static_cast<const SpellErrorAttrib&>(rAttr).m_xAlternatives.get();
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::SpellLanguageAttrib(LanguageType eLang) :
    TextAttrib(TEXTATTR_SPELL_LANGUAGE),
    m_eLanguage(eLang)
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::SpellLanguageAttrib( const SpellLanguageAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_LANGUAGE),
    m_eLanguage(rAttr.m_eLanguage)
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::~SpellLanguageAttrib()
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellLanguageAttrib::SetFont( Font& rFont ) const
{
    //no visual effect
}
/*-- 10.09.2003 14:27:44---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib* SpellLanguageAttrib::Clone() const
{
    return new SpellLanguageAttrib(*this);
}
/*-- 10.09.2003 14:27:44---------------------------------------------------

  -----------------------------------------------------------------------*/
int SpellLanguageAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
            m_eLanguage == static_cast<const SpellLanguageAttrib&>(rAttr).m_eLanguage;
}
/*-- 31.10.2003 16:07:45---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::SpellBackgroundAttrib(const Color& rCol) :
    TextAttrib(TEXTATTR_SPELL_BACKGROUND),
    m_aBackgroundColor(rCol)
{
}
/*-- 31.10.2003 16:07:45---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::SpellBackgroundAttrib( const SpellBackgroundAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_BACKGROUND),
    m_aBackgroundColor(rAttr.m_aBackgroundColor)
{
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::~SpellBackgroundAttrib()
{
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellBackgroundAttrib::SetFont( Font& rFont ) const
{
    rFont.SetFillColor(m_aBackgroundColor);
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib* SpellBackgroundAttrib::Clone() const
{
    return new SpellBackgroundAttrib(*this);
}
/*-- 31.10.2003 16:07:47---------------------------------------------------

  -----------------------------------------------------------------------*/
int  SpellBackgroundAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
            m_aBackgroundColor == static_cast<const SpellBackgroundAttrib&>(rAttr).m_aBackgroundColor;
}

<commit_msg>INTEGRATION: CWS visibility01 (1.2.78); FILE MERGED 2004/11/01 20:17:11 mhu 1.2.78.1: #i35758# Undefine SVX_DLLIMPLEMENTATION for objects going into 'cui' library.<commit_after>/*************************************************************************
 *
 *  $RCSfile: SpellAttrib.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2005-01-21 16:25:53 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifdef SVX_DLLIMPLEMENTATION
#undef SVX_DLLIMPLEMENTATION
#endif

#ifndef _SVX_SPELL_ATTRIB
#include <SpellAttrib.hxx>
#endif
#ifndef _SV_FONT_HXX
#include <vcl/font.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_REFERENCE_HXX_
#include <com/sun/star/uno/Reference.hxx>
#endif
#ifndef _COM_SUN_STAR_LINGUISTIC2_XSPELLALTERNATIVES_HPP_
#include <com/sun/star/linguistic2/XSpellAlternatives.hpp>
#endif
using namespace svx;
using namespace com::sun::star::linguistic2;
using namespace com::sun::star::uno;
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::SpellErrorAttrib(Reference<XSpellAlternatives> xAlt) :
    TextAttrib(TEXTATTR_SPELL_ERROR),
    m_xAlternatives(xAlt)
{
}
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::SpellErrorAttrib( const SpellErrorAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_ERROR),
    m_xAlternatives( rAttr.m_xAlternatives )
{
}
/*-- 10.09.2003 12:54:34---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellErrorAttrib::~SpellErrorAttrib()
{
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellErrorAttrib::SetFont( Font& rFont ) const
{
    //this attribute doesn't have a visual effect
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib*     SpellErrorAttrib::Clone() const
{
    return new SpellErrorAttrib(*this);
}
/*-- 10.09.2003 12:54:35---------------------------------------------------

  -----------------------------------------------------------------------*/
int SpellErrorAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
             m_xAlternatives.get() == static_cast<const SpellErrorAttrib&>(rAttr).m_xAlternatives.get();
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::SpellLanguageAttrib(LanguageType eLang) :
    TextAttrib(TEXTATTR_SPELL_LANGUAGE),
    m_eLanguage(eLang)
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::SpellLanguageAttrib( const SpellLanguageAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_LANGUAGE),
    m_eLanguage(rAttr.m_eLanguage)
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellLanguageAttrib::~SpellLanguageAttrib()
{
}
/*-- 10.09.2003 14:27:43---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellLanguageAttrib::SetFont( Font& rFont ) const
{
    //no visual effect
}
/*-- 10.09.2003 14:27:44---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib* SpellLanguageAttrib::Clone() const
{
    return new SpellLanguageAttrib(*this);
}
/*-- 10.09.2003 14:27:44---------------------------------------------------

  -----------------------------------------------------------------------*/
int SpellLanguageAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
            m_eLanguage == static_cast<const SpellLanguageAttrib&>(rAttr).m_eLanguage;
}
/*-- 31.10.2003 16:07:45---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::SpellBackgroundAttrib(const Color& rCol) :
    TextAttrib(TEXTATTR_SPELL_BACKGROUND),
    m_aBackgroundColor(rCol)
{
}
/*-- 31.10.2003 16:07:45---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::SpellBackgroundAttrib( const SpellBackgroundAttrib& rAttr ) :
    TextAttrib(TEXTATTR_SPELL_BACKGROUND),
    m_aBackgroundColor(rAttr.m_aBackgroundColor)
{
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
SpellBackgroundAttrib::~SpellBackgroundAttrib()
{
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
void SpellBackgroundAttrib::SetFont( Font& rFont ) const
{
    rFont.SetFillColor(m_aBackgroundColor);
}
/*-- 31.10.2003 16:07:46---------------------------------------------------

  -----------------------------------------------------------------------*/
TextAttrib* SpellBackgroundAttrib::Clone() const
{
    return new SpellBackgroundAttrib(*this);
}
/*-- 31.10.2003 16:07:47---------------------------------------------------

  -----------------------------------------------------------------------*/
int  SpellBackgroundAttrib::operator==( const TextAttrib& rAttr ) const
{
    return Which() == rAttr.Which() &&
            m_aBackgroundColor == static_cast<const SpellBackgroundAttrib&>(rAttr).m_aBackgroundColor;
}

<|endoftext|>