<commit_before>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#ifndef UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
#define UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED

#include <uavcan/build_config.hpp>
#include <uavcan/util/multiset.hpp>
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>

#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
#endif

#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
# include <functional>
#endif

namespace uavcan
{

template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
    typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
                                                         NumStaticReceiversAndBuffers,
                                                         NumStaticReceiversAndBuffers,
                                                         TransferListenerWithFilter>::Type Type;
};

/**
 * This struct describes a pending service call.
 * Refer to @ref ServiceClient to learn more about service calls.
 */
struct ServiceCallID
{
    NodeID server_node_id;
    TransferID transfer_id;

    ServiceCallID() { }

    ServiceCallID(NodeID arg_server_node_id, TransferID arg_transfer_id)
        : server_node_id(arg_server_node_id)
        , transfer_id(arg_transfer_id)
    { }

    bool operator==(const ServiceCallID rhs) const
    {
        return (rhs.server_node_id == server_node_id) &&
               (rhs.transfer_id == transfer_id);
    }

    bool isValid() const { return server_node_id.isUnicast(); }
};

/**
 * Object of this type will be returned to the application as a result of service call.
 * Note that application ALWAYS gets this result, even when it times out or fails because of some other reason.
 */
template <typename DataType>
class UAVCAN_EXPORT ServiceCallResult
{
public:
    typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;

    enum Status { Success, ErrorTimeout };

private:
    const Status status_;               ///< Whether successful or not. Failure to decode the response causes timeout.
    ServiceCallID call_id_;             ///< Identifies the call
    ResponseFieldType& response_;       ///< Returned data structure. Value undefined if the service call has failed.

public:
    ServiceCallResult(Status arg_status, ServiceCallID arg_call_id, ResponseFieldType& arg_response)
        : status_(arg_status)
        , call_id_(arg_call_id)
        , response_(arg_response)
    {
        UAVCAN_ASSERT(call_id_.isValid());
        UAVCAN_ASSERT((status_ == Success) || (status_ == ErrorTimeout));
    }

    /**
     * Shortcut to quickly check whether the call was successful.
     */
    bool isSuccessful() const { return status_ == Success; }

    Status getStatus() const { return status_; }

    ServiceCallID getCallID() const { return call_id_; }

    const ResponseFieldType& getResponse() const { return response_; }
    ResponseFieldType& getResponse() { return response_; }
};

/**
 * This operator neatly prints the service call result prepended with extra data like Server Node ID.
 * The extra data will be represented as YAML comment.
 */
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
{
    s << "# Service call result [" << DataType::getDataTypeFullName() << "] "
      << (scr.isSuccessful() ? "OK" : "FAILURE")
      << " server_node_id=" << int(scr.getCallID().server_node_id.get())
      << " tid=" << int(scr.getCallID().transfer_id.get()) << "\n";
    if (scr.isSuccessful())
    {
        s << scr.getResponse();
    }
    else
    {
        s << "# (no data)";
    }
    return s;
}

/**
 * Do not use directly.
 */
class ServiceClientBase : public ITransferAcceptanceFilter, Noncopyable
{
    const DataTypeDescriptor* data_type_descriptor_;  ///< This will be initialized at the time of first call

protected:
    class CallState : DeadlineHandler
    {
        ServiceClientBase& owner_;
        const ServiceCallID id_;

        virtual void handleDeadline(MonotonicTime);

    public:
        CallState(INode& node, ServiceClientBase& owner, ServiceCallID call_id)
            : DeadlineHandler(node.getScheduler())
            , owner_(owner)
            , id_(call_id)
        {
            UAVCAN_ASSERT(id_.isValid());
            DeadlineHandler::startWithDelay(owner_.request_timeout_);
        }

        bool doesMatch(ServiceCallID call_id) const { return call_id == id_; }

        bool operator==(const CallState& rhs) const
        {
            return (&owner_ == &rhs.owner_) && (id_ == rhs.id_);
        }
    };

    struct CallStateMatchingPredicate
    {
        const ServiceCallID id;
        CallStateMatchingPredicate(ServiceCallID reference) : id(reference) { }
        bool operator()(const CallState& state) const { return state.doesMatch(id); }
    };

    MonotonicDuration request_timeout_;

    ServiceClientBase()
        : data_type_descriptor_(NULL)
        , request_timeout_(getDefaultRequestTimeout())
    { }

    virtual ~ServiceClientBase() { }

    int prepareToCall(INode& node, const char* dtname, NodeID server_node_id, ServiceCallID& out_call_id);

    virtual void handleTimeout(ServiceCallID call_id) = 0;

public:
    /**
     * It's not recommended to override default timeouts.
     * Change of this value will not affect pending calls.
     */
    static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(500); }
    static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
    static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
};

/**
 * Use this class to invoke services on remote nodes.
 *
 * @tparam DataType_        Service data type.
 *
 * @tparam Callback_        Service response will be delivered through the callback of this type.
 *                          In C++11 mode this type defaults to std::function<>.
 *                          In C++03 mode this type defaults to a plain function pointer; use binder to
 *                          call member functions as callbacks.
 */
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
          typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
#else
          typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
#endif
          unsigned NumStaticCalls_ = 1
          >
class UAVCAN_EXPORT ServiceClient
    : public GenericSubscriber<DataType_,
                               typename DataType_::Response,
                               typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
                                                                                NumStaticCalls_>::Type>
    , public ServiceClientBase
{
public:
    typedef DataType_ DataType;
    typedef typename DataType::Request RequestType;
    typedef typename DataType::Response ResponseType;
    typedef ServiceCallResult<DataType> ServiceCallResultType;
    typedef Callback_ Callback;

    enum { NumStaticCalls = NumStaticCalls_ };

private:
    typedef ServiceClient<DataType, Callback> SelfType;
    typedef GenericPublisher<DataType, RequestType> PublisherType;
    typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
            TransferListenerType;
    typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;

#if 0
    typedef Multiset<CallState, NumStaticCalls> CallRegistry;
    CallRegistry call_registry_;
#endif

    PublisherType publisher_;
    Callback callback_;

    virtual bool shouldAcceptFrame(const RxFrame& frame) const; // Called from the transfer listener

    void invokeCallback(ServiceCallResultType& result);

    virtual void handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response);

    virtual void handleTimeout(ServiceCallID call_id);

    int addCallState(ServiceCallID call_id);

public:
    /**
     * @param node      Node instance this client will be registered with.
     * @param callback  Callback instance. Optional, can be assigned later.
     */
    explicit ServiceClient(INode& node, const Callback& callback = Callback())
        : SubscriberType(node)
        , publisher_(node, getDefaultRequestTimeout())
        , callback_(callback)
    {
        setRequestTimeout(getDefaultRequestTimeout());
#if UAVCAN_DEBUG
        UAVCAN_ASSERT(getRequestTimeout() == getDefaultRequestTimeout());  // Making sure default values are OK
#endif
    }

    virtual ~ServiceClient() { cancelAll(); }

    /**
     * Shall be called before first use.
     * Returns negative error code.
     */
    int init()
    {
        return publisher_.init();
    }

    /**
     * Performs non-blocking service call.
     * This method transmits the service request and returns immediately.
     *
     * Service response will be delivered into the application via the callback.
     * Note that the callback will ALWAYS be called even if the service call has timed out; the
     * actual result of the call (success/failure) will be passed to the callback as well.
     *
     * Returns negative error code.
     */
    int call(NodeID server_node_id, const RequestType& request);

    /**
     * Same as plain @ref call() above, but this overload also returns the call ID of the new call.
     */
    int call(NodeID server_node_id, const RequestType& request, ServiceCallID& out_call_id);

    /**
     * Cancels certain call referred via call ID structure.
     */
    void cancel(ServiceCallID call_id);

    /**
     * Cancels all pending calls.
     */
    void cancelAll();

    /**
     * Service response callback must be set prior service call.
     */
    const Callback& getCallback() const { return callback_; }
    void setCallback(const Callback& cb) { callback_ = cb; }

#if 0
    unsigned getNumPendingCalls() const { return call_registry_.getSize(); }
#endif

#if 0
    bool hasPendingCalls() const { return !call_registry_.isEmpty(); }
#else
    bool hasPendingCalls() const { return false; }
#endif

    /**
     * Returns the number of failed attempts to decode received response. Generally, a failed attempt means either:
     * - Transient failure in the transport layer.
     * - Incompatible data types.
     */
    uint32_t getResponseFailureCount() const { return SubscriberType::getFailureCount(); }

    /**
     * Request timeouts.
     * There is no such config as TX timeout - TX timeouts are configured automagically according to request timeouts.
     * Not recommended to change.
     */
    MonotonicDuration getRequestTimeout() const { return request_timeout_; }
    void setRequestTimeout(MonotonicDuration timeout)
    {
        timeout = max(timeout, getMinRequestTimeout());
        timeout = min(timeout, getMaxRequestTimeout());

        publisher_.setTxTimeout(timeout);
        request_timeout_ = max(timeout, publisher_.getTxTimeout());  // No less than TX timeout
    }
};

// ----------------------------------------------------------------------------

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
{
    if (try_implicit_cast<bool>(callback_, true))
    {
        callback_(result);
    }
    else
    {
        handleFatalError("Srv client clbk");
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
{
    UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher

#if 0
    return call_registry_.findFirst(CallStateMatchingPredicate(ServiceCallID(frame.getSrcNodeID(),
                                                                             frame.getTransferID()))) != NULL;
#else
    (void)frame;
    return false;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
    UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);

    ServiceCallID call_id(response.getSrcNodeID(), response.getTransferID());
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::Success, call_id, response);    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleTimeout(ServiceCallID call_id)
{
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, call_id,
                                 SubscriberType::getReceivedStructStorage());    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
{
#if 0
    if (call_registry_.isEmpty())
    {
        const int subscriber_res = SubscriberType::startAsServiceResponseListener();
        if (subscriber_res < 0)
        {
            UAVCAN_TRACE("ServiceClient", "Failed to start the subscriber, error: %i", subscriber_res);
            return subscriber_res;
        }
    }

    if (call_registry_.add(CallState(SubscriberType::getNode(), *this, call_id)) == NULL)
    {
        SubscriberType::stop();
        return -ErrMemory;
    }

    return 0;
#else
    (void)call_id;
    return 0;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id,
                                                               const RequestType& request)
{
   ServiceCallID dummy;
   return call(server_node_id, request, dummy);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id,
                                                               const RequestType& request,
                                                               ServiceCallID& out_call_id)
{
    if (!try_implicit_cast<bool>(callback_, true))
    {
        UAVCAN_TRACE("ServiceClient", "Invalid callback");
        return -ErrInvalidConfiguration;
    }

    /*
     * Common procedures that don't depend on the struct data type
     */
    TransferID transfer_id;
    const int prep_res =
        prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);
    if (prep_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
        return prep_res;
    }

    /*
     * Initializing the call state - this will start the subscriber ad-hoc
     */
    const int call_state_res = addCallState(out_call_id);
    if (call_state_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to add call state, error: %i", call_state_res);
        return call_state_res;
    }

    /*
     * Configuring the listener so it will accept only the matching responses
     * TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
     */
    TransferListenerType* const tl = SubscriberType::getTransferListener();
    if (tl == NULL)
    {
        UAVCAN_ASSERT(0);  // Must have been created
        cancel(out_call_id);
        return -ErrLogic;
    }
    tl->installAcceptanceFilter(this);

    /*
     * Publishing the request
     */
    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id, transfer_id);
    if (publisher_res < 0)
    {
        cancel(out_call_id);
        return publisher_res;
    }

    return 0;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancel(ServiceCallID call_id)
{
#if 0
    call_registry_.remove(call_id);
    if (call_registry_.isEmpty())
    {
        SubscriberType::stop();
    }
#else
    (void)call_id;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAll()
{
#if 0
    call_registry_.removeAll();
#endif
    SubscriberType::stop();
}

}

#endif // UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
<commit_msg>Temporary fix for assertion failure in tests; 12 tests are failing<commit_after>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#ifndef UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
#define UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED

#include <uavcan/build_config.hpp>
#include <uavcan/util/multiset.hpp>
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>

#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
#endif

#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
# include <functional>
#endif

namespace uavcan
{

template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
    typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
                                                         NumStaticReceiversAndBuffers,
                                                         NumStaticReceiversAndBuffers,
                                                         TransferListenerWithFilter>::Type Type;
};

/**
 * This struct describes a pending service call.
 * Refer to @ref ServiceClient to learn more about service calls.
 */
struct ServiceCallID
{
    NodeID server_node_id;
    TransferID transfer_id;

    ServiceCallID() { }

    ServiceCallID(NodeID arg_server_node_id, TransferID arg_transfer_id)
        : server_node_id(arg_server_node_id)
        , transfer_id(arg_transfer_id)
    { }

    bool operator==(const ServiceCallID rhs) const
    {
        return (rhs.server_node_id == server_node_id) &&
               (rhs.transfer_id == transfer_id);
    }

    bool isValid() const { return server_node_id.isUnicast(); }
};

/**
 * Object of this type will be returned to the application as a result of service call.
 * Note that application ALWAYS gets this result, even when it times out or fails because of some other reason.
 */
template <typename DataType>
class UAVCAN_EXPORT ServiceCallResult
{
public:
    typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;

    enum Status { Success, ErrorTimeout };

private:
    const Status status_;               ///< Whether successful or not. Failure to decode the response causes timeout.
    ServiceCallID call_id_;             ///< Identifies the call
    ResponseFieldType& response_;       ///< Returned data structure. Value undefined if the service call has failed.

public:
    ServiceCallResult(Status arg_status, ServiceCallID arg_call_id, ResponseFieldType& arg_response)
        : status_(arg_status)
        , call_id_(arg_call_id)
        , response_(arg_response)
    {
        UAVCAN_ASSERT(call_id_.isValid());
        UAVCAN_ASSERT((status_ == Success) || (status_ == ErrorTimeout));
    }

    /**
     * Shortcut to quickly check whether the call was successful.
     */
    bool isSuccessful() const { return status_ == Success; }

    Status getStatus() const { return status_; }

    ServiceCallID getCallID() const { return call_id_; }

    const ResponseFieldType& getResponse() const { return response_; }
    ResponseFieldType& getResponse() { return response_; }
};

/**
 * This operator neatly prints the service call result prepended with extra data like Server Node ID.
 * The extra data will be represented as YAML comment.
 */
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
{
    s << "# Service call result [" << DataType::getDataTypeFullName() << "] "
      << (scr.isSuccessful() ? "OK" : "FAILURE")
      << " server_node_id=" << int(scr.getCallID().server_node_id.get())
      << " tid=" << int(scr.getCallID().transfer_id.get()) << "\n";
    if (scr.isSuccessful())
    {
        s << scr.getResponse();
    }
    else
    {
        s << "# (no data)";
    }
    return s;
}

/**
 * Do not use directly.
 */
class ServiceClientBase : public ITransferAcceptanceFilter, Noncopyable
{
    const DataTypeDescriptor* data_type_descriptor_;  ///< This will be initialized at the time of first call

protected:
    class CallState : DeadlineHandler
    {
        ServiceClientBase& owner_;
        const ServiceCallID id_;

        virtual void handleDeadline(MonotonicTime);

    public:
        CallState(INode& node, ServiceClientBase& owner, ServiceCallID call_id)
            : DeadlineHandler(node.getScheduler())
            , owner_(owner)
            , id_(call_id)
        {
            UAVCAN_ASSERT(id_.isValid());
            DeadlineHandler::startWithDelay(owner_.request_timeout_);
        }

        bool doesMatch(ServiceCallID call_id) const { return call_id == id_; }

        bool operator==(const CallState& rhs) const
        {
            return (&owner_ == &rhs.owner_) && (id_ == rhs.id_);
        }
    };

    struct CallStateMatchingPredicate
    {
        const ServiceCallID id;
        CallStateMatchingPredicate(ServiceCallID reference) : id(reference) { }
        bool operator()(const CallState& state) const { return state.doesMatch(id); }
    };

    MonotonicDuration request_timeout_;

    ServiceClientBase()
        : data_type_descriptor_(NULL)
        , request_timeout_(getDefaultRequestTimeout())
    { }

    virtual ~ServiceClientBase() { }

    int prepareToCall(INode& node, const char* dtname, NodeID server_node_id, ServiceCallID& out_call_id);

    virtual void handleTimeout(ServiceCallID call_id) = 0;

public:
    /**
     * It's not recommended to override default timeouts.
     * Change of this value will not affect pending calls.
     */
    static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(500); }
    static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
    static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
};

/**
 * Use this class to invoke services on remote nodes.
 *
 * @tparam DataType_        Service data type.
 *
 * @tparam Callback_        Service response will be delivered through the callback of this type.
 *                          In C++11 mode this type defaults to std::function<>.
 *                          In C++03 mode this type defaults to a plain function pointer; use binder to
 *                          call member functions as callbacks.
 */
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
          typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
#else
          typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
#endif
          unsigned NumStaticCalls_ = 1
          >
class UAVCAN_EXPORT ServiceClient
    : public GenericSubscriber<DataType_,
                               typename DataType_::Response,
                               typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
                                                                                NumStaticCalls_>::Type>
    , public ServiceClientBase
{
public:
    typedef DataType_ DataType;
    typedef typename DataType::Request RequestType;
    typedef typename DataType::Response ResponseType;
    typedef ServiceCallResult<DataType> ServiceCallResultType;
    typedef Callback_ Callback;

    enum { NumStaticCalls = NumStaticCalls_ };

private:
    typedef ServiceClient<DataType, Callback> SelfType;
    typedef GenericPublisher<DataType, RequestType> PublisherType;
    typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
            TransferListenerType;
    typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;

#if 0
    typedef Multiset<CallState, NumStaticCalls> CallRegistry;
    CallRegistry call_registry_;
#endif

    PublisherType publisher_;
    Callback callback_;

    virtual bool shouldAcceptFrame(const RxFrame& frame) const; // Called from the transfer listener

    void invokeCallback(ServiceCallResultType& result);

    virtual void handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response);

    virtual void handleTimeout(ServiceCallID call_id);

    int addCallState(ServiceCallID call_id);

public:
    /**
     * @param node      Node instance this client will be registered with.
     * @param callback  Callback instance. Optional, can be assigned later.
     */
    explicit ServiceClient(INode& node, const Callback& callback = Callback())
        : SubscriberType(node)
        , publisher_(node, getDefaultRequestTimeout())
        , callback_(callback)
    {
        setRequestTimeout(getDefaultRequestTimeout());
#if UAVCAN_DEBUG
        UAVCAN_ASSERT(getRequestTimeout() == getDefaultRequestTimeout());  // Making sure default values are OK
#endif
    }

    virtual ~ServiceClient() { cancelAll(); }

    /**
     * Shall be called before first use.
     * Returns negative error code.
     */
    int init()
    {
        return publisher_.init();
    }

    /**
     * Performs non-blocking service call.
     * This method transmits the service request and returns immediately.
     *
     * Service response will be delivered into the application via the callback.
     * Note that the callback will ALWAYS be called even if the service call has timed out; the
     * actual result of the call (success/failure) will be passed to the callback as well.
     *
     * Returns negative error code.
     */
    int call(NodeID server_node_id, const RequestType& request);

    /**
     * Same as plain @ref call() above, but this overload also returns the call ID of the new call.
     */
    int call(NodeID server_node_id, const RequestType& request, ServiceCallID& out_call_id);

    /**
     * Cancels certain call referred via call ID structure.
     */
    void cancel(ServiceCallID call_id);

    /**
     * Cancels all pending calls.
     */
    void cancelAll();

    /**
     * Service response callback must be set prior service call.
     */
    const Callback& getCallback() const { return callback_; }
    void setCallback(const Callback& cb) { callback_ = cb; }

#if 0
    unsigned getNumPendingCalls() const { return call_registry_.getSize(); }
#endif

#if 0
    bool hasPendingCalls() const { return !call_registry_.isEmpty(); }
#else
    bool hasPendingCalls() const { return false; }
#endif

    /**
     * Returns the number of failed attempts to decode received response. Generally, a failed attempt means either:
     * - Transient failure in the transport layer.
     * - Incompatible data types.
     */
    uint32_t getResponseFailureCount() const { return SubscriberType::getFailureCount(); }

    /**
     * Request timeouts.
     * There is no such config as TX timeout - TX timeouts are configured automagically according to request timeouts.
     * Not recommended to change.
     */
    MonotonicDuration getRequestTimeout() const { return request_timeout_; }
    void setRequestTimeout(MonotonicDuration timeout)
    {
        timeout = max(timeout, getMinRequestTimeout());
        timeout = min(timeout, getMaxRequestTimeout());

        publisher_.setTxTimeout(timeout);
        request_timeout_ = max(timeout, publisher_.getTxTimeout());  // No less than TX timeout
    }
};

// ----------------------------------------------------------------------------

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
{
    if (try_implicit_cast<bool>(callback_, true))
    {
        callback_(result);
    }
    else
    {
        handleFatalError("Srv client clbk");
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
{
    UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher

#if 0
    return call_registry_.findFirst(CallStateMatchingPredicate(ServiceCallID(frame.getSrcNodeID(),
                                                                             frame.getTransferID()))) != NULL;
#else
    (void)frame;
    return false;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
    UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);

    ServiceCallID call_id(response.getSrcNodeID(), response.getTransferID());
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::Success, call_id, response);    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleTimeout(ServiceCallID call_id)
{
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, call_id,
                                 SubscriberType::getReceivedStructStorage());    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
{
#if 0
    if (call_registry_.isEmpty())
    {
        const int subscriber_res = SubscriberType::startAsServiceResponseListener();
        if (subscriber_res < 0)
        {
            UAVCAN_TRACE("ServiceClient", "Failed to start the subscriber, error: %i", subscriber_res);
            return subscriber_res;
        }
    }

    if (call_registry_.add(CallState(SubscriberType::getNode(), *this, call_id)) == NULL)
    {
        SubscriberType::stop();
        return -ErrMemory;
    }

    return 0;
#else
    (void)call_id;
    return -ErrNotInited;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id,
                                                               const RequestType& request)
{
   ServiceCallID dummy;
   return call(server_node_id, request, dummy);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id,
                                                               const RequestType& request,
                                                               ServiceCallID& out_call_id)
{
    if (!try_implicit_cast<bool>(callback_, true))
    {
        UAVCAN_TRACE("ServiceClient", "Invalid callback");
        return -ErrInvalidConfiguration;
    }

    /*
     * Common procedures that don't depend on the struct data type
     */
    TransferID transfer_id;
    const int prep_res =
        prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);
    if (prep_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
        return prep_res;
    }

    /*
     * Initializing the call state - this will start the subscriber ad-hoc
     */
    const int call_state_res = addCallState(out_call_id);
    if (call_state_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to add call state, error: %i", call_state_res);
        return call_state_res;
    }

    /*
     * Configuring the listener so it will accept only the matching responses
     * TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
     */
    TransferListenerType* const tl = SubscriberType::getTransferListener();
    if (tl == NULL)
    {
        UAVCAN_ASSERT(0);  // Must have been created
        cancel(out_call_id);
        return -ErrLogic;
    }
    tl->installAcceptanceFilter(this);

    /*
     * Publishing the request
     */
    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id, transfer_id);
    if (publisher_res < 0)
    {
        cancel(out_call_id);
        return publisher_res;
    }

    return 0;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancel(ServiceCallID call_id)
{
#if 0
    call_registry_.remove(call_id);
    if (call_registry_.isEmpty())
    {
        SubscriberType::stop();
    }
#else
    (void)call_id;
#endif
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAll()
{
#if 0
    call_registry_.removeAll();
#endif
    SubscriberType::stop();
}

}

#endif // UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Jonas Weber (mail@jonasw.de)
 * 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.
 * 3. Neither the name of the Author 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 AUTHOR 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 AUTHOR
 * 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.
 */

// xmlwrapp includes
#include "xmlwrapp/xpath.h"
#include "xmlwrapp/document.h"
#include "xmlwrapp/node.h"
#include "xmlwrapp/errors.h"

// libxml includes
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <iostream>

namespace xml {
    namespace xpath {
        bool XMLWRAPP_API operator==(const xml::xpath::node_set::iterator& l, const xml::xpath::node_set::iterator& r)
        {
            return ((l.pos == r.pos) && (l.data == r.data));
        }

        bool XMLWRAPP_API operator!=(const xml::xpath::node_set::iterator& l, const xml::xpath::node_set::iterator& r)
        {
            return ((l.pos != r.pos) || (l.data != r.data));
        }}

        namespace impl
        {
            struct xpitimpl
            {
                xml::node node;
                xpitimpl() : node(0) { };
                void set_data(xmlNodePtr n) { node.set_node_data(n); };
                xml::node& get() { return node; }
            };
        }

        namespace xpath
        {

            //------------------------
            // node_set
            //------------------------
            //[data] is xmlXPathObjectPtr
            node_set::node_set(void* data) : data(data)
            {
            }

            node_set::~node_set()
            {
                if (data != NULL)
                    xmlXPathFreeObject(static_cast<xmlXPathObjectPtr>(data));
            }

            int node_set::count() const
            {
                if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                    return static_cast<xmlXPathObjectPtr>(data)->nodesetval->nodeNr;
                else
                    return 0;
            }

            bool node_set::empty() const
            {
                return (count() == 0); // TODO: best way?
            }

            node_set::iterator node_set::begin()
            {
		if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                {
                    xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                    return node_set::iterator(nset, 0);
                } else {
                    return node_set::iterator(NULL, 0);
                }
            }

            node_set::iterator node_set::end()
            {
                if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                {
                    xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                    if (nset == 0)
                    {
                        return iterator(nset, 0);
                    } else {
                        return iterator(nset, nset->nodeNr);
                    }
                } else {
                    return iterator(NULL, 0);
                }
            }

	    bool node_set::contains(const xml::node& n) const
	    {
                xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                xmlNodePtr nodeptr = static_cast<xmlNodePtr>(const_cast<xml::node&>(n).get_node_data());

                return (xmlXPathNodeSetContains(nset, nodeptr) == 1);
            }

            //-----------------------
            //node_set::iterator
            //----------------------

            node_set::iterator::iterator (void* data, int pos) : data(data), pos(pos)
            {
                pimpl_ = new xml::impl::xpitimpl();
            }

            node_set::iterator& node_set::iterator::operator++()
            {
                if (data != NULL && pos < static_cast<xmlNodeSetPtr>(data)->nodeNr)
                    ++pos;

                return *this;
            }

            node_set::iterator node_set::iterator::operator++(int)
            {
                node_set::iterator tmp = *this;
                ++(*this);
                return tmp;
            }
            xml::node& xml::xpath::node_set::iterator::operator*()
            {
                xmlNodeSetPtr nodeset = static_cast<xmlNodeSetPtr>(data);
                if (data == NULL || nodeset->nodeNr == pos) throw xml::exception("dereferencing end");
                pimpl_->set_data((nodeset->nodeTab)[pos]);
                return pimpl_->get();
            }

            xml::node* xml::xpath::node_set::iterator::operator->()
            {
                xmlNodeSetPtr nodeset = static_cast<xmlNodeSetPtr>(data);
                if (data == NULL || nodeset->nodeNr == pos) throw xml::exception("dereferencing end");
                pimpl_->set_data((nodeset->nodeTab)[pos]);
                return &(pimpl_->get());
            }

            node_set::iterator::~iterator()
            {
                delete pimpl_;
            }

            node_set::iterator& node_set::iterator::operator=(const iterator& i)
            {
                data = i.data;
                pos = i.pos;
                return *this;
            }

            node_set::iterator::iterator(const node_set::iterator& i) : data(i.data), pos(i.pos), pimpl_(new impl::xpitimpl)
            {
            }

            //-----------------
            // xpath::context
            //-----------------

            context::context(const xml::document& doc)
            {
                ctxtptr = xmlXPathNewContext(static_cast<xmlDocPtr>(doc.get_doc_data_read_only()));
            }

            context::~context()
            {
                xmlXPathFreeContext(static_cast<xmlXPathContextPtr>(ctxtptr));
            }

            void context::register_namespace(const char* prefix, const char* href)
            {
                xmlXPathRegisterNs(static_cast<xmlXPathContextPtr>(ctxtptr),
                        reinterpret_cast<const xmlChar*>(prefix),
                        reinterpret_cast<const xmlChar*>(href));
            }

            node_set context::evaluate(const char* expr)
            {
                xmlXPathObjectPtr nsptr = xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(expr),
                        static_cast<xmlXPathContextPtr>(ctxtptr));
                return node_set(nsptr);
            }

        }
}
<commit_msg>Use xmlXPathEval instead of xmlXPathEvalExpression.<commit_after>/*
 * Copyright (C) 2011 Jonas Weber (mail@jonasw.de)
 * 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.
 * 3. Neither the name of the Author 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 AUTHOR 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 AUTHOR
 * 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.
 */

// xmlwrapp includes
#include "xmlwrapp/xpath.h"
#include "xmlwrapp/document.h"
#include "xmlwrapp/node.h"
#include "xmlwrapp/errors.h"

// libxml includes
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <iostream>

namespace xml {
    namespace xpath {
        bool XMLWRAPP_API operator==(const xml::xpath::node_set::iterator& l, const xml::xpath::node_set::iterator& r)
        {
            return ((l.pos == r.pos) && (l.data == r.data));
        }

        bool XMLWRAPP_API operator!=(const xml::xpath::node_set::iterator& l, const xml::xpath::node_set::iterator& r)
        {
            return ((l.pos != r.pos) || (l.data != r.data));
        }}

        namespace impl
        {
            struct xpitimpl
            {
                xml::node node;
                xpitimpl() : node(0) { };
                void set_data(xmlNodePtr n) { node.set_node_data(n); };
                xml::node& get() { return node; }
            };
        }

        namespace xpath
        {

            //------------------------
            // node_set
            //------------------------
            //[data] is xmlXPathObjectPtr
            node_set::node_set(void* data) : data(data)
            {
            }

            node_set::~node_set()
            {
                if (data != NULL)
                    xmlXPathFreeObject(static_cast<xmlXPathObjectPtr>(data));
            }

            int node_set::count() const
            {
                if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                    return static_cast<xmlXPathObjectPtr>(data)->nodesetval->nodeNr;
                else
                    return 0;
            }

            bool node_set::empty() const
            {
                return (count() == 0); // TODO: best way?
            }

            node_set::iterator node_set::begin()
            {
		if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                {
                    xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                    return node_set::iterator(nset, 0);
                } else {
                    return node_set::iterator(NULL, 0);
                }
            }

            node_set::iterator node_set::end()
            {
                if (data != NULL && static_cast<xmlXPathObjectPtr>(data)->nodesetval != NULL)
                {
                    xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                    if (nset == 0)
                    {
                        return iterator(nset, 0);
                    } else {
                        return iterator(nset, nset->nodeNr);
                    }
                } else {
                    return iterator(NULL, 0);
                }
            }

	    bool node_set::contains(const xml::node& n) const
	    {
                xmlNodeSetPtr nset = static_cast<xmlXPathObjectPtr>(data)->nodesetval;
                xmlNodePtr nodeptr = static_cast<xmlNodePtr>(const_cast<xml::node&>(n).get_node_data());

                return (xmlXPathNodeSetContains(nset, nodeptr) == 1);
            }

            //-----------------------
            //node_set::iterator
            //----------------------

            node_set::iterator::iterator (void* data, int pos) : data(data), pos(pos)
            {
                pimpl_ = new xml::impl::xpitimpl();
            }

            node_set::iterator& node_set::iterator::operator++()
            {
                if (data != NULL && pos < static_cast<xmlNodeSetPtr>(data)->nodeNr)
                    ++pos;

                return *this;
            }

            node_set::iterator node_set::iterator::operator++(int)
            {
                node_set::iterator tmp = *this;
                ++(*this);
                return tmp;
            }
            xml::node& xml::xpath::node_set::iterator::operator*()
            {
                xmlNodeSetPtr nodeset = static_cast<xmlNodeSetPtr>(data);
                if (data == NULL || nodeset->nodeNr == pos) throw xml::exception("dereferencing end");
                pimpl_->set_data((nodeset->nodeTab)[pos]);
                return pimpl_->get();
            }

            xml::node* xml::xpath::node_set::iterator::operator->()
            {
                xmlNodeSetPtr nodeset = static_cast<xmlNodeSetPtr>(data);
                if (data == NULL || nodeset->nodeNr == pos) throw xml::exception("dereferencing end");
                pimpl_->set_data((nodeset->nodeTab)[pos]);
                return &(pimpl_->get());
            }

            node_set::iterator::~iterator()
            {
                delete pimpl_;
            }

            node_set::iterator& node_set::iterator::operator=(const iterator& i)
            {
                data = i.data;
                pos = i.pos;
                return *this;
            }

            node_set::iterator::iterator(const node_set::iterator& i) : data(i.data), pos(i.pos), pimpl_(new impl::xpitimpl)
            {
            }

            //-----------------
            // xpath::context
            //-----------------

            context::context(const xml::document& doc)
            {
                ctxtptr = xmlXPathNewContext(static_cast<xmlDocPtr>(doc.get_doc_data_read_only()));
            }

            context::~context()
            {
                xmlXPathFreeContext(static_cast<xmlXPathContextPtr>(ctxtptr));
            }

            void context::register_namespace(const char* prefix, const char* href)
            {
                xmlXPathRegisterNs(static_cast<xmlXPathContextPtr>(ctxtptr),
                        reinterpret_cast<const xmlChar*>(prefix),
                        reinterpret_cast<const xmlChar*>(href));
            }

            node_set context::evaluate(const char* expr)
            {
                xmlXPathObjectPtr nsptr = xmlXPathEval(reinterpret_cast<const xmlChar*>(expr),
                        static_cast<xmlXPathContextPtr>(ctxtptr));
                return node_set(nsptr);
            }

        }
}
<|endoftext|>


<commit_before>//
//  ZX8081.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 08/06/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#include "ZX8081.hpp"

using namespace Storage::Data::ZX8081;

static uint16_t short_at(std::size_t address, const std::vector<uint8_t> &data) {
	return static_cast<uint16_t>(data[address] | (data[address + 1] << 8));
}

static std::shared_ptr<File> ZX80FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX80 file?

	if(data.size() < 0x28) return nullptr;

//	uint16_t next_line_number = short_at(0x2, data);
//	uint16_t first_visible_line = short_at(0x13, data);

	uint16_t vars = short_at(0x8, data);
	uint16_t end_of_file = short_at(0xa, data);
	uint16_t display_address = short_at(0xc, data);

	// check that the end of file is contained within the supplied data
	if(static_cast<size_t>(end_of_file - 0x4000) > data.size()) return nullptr;

	// check for the proper ordering of buffers
	if(vars > end_of_file) return nullptr;
	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->data = data;
	file->isZX81 = false;
	return file;
}

static std::shared_ptr<File> ZX81FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX81 file?

	// Look for a file name.
	std::size_t data_pointer = 0;
	std::vector<uint8_t> name_data;
	std::size_t c = 11;
	while(c < data.size() && c--) {
		name_data.push_back(data[data_pointer] & 0x3f);
		if(data[data_pointer] & 0x80) break;
		data_pointer++;
	}
	if(!c) return nullptr;
	data_pointer++;

	if(data.size() < data_pointer + 0x405e - 0x4009) return nullptr;

//	if(data[data_pointer]) return nullptr;

	uint16_t vars = short_at(data_pointer + 0x4010 - 0x4009, data);
	uint16_t end_of_file = short_at(data_pointer + 0x4014 - 0x4009, data);
//	uint16_t display_address = short_at(0x400c - 0x4009, data);

	// check that the end of file is contained within the supplied data
	if(data_pointer + end_of_file - 0x4009 > data.size()) return nullptr;

	// check for the proper ordering of buffers
	if(vars > end_of_file) return nullptr;
//	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->name = StringFromData(name_data, true);
	file->data = data;
	file->isZX81 = true;
	return file;
}

std::shared_ptr<File> Storage::Data::ZX8081::FileFromData(const std::vector<uint8_t> &data) {
	std::shared_ptr<Storage::Data::ZX8081::File> result = ZX81FileFromData(data);
	if(result) return result;
	return ZX80FileFromData(data);
}

// MARK: - String conversion

std::wstring Storage::Data::ZX8081::StringFromData(const std::vector<uint8_t> &data, bool is_zx81) {
	std::wstring string;

	wchar_t zx80_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'>',		'<',		'=',		'+',		'-',		'*',		'/',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	// TODO: the block character conversions shown here are in the wrong order
	wchar_t zx81_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'-',		'+',		'*',		'/',		'=',		'>',		'<',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	wchar_t *map = is_zx81 ? zx81_map : zx80_map;

	for(uint8_t byte : data) {
		string.push_back(map[byte & 0x3f]);
	}

	return string;
}

std::vector<uint8_t> Storage::Data::ZX8081::DataFromString(const std::wstring &string, bool is_zx81) {
	std::vector<uint8_t> data;

	// TODO

	return data;
}
<commit_msg>Eliminates `vars` test.<commit_after>//
//  ZX8081.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 08/06/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#include "ZX8081.hpp"

using namespace Storage::Data::ZX8081;

static uint16_t short_at(std::size_t address, const std::vector<uint8_t> &data) {
	return static_cast<uint16_t>(data[address] | (data[address + 1] << 8));
}

static std::shared_ptr<File> ZX80FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX80 file?

	if(data.size() < 0x28) return nullptr;

//	uint16_t next_line_number = short_at(0x2, data);
//	uint16_t first_visible_line = short_at(0x13, data);

	uint16_t vars = short_at(0x8, data);
	uint16_t end_of_file = short_at(0xa, data);
	uint16_t display_address = short_at(0xc, data);

	// check that the end of file is contained within the supplied data
	if(static_cast<size_t>(end_of_file - 0x4000) > data.size()) return nullptr;

	// check for the proper ordering of buffers
	if(vars > end_of_file) return nullptr;
	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->data = data;
	file->isZX81 = false;
	return file;
}

static std::shared_ptr<File> ZX81FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX81 file?

	// Look for a file name.
	std::size_t data_pointer = 0;
	std::vector<uint8_t> name_data;
	std::size_t c = 11;
	while(c < data.size() && c--) {
		name_data.push_back(data[data_pointer] & 0x3f);
		if(data[data_pointer] & 0x80) break;
		data_pointer++;
	}
	if(!c) return nullptr;
	data_pointer++;

	if(data.size() < data_pointer + 0x405e - 0x4009) return nullptr;

//	if(data[data_pointer]) return nullptr;

	uint16_t vars = short_at(data_pointer + 0x4010 - 0x4009, data);
	uint16_t end_of_file = short_at(data_pointer + 0x4014 - 0x4009, data);
//	uint16_t display_address = short_at(0x400c - 0x4009, data);

	// check that the end of file is contained within the supplied data
	if(data_pointer + end_of_file - 0x4009 > data.size()) return nullptr;

	// check for the proper ordering of buffers
//	if(vars > end_of_file) return nullptr;
//	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->name = StringFromData(name_data, true);
	file->data = data;
	file->isZX81 = true;
	return file;
}

std::shared_ptr<File> Storage::Data::ZX8081::FileFromData(const std::vector<uint8_t> &data) {
	std::shared_ptr<Storage::Data::ZX8081::File> result = ZX81FileFromData(data);
	if(result) return result;
	return ZX80FileFromData(data);
}

// MARK: - String conversion

std::wstring Storage::Data::ZX8081::StringFromData(const std::vector<uint8_t> &data, bool is_zx81) {
	std::wstring string;

	wchar_t zx80_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'>',		'<',		'=',		'+',		'-',		'*',		'/',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	// TODO: the block character conversions shown here are in the wrong order
	wchar_t zx81_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'-',		'+',		'*',		'/',		'=',		'>',		'<',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	wchar_t *map = is_zx81 ? zx81_map : zx80_map;

	for(uint8_t byte : data) {
		string.push_back(map[byte & 0x3f]);
	}

	return string;
}

std::vector<uint8_t> Storage::Data::ZX8081::DataFromString(const std::wstring &string, bool is_zx81) {
	std::vector<uint8_t> data;

	// TODO

	return data;
}
<|endoftext|>


<commit_before>#include "javaHandler.h"

#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include <QtCore/QDebug>

#include "../../kernel/definitions.h"
#include "../../../qrrepo/repoApi.h"

using namespace qReal;
using namespace generators;

JavaHandler::JavaHandler(qrRepo::RepoApi const &api)
    : mApi(api)
{
}

QString JavaHandler::generateToJava(QString const &pathToDir)
{
    mErrorText = "";
    this->pathToDir = pathToDir;

    Id repoId = ROOT_ID;

    IdList rootDiagrams = mApi.children(repoId);

    foreach (Id const typeDiagram, rootDiagrams) {
        serializeChildren(typeDiagram);
    }

    qDebug() << "Done.";
    return mErrorText;
}

QString JavaHandler::serializeChildren(Id const &idParent)
{
    QString result = "";
    IdList childElems = mApi.children(idParent);

    foreach (Id const id, childElems) {
        result += serializeObject(id);
    }

    return result;
}

QString JavaHandler::serializeObject(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    // class diagramm

    if ((objectType == "Class_Diagram_Class")|(objectType == "Class_Diagram_Interface")) {

        //-----------
        QString const pathToFile = pathToDir + "/" + mApi.name(id) + ".java";

        QFile file(pathToFile);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return "";

        QTextStream out(&file);
        //-----------

        QString visibility = getVisibility(id);
        QString multiplicity = getMultiplicity(id);
        QString isFinalField = hasModifier(id, "final");
        QString isAbstractField = hasModifier(id, "abstract");
        QString parents = getSuperclass(id);

        if (isAbstractField == "abstract " && isFinalField == "final ") {
            addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract final\" declaration doesn't make sense");
        }

        out << isAbstractField + isFinalField + visibility + "class " + mApi.name(id) + parents +  " {" + "\n";
        out << serializeChildren(id);

        if (!mApi.links(id).isEmpty()) {
            // search for the Class-typed attrbutes
            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                if (aLink.element() == "Class_Diagram_Directed_Association" || aLink.element() == "Class_Diagram_Association") {
                    QString type = mApi.name(mApi.otherEntityFromLink(aLink, id)) + " ";
                    QString visibility = getVisibility(aLink);
                    QString isFinalField = hasModifier(aLink, "final");
                    QString isStaticField = hasModifier(aLink, "static");
                    QString isVolatileField = hasModifier(aLink, "volatile");
                    QString isTransientField = hasModifier(aLink, "transient");

                    if (isVolatileField == "volatile " && isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + aLink.toString() + ". \"final volatile\" declaration doesn't make sense");
                    }

                    out << isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(aLink);
                    out << ";\n";
                }
            }

            //search for bidirectional assocciation
            IdList linksIn = mApi.incomingLinks(id);
            foreach (Id const aLink, linksIn) {
                if (aLink.element() == "Class_Diagram_Association") {
                    QString type = mApi.name(mApi.otherEntityFromLink(aLink, id)) + " ";
                    QString visibility = getVisibility(aLink);
                    QString isFinalField = hasModifier(aLink, "final");
                    QString isStaticField = hasModifier(aLink, "static");
                    QString isVolatileField = hasModifier(aLink, "volatile");
                    QString isTransientField = hasModifier(aLink, "transient");

                    if (isVolatileField == "volatile " && isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + aLink.toString() + ". \"final volatile\" declaration doesn't make sense");
                    }

                    out << isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(aLink);
                    out << ";\n";
                }
            }
        }

        out << "}\n";
    } else if (objectType == "Class_Diagram_View") {
        //	    to do someting
    } else if (objectType == "Class_Diagram_Class_method") {
        IdList parents = mApi.parents(id);
        if (!parents.isEmpty()) {
            Id parentId = parents.at(0);
            QString const parentType = mApi.typeName(parentId);

            if (parentType == "Class_Diagram_Class") {
                QString visibility = getVisibility(id);
                QString type = getType(id);
                QString operationFactors = getOperationFactors(id);
                QString isFinalField = hasModifier(id, "final");
                QString isAbstractField = hasModifier(id, "abstract");
                QString isStaticField = hasModifier(id, "static");
                QString isSynchronizedField = hasModifier(id, "synchronized");
                QString isNativeField = hasModifier(id, "native");

                if (isAbstractField == "abstract ") {
                    if (isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract final\" declaration doesn't make sense");
                    } else if (isStaticField == "static ") {
                        addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract static\" declaration doesn't make sense");
                    }
                }

                QString methodBody = getMethodCode(id);

                result += isAbstractField + isFinalField + isStaticField + isSynchronizedField + isNativeField +
                          visibility + type  + mApi.name(id) + "(" + operationFactors + ")" + methodBody + "\n";
            } else {
                this->addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Move it inside some Class");
            }
        }
    } else if (objectType == "Class_Diagram_Class_field") {
        IdList parents = mApi.parents(id);
        if (!parents.isEmpty()) {
            Id parentId = parents.at(0);
            QString const parentType = mApi.typeName(parentId);

            if (parentType == "Class_Diagram_Class") {
                QString visibility = getVisibility(id);
                QString type = getType(id);
                QString defaultValue = getDefaultValue(id);
                QString isFinalField = hasModifier(id, "final");
                QString isStaticField = hasModifier(id, "static");
                QString isVolatileField = hasModifier(id, "volatile");
                QString isTransientField = hasModifier(id, "transient");

                if (isVolatileField == "volatile " && isFinalField == "final ") {
                    addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"final volatile\" declaration doesn't make sense");
                }
                result += isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(id);
                if (defaultValue != "") {
                    result += " " + defaultValue;
                }
                result += ";\n";
            } else {
                addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Move it inside some Class");
            }
        }
    }

    // activity diagram

    else if (objectType == "Activity_Diagram_Initial_Node") {
        if (!mApi.links(id).isEmpty()) {
           if (!mApi.incomingLinks(id).isEmpty()) {
              addError("object " + objectType + " with id  " + id.toString() + " can not have incoming links");
           }

            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                //imagine, that Initial Node has just one outcoming link
                if (aLink.element() == "Activity_Diagram_Control_Flow") {
                    Id toConsider = mApi.otherEntityFromLink(aLink, id);
                    result += serializeObject(toConsider);
                } else {
                    addError("object " + objectType + " with id  " + id.toString() + " can not have not Control Flow link as outcoming link.");
                }
            }
        }
    } else if (objectType == "Activity_Diagram_Action") {
        result += mApi.name(id) + "();" + "\n";
        if (!mApi.links(id).isEmpty()) {
            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                if (aLink.element() == "Activity_Diagram_Control_Flow") {
                    Id toConsider = mApi.otherEntityFromLink(aLink, id);
                    result += serializeObject(toConsider);
                }
            }
        }
    } else if (objectType == "Activity_Diagram_Activity_Final_Node") {
        result += "}\n";
    } else if (objectType == "Activity_Diagram_Decision_Node") {
        result += "if (";
        if (!mApi.links(id).isEmpty()) {
            IdList linksOut = mApi.outgoingLinks(id);
            if (!linksOut.isEmpty()) {
                foreach (Id const aLink, linksOut) {
                    if (aLink.element() == "Activity_Diagram_Control_Flow") {
                        Id toConsider = mApi.otherEntityFromLink(aLink, id);
                        result += serializeObject(toConsider);
                    }
                }
            } else {
                addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Is must have at least one outgoing edge.");
            }
        }
    } else if (objectType == "Activity_Diagram_Merge_Node") {
        result += "}\n";
    }

    return result;
}

QString JavaHandler::getVisibility(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "visibility")) {
        QString visibility = mApi.stringProperty(id, "visibility");

        if (visibility == "+")
            visibility = "public";
        else if (visibility == "-")
            visibility = "private";
        else if (visibility == "#")
            visibility = "protected";
        else if (visibility == "~")
            visibility = "";

        if (isVisibilitySuitable(visibility)) {
            result = visibility;
            if (result != "")
                result += " ";
        } else {
            addError("object " + objectType + " with id  " + id.toString() + " has invalid visibility property: " + visibility);
        }
    }

    return result;
}

QString JavaHandler::getMultiplicity(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "multiplicity")) {
        QString multiplicity = mApi.stringProperty(id, "multiplicity");
    }

    return result;
}

QString JavaHandler::getType(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "type")) {
        QString type = mApi.stringProperty(id, "type");

        if (isTypeSuitable(type) || (objectType == "Class_Diagram_Class_method" && type == "void")) {
            result = type;
            if (result != "")
                result += " ";
        } else {
            addError("object " + objectType + " with id " + id.toString() + " has invalid type: " + type);
        }
    }

    return result;
}

QString JavaHandler::getSuperclass(Id const &id)
{
    QString result = "";
    bool hasParentClass = false;

    QString const objectType = mApi.typeName(id);

    if (!mApi.links(id).isEmpty()) {
        IdList links = mApi.outgoingLinks(id);

        foreach (Id const aLink, links) {
            if (aLink.element() == "Class_Diagram_Generalization") {
                if (hasParentClass == false) {
                    hasParentClass = true;
                    if (id == mApi.otherEntityFromLink(aLink, id)) {
                        addError("object " + objectType + " with id " + id.toString() + " can not be his own superclass");
                    } else {
                        QString parentClass = mApi.name(mApi.otherEntityFromLink(aLink, id));
                        result += " extends " + parentClass;
                    }
                } else {
                    addError("object " + objectType + " with id " + id.toString() + " has too much superclasses");
                }
            }
        }

    }

    return result;
}

QString JavaHandler::getMethodCode(Id const &id)
{
    QString result = "{}";

    QString const objectType = mApi.typeName(id);

    if (!mApi.outgoingConnections(id).isEmpty()) {
        IdList outgoingConnections = mApi.outgoingConnections(id);

        if (outgoingConnections.length() == 1) {
            //there is a big hope here that the user will connect just Activity Diagrams
            if (outgoingConnections.at(0).element() == "Kernel_Diagram") {
                result = serializeObject(outgoingConnections.at(0));
            } else {
                addError("object " + objectType + " with id " + id.toString() + ". Only Activity Diagram can be its realization.");
            }
        } else {
            addError("object " + objectType + " with id " + id.toString() + " has too much realizations");
        }

    } else {
        addError("Method " + objectType + " with id " + id.toString() + " will be empty.");
    }

    return result;
}

QString JavaHandler::hasModifier(Id const &id, QString const &modifier)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    QString isModifier = "";

    if (modifier == "final") {
        isModifier = "isLeaf";
    } else {
        isModifier = "is" + modifier.left(1).toUpper() + modifier.mid(1, modifier.length());
    }

    if (mApi.hasProperty(id, isModifier)) {
        QString hasModifier = mApi.stringProperty(id, isModifier);

        if (hasModifier == "true") {
            result = modifier + " ";
        }else if (hasModifier != "false" && hasModifier != "") {
            addError("object " + objectType + " with id " + id.toString() + " has invalid " + isModifier + " value: " + hasModifier);
        }
    }

    return result;
}

QString JavaHandler::getOperationFactors(Id const &id)
{
    QString result = "";

    if (mApi.hasProperty(id, "type")) {
        QString operationFactors = mApi.stringProperty(id, "operationFactors");

        //	to check for the correct data
        //	if (isTypeSuitable(type) || (objectType == "cnClassMethod" && type == "void")) {
        result = operationFactors;
        //	} else {
        //		addError("object " + objectType + " with id " + id.toString() + " has invalid type: " + type);
        //    	}
    }

    return result;
}

QString JavaHandler::getDefaultValue(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "defaultValue")) {
        QString defaultValue = mApi.stringProperty(id, "defaultValue");

        //	if (isTypeSuitable(defaultValue)) {
        //	to check for the corract data
        result = defaultValue;
        //	} else {
        //		addError("object " + objectType + " with id " + id.toString() + " has invalid default value: " + defaultValue);
        //    	}
    }

    return result;
}

bool JavaHandler::isTypeSuitable(QString const &type) const
{
    //  todo: check class-type for correctness
    return type == "int" || type == "float" || type == "double" || type == "boolean"
            || type == "char" || type == "byte" || type == "long" || type == "short";
}

bool JavaHandler::isVisibilitySuitable(QString const &visibility) const
{
    return visibility == "public" || visibility == "private" || visibility == "protected" || visibility == "";
}

void JavaHandler::addError(QString const &errorText)
{
    mErrorText += errorText + "\n";
}
<commit_msg>too _many_<commit_after>#include "javaHandler.h"

#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include <QtCore/QDebug>

#include "../../kernel/definitions.h"
#include "../../../qrrepo/repoApi.h"

using namespace qReal;
using namespace generators;

JavaHandler::JavaHandler(qrRepo::RepoApi const &api)
    : mApi(api)
{
}

QString JavaHandler::generateToJava(QString const &pathToDir)
{
    mErrorText = "";
    this->pathToDir = pathToDir;

    Id repoId = ROOT_ID;

    IdList rootDiagrams = mApi.children(repoId);

    foreach (Id const typeDiagram, rootDiagrams) {
        serializeChildren(typeDiagram);
    }

    qDebug() << "Done.";
    return mErrorText;
}

QString JavaHandler::serializeChildren(Id const &idParent)
{
    QString result = "";
    IdList childElems = mApi.children(idParent);

    foreach (Id const id, childElems) {
        result += serializeObject(id);
    }

    return result;
}

QString JavaHandler::serializeObject(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    // class diagramm

    if ((objectType == "Class_Diagram_Class")|(objectType == "Class_Diagram_Interface")) {

        //-----------
        QString const pathToFile = pathToDir + "/" + mApi.name(id) + ".java";

        QFile file(pathToFile);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return "";

        QTextStream out(&file);
        //-----------

        QString visibility = getVisibility(id);
        QString multiplicity = getMultiplicity(id);
        QString isFinalField = hasModifier(id, "final");
        QString isAbstractField = hasModifier(id, "abstract");
        QString parents = getSuperclass(id);

        if (isAbstractField == "abstract " && isFinalField == "final ") {
            addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract final\" declaration doesn't make sense");
        }

        out << isAbstractField + isFinalField + visibility + "class " + mApi.name(id) + parents +  " {" + "\n";
        out << serializeChildren(id);

        if (!mApi.links(id).isEmpty()) {
            // search for the Class-typed attrbutes
            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                if (aLink.element() == "Class_Diagram_Directed_Association" || aLink.element() == "Class_Diagram_Association") {
                    QString type = mApi.name(mApi.otherEntityFromLink(aLink, id)) + " ";
                    QString visibility = getVisibility(aLink);
                    QString isFinalField = hasModifier(aLink, "final");
                    QString isStaticField = hasModifier(aLink, "static");
                    QString isVolatileField = hasModifier(aLink, "volatile");
                    QString isTransientField = hasModifier(aLink, "transient");

                    if (isVolatileField == "volatile " && isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + aLink.toString() + ". \"final volatile\" declaration doesn't make sense");
                    }

                    out << isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(aLink);
                    out << ";\n";
                }
            }

            //search for bidirectional assocciation
            IdList linksIn = mApi.incomingLinks(id);
            foreach (Id const aLink, linksIn) {
                if (aLink.element() == "Class_Diagram_Association") {
                    QString type = mApi.name(mApi.otherEntityFromLink(aLink, id)) + " ";
                    QString visibility = getVisibility(aLink);
                    QString isFinalField = hasModifier(aLink, "final");
                    QString isStaticField = hasModifier(aLink, "static");
                    QString isVolatileField = hasModifier(aLink, "volatile");
                    QString isTransientField = hasModifier(aLink, "transient");

                    if (isVolatileField == "volatile " && isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + aLink.toString() + ". \"final volatile\" declaration doesn't make sense");
                    }

                    out << isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(aLink);
                    out << ";\n";
                }
            }
        }

        out << "}\n";
    } else if (objectType == "Class_Diagram_View") {
        //	    to do someting
    } else if (objectType == "Class_Diagram_Class_method") {
        IdList parents = mApi.parents(id);
        if (!parents.isEmpty()) {
            Id parentId = parents.at(0);
            QString const parentType = mApi.typeName(parentId);

            if (parentType == "Class_Diagram_Class") {
                QString visibility = getVisibility(id);
                QString type = getType(id);
                QString operationFactors = getOperationFactors(id);
                QString isFinalField = hasModifier(id, "final");
                QString isAbstractField = hasModifier(id, "abstract");
                QString isStaticField = hasModifier(id, "static");
                QString isSynchronizedField = hasModifier(id, "synchronized");
                QString isNativeField = hasModifier(id, "native");

                if (isAbstractField == "abstract ") {
                    if (isFinalField == "final ") {
                        addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract final\" declaration doesn't make sense");
                    } else if (isStaticField == "static ") {
                        addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"abstract static\" declaration doesn't make sense");
                    }
                }

                QString methodBody = getMethodCode(id);

                result += isAbstractField + isFinalField + isStaticField + isSynchronizedField + isNativeField +
                          visibility + type  + mApi.name(id) + "(" + operationFactors + ")" + methodBody + "\n";
            } else {
                this->addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Move it inside some Class");
            }
        }
    } else if (objectType == "Class_Diagram_Class_field") {
        IdList parents = mApi.parents(id);
        if (!parents.isEmpty()) {
            Id parentId = parents.at(0);
            QString const parentType = mApi.typeName(parentId);

            if (parentType == "Class_Diagram_Class") {
                QString visibility = getVisibility(id);
                QString type = getType(id);
                QString defaultValue = getDefaultValue(id);
                QString isFinalField = hasModifier(id, "final");
                QString isStaticField = hasModifier(id, "static");
                QString isVolatileField = hasModifier(id, "volatile");
                QString isTransientField = hasModifier(id, "transient");

                if (isVolatileField == "volatile " && isFinalField == "final ") {
                    addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". \"final volatile\" declaration doesn't make sense");
                }
                result += isFinalField + visibility + isStaticField + isVolatileField + isTransientField + type + mApi.name(id);
                if (defaultValue != "") {
                    result += " " + defaultValue;
                }
                result += ";\n";
            } else {
                addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Move it inside some Class");
            }
        }
    }

    // activity diagram

    else if (objectType == "Activity_Diagram_Initial_Node") {
        if (!mApi.links(id).isEmpty()) {
           if (!mApi.incomingLinks(id).isEmpty()) {
              addError("object " + objectType + " with id  " + id.toString() + " can not have incoming links");
           }

            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                //imagine, that Initial Node has just one outcoming link
                if (aLink.element() == "Activity_Diagram_Control_Flow") {
                    Id toConsider = mApi.otherEntityFromLink(aLink, id);
                    result += serializeObject(toConsider);
                } else {
                    addError("object " + objectType + " with id  " + id.toString() + " can not have not Control Flow link as outcoming link.");
                }
            }
        }
    } else if (objectType == "Activity_Diagram_Action") {
        result += mApi.name(id) + "();" + "\n";
        if (!mApi.links(id).isEmpty()) {
            IdList linksOut = mApi.outgoingLinks(id);
            foreach (Id const aLink, linksOut) {
                if (aLink.element() == "Activity_Diagram_Control_Flow") {
                    Id toConsider = mApi.otherEntityFromLink(aLink, id);
                    result += serializeObject(toConsider);
                }
            }
        }
    } else if (objectType == "Activity_Diagram_Activity_Final_Node") {
        result += "}\n";
    } else if (objectType == "Activity_Diagram_Decision_Node") {
        result += "if (";
        if (!mApi.links(id).isEmpty()) {
            IdList linksOut = mApi.outgoingLinks(id);
            if (!linksOut.isEmpty()) {
                foreach (Id const aLink, linksOut) {
                    if (aLink.element() == "Activity_Diagram_Control_Flow") {
                        Id toConsider = mApi.otherEntityFromLink(aLink, id);
                        result += serializeObject(toConsider);
                    }
                }
            } else {
                addError("unable to serialize object " + objectType + " with id: " + id.toString() + ". Is must have at least one outgoing edge.");
            }
        }
    } else if (objectType == "Activity_Diagram_Merge_Node") {
        result += "}\n";
    }

    return result;
}

QString JavaHandler::getVisibility(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "visibility")) {
        QString visibility = mApi.stringProperty(id, "visibility");

        if (visibility == "+")
            visibility = "public";
        else if (visibility == "-")
            visibility = "private";
        else if (visibility == "#")
            visibility = "protected";
        else if (visibility == "~")
            visibility = "";

        if (isVisibilitySuitable(visibility)) {
            result = visibility;
            if (result != "")
                result += " ";
        } else {
            addError("object " + objectType + " with id  " + id.toString() + " has invalid visibility property: " + visibility);
        }
    }

    return result;
}

QString JavaHandler::getMultiplicity(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "multiplicity")) {
        QString multiplicity = mApi.stringProperty(id, "multiplicity");
    }

    return result;
}

QString JavaHandler::getType(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "type")) {
        QString type = mApi.stringProperty(id, "type");

        if (isTypeSuitable(type) || (objectType == "Class_Diagram_Class_method" && type == "void")) {
            result = type;
            if (result != "")
                result += " ";
        } else {
            addError("object " + objectType + " with id " + id.toString() + " has invalid type: " + type);
        }
    }

    return result;
}

QString JavaHandler::getSuperclass(Id const &id)
{
    QString result = "";
    bool hasParentClass = false;

    QString const objectType = mApi.typeName(id);

    if (!mApi.links(id).isEmpty()) {
        IdList links = mApi.outgoingLinks(id);

        foreach (Id const aLink, links) {
            if (aLink.element() == "Class_Diagram_Generalization") {
                if (hasParentClass == false) {
                    hasParentClass = true;
                    if (id == mApi.otherEntityFromLink(aLink, id)) {
                        addError("object " + objectType + " with id " + id.toString() + " can not be his own superclass");
                    } else {
                        QString parentClass = mApi.name(mApi.otherEntityFromLink(aLink, id));
                        result += " extends " + parentClass;
                    }
                } else {
                    addError("object " + objectType + " with id " + id.toString() + " has too much superclasses");
                }
            }
        }

    }

    return result;
}

QString JavaHandler::getMethodCode(Id const &id)
{
    QString result = "{}";

    QString const objectType = mApi.typeName(id);

    if (!mApi.outgoingConnections(id).isEmpty()) {
        IdList outgoingConnections = mApi.outgoingConnections(id);

        if (outgoingConnections.length() == 1) {
            //there is a big hope here that the user will connect just Activity Diagrams
            if (outgoingConnections.at(0).element() == "Kernel_Diagram") {
                result = serializeChildren(outgoingConnections.at(0));
            } else {
                addError("object " + objectType + " with id " + id.toString() + ". Only Activity Diagram can be its realization.");
            }
        } else {
            addError("object " + objectType + " with id " + id.toString() + " has too many realizations");
        }

    } else {
        addError("Method " + objectType + " with id " + id.toString() + " will be empty.");
    }

    return result;
}

QString JavaHandler::hasModifier(Id const &id, QString const &modifier)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    QString isModifier = "";

    if (modifier == "final") {
        isModifier = "isLeaf";
    } else {
        isModifier = "is" + modifier.left(1).toUpper() + modifier.mid(1, modifier.length());
    }

    if (mApi.hasProperty(id, isModifier)) {
        QString hasModifier = mApi.stringProperty(id, isModifier);

        if (hasModifier == "true") {
            result = modifier + " ";
        }else if (hasModifier != "false" && hasModifier != "") {
            addError("object " + objectType + " with id " + id.toString() + " has invalid " + isModifier + " value: " + hasModifier);
        }
    }

    return result;
}

QString JavaHandler::getOperationFactors(Id const &id)
{
    QString result = "";

    if (mApi.hasProperty(id, "type")) {
        QString operationFactors = mApi.stringProperty(id, "operationFactors");

        //	to check for the correct data
        //	if (isTypeSuitable(type) || (objectType == "cnClassMethod" && type == "void")) {
        result = operationFactors;
        //	} else {
        //		addError("object " + objectType + " with id " + id.toString() + " has invalid type: " + type);
        //    	}
    }

    return result;
}

QString JavaHandler::getDefaultValue(Id const &id)
{
    QString result = "";

    QString const objectType = mApi.typeName(id);

    if (mApi.hasProperty(id, "defaultValue")) {
        QString defaultValue = mApi.stringProperty(id, "defaultValue");

        //	if (isTypeSuitable(defaultValue)) {
        //	to check for the corract data
        result = defaultValue;
        //	} else {
        //		addError("object " + objectType + " with id " + id.toString() + " has invalid default value: " + defaultValue);
        //    	}
    }

    return result;
}

bool JavaHandler::isTypeSuitable(QString const &type) const
{
    //  todo: check class-type for correctness
    return type == "int" || type == "float" || type == "double" || type == "boolean"
            || type == "char" || type == "byte" || type == "long" || type == "short";
}

bool JavaHandler::isVisibilitySuitable(QString const &visibility) const
{
    return visibility == "public" || visibility == "private" || visibility == "protected" || visibility == "";
}

void JavaHandler::addError(QString const &errorText)
{
    mErrorText += errorText + "\n";
}
<|endoftext|>