<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "jvm_callback_op.h"

#include "exception.h"
#include "utilities.h"

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api.h"
#include "tensorflow/c/eager/c_api_internal.h"
#include "tensorflow/core/common_runtime/eager/tensor_handle.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/kernel_def.pb_text.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/lib/core/coding.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"

namespace tensorflow {
REGISTER_OP("JVMCallback")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >=0")
    .SetIsStateful()
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
Invokes a JVM callback function, `f` to compute `f(input)->output`.

This operation is considered stateful. For a stateless version, see
`JVMCallback`.

id: A unique ID representing a registered JVM callback function
  in this address space.
jvm_pointer: A pointer to an existing JVM instance represented as a
  string. This is the JVM that will be used when invoking this JVM
  callback.
registry_pointer: Pointer to the JVM callbacks registry class.
input: List of tensors that will provide input to the op.
output: Output tensors from the op.
Tin: Data types of the inputs to the op.
Tout: Data types of the outputs from the op.
      The length of the list specifies the number of outputs.
)doc");

REGISTER_OP("JVMCallbackStateless")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >= 0")
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
A stateless version of `JVMCallback`.
)doc");

namespace {
  // Given the 'call', prepares the inputs as a JNI long array that is appropriate for calling the registry.
  jlongArray MakeInputs(JVMCall* call) {
    unsigned long n = call->inputs.size();
    jlongArray inputs = call->env->NewLongArray(static_cast<jsize>(n));
    jlong* inputs_array = call->env->GetLongArrayElements(inputs, nullptr);
    for (int64 i = 0; i < n; ++i) {
      const Tensor& t = call->inputs[i];
      TFE_TensorHandle* tensor;
      if (call->gpu) {
        tensor = new TFE_TensorHandle(t, call->device, call->device);
      } else {
        tensor = new TFE_TensorHandle(t, nullptr, nullptr);
      }
      inputs_array[i] = reinterpret_cast<jlong>(tensor);
    }
    call->env->ReleaseLongArrayElements(inputs, inputs_array, 0);
    return inputs;
  }

  // Process the return values by converting them back to TensorFlow tensors and adding them to the call outputs.
  void ProcessOutputs(JVMCall* call, jlongArray call_outputs, TF_Status* status) {
    call->outputs.clear();
    jsize n = call->env->GetArrayLength(call_outputs);
    jlong* outputs_array = call->env->GetLongArrayElements(call_outputs, nullptr);
    for (int i = 0; i < n; ++i) {
      static_assert(sizeof(jlong) >= sizeof(TFE_TensorHandle*), "Cannot package C object pointers as a Java long");
      if (outputs_array[i] == 0) {
        status->status = errors::InvalidArgument("One of the op output tensors has been disposed already.");
        return;
      }
      auto* h = reinterpret_cast<TFE_TensorHandle*>(outputs_array[i]);
      if (h == nullptr) {
        status->status = errors::InvalidArgument("Could not obtain tensor handle to one of the outputs.");
        return;
      }
      if (!status->status.ok()) return;
      const tensorflow::Tensor* t = nullptr;
      status->status = h->handle->Tensor(&t);
      call->outputs.push_back(*t);
    }
    call->env->ReleaseLongArrayElements(call_outputs, outputs_array, 0);
  }

  // Calls the registered JVM function through the registry.
  Status CallJVMFunction(JVMCall* call) {
    // Prepare the call arguments.
    jlongArray call_inputs = MakeInputs(call);

    // Invoke the registry 'call' method.
    if (call->registry != nullptr && call->call_method_id != nullptr) {
      auto outputs = (jlongArray) call->env->CallStaticObjectMethod(
          call->registry, call->call_method_id, call->id, call_inputs);
      jthrowable exc(call->env->ExceptionOccurred());
      if (exc) {
        // Get the exception string representation to use as the error message.
        jclass excObjCls(call->env->GetObjectClass(exc));
        jmethodID toString = call->env->GetMethodID(excObjCls, "toString", "()Ljava/lang/String;");
        jstring excString = (jstring) call->env->CallObjectMethod(exc, toString);
        const char* excCString = call->env->GetStringUTFChars(excString, 0);
        std::string excCppString(excCString);
        tensorflow::StringPiece tf_exc_string(excCppString);
        call->env->ReleaseStringUTFChars(excString, excCString);

        // Get the exception class name and convert it to a TensorFlow error code.
        jclass classCls(call->env->FindClass("java/lang/Class"));
        jmethodID getName(call->env->GetMethodID(classCls, "getName", "()Ljava/lang/String;"));
        jstring clsName(static_cast<jstring>(call->env->CallObjectMethod(excObjCls, getName)));
        const char* clsNameCString = call->env->GetStringUTFChars(clsName, 0);
        std::string clsNameCppString(clsNameCString);
        int error_code = tf_error_code(clsNameCppString);
        call->env->ReleaseStringUTFChars(clsName, clsNameCString);
        call->env->ExceptionClear();
        return tensorflow::Status((tensorflow::error::Code) error_code, tf_exc_string);
      }

      if (outputs == nullptr) {
        return errors::Unknown("Failed to run JVM callback function.");
      }

      // Process the return values and convert them back to TensorFlow tensors.
      auto* status = new TF_Status;
      ProcessOutputs(call, outputs, status);
      return status->status;
    } else {
      return errors::Unknown("Failed to run JVM callback function. Could not find registry class or its 'call' method.");
    }
  }

  struct JVMWrapper {
    JavaVM* jvm_;
    mutex lock;

	  JVMWrapper(JavaVM* jvm_) : jvm_(jvm_) { }
  };

  static std::vector<JVMWrapper*> jvms;

  struct JVMThreadHelper {
    JNIEnv* env;
	  JavaVM* jvm_;

    JVMThreadHelper() : jvm_(nullptr) { }

    ~JVMThreadHelper() {
      if (jvm_ != nullptr)
        jvm_->DetachCurrentThread();
    }

    void set_jvm(JavaVM* jvm) {
      if (jvm_ != nullptr) {
        if (jvm_ != jvm)
          throw "Multiple JVMs detected per thread.";
	       return;
      }
      jvm_ = jvm;
      int jvmEnvStatus = jvm_->GetEnv((void**) &env, JNI_VERSION_1_6);
      if (jvmEnvStatus == JNI_EDETACHED)
        jvm_->AttachCurrentThread((void**) &env, nullptr);
	  }
  };

  JVMWrapper& get_jvm_wrapper(JavaVM* jvm_) {
    for (JVMWrapper* wrapper : jvms)
      if (wrapper->jvm_ == jvm_)
        return *wrapper;

    /* the JVM isn't in the array */
    jvms.push_back(new JVMWrapper(jvm_));
    return **jvms.rbegin();
  }

  thread_local JVMThreadHelper jvm_thread;
}  // namespace

class JVMCallbackOp : public OpKernel {

public:
  explicit JVMCallbackOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("id", &id_));
    std::string jvm_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("jvm_pointer", &jvm_pointer));
    jvm_ = pointerFromString<JavaVM*>(jvm_pointer);
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;
    std::string registry_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("registry_pointer", &registry_pointer));
    registry_ = pointerFromString<jclass>(registry_pointer);
    call_method_id_ = env->GetStaticMethodID(registry_, "call", "(I[J)[J");
    gpu_ = ctx->device_type().type_string() == DEVICE_GPU;
  }

  void Compute(OpKernelContext* ctx) override {
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;

    JVMCall call;
    call.env = env;
    call.registry = registry_;
    call.call_method_id = call_method_id_;
    call.device = dynamic_cast<Device*>(ctx->device());
    call.gpu = gpu_;
    call.id = id_;
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      call.inputs.push_back(ctx->input(i));
    }

    Status s = CallJVMFunction(&call);

    OP_REQUIRES_OK(ctx, s);

    OP_REQUIRES(ctx, static_cast<int32>(call.outputs.size()) == ctx->num_outputs(),
                errors::InvalidArgument(id_, " returns ", call.outputs.size(),
                                        " values, but expects to see ",
                                        ctx->num_outputs(), " values."));
    for (size_t i = 0; i < call.outputs.size(); ++i) {
      const auto& t = call.outputs[i];
      OP_REQUIRES(
          ctx, t.dtype() == output_type(i),
          errors::InvalidArgument(i, "-th value returned by ", id_, " is ",
                                  DataTypeString(t.dtype()), ", but expects ",
                                  DataTypeString(output_type(i))));
      ctx->set_output(i, t);
    }
  }

private:
  int id_;
  JavaVM* jvm_;
  jclass registry_;
  jmethodID call_method_id_;

  // True if and only if this op has been placed on a GPU.
  bool gpu_;

  TF_DISALLOW_COPY_AND_ASSIGN(JVMCallbackOp);
};

namespace kernel_factory {
struct KernelRegistration {
  KernelRegistration(const KernelDef& d, StringPiece c,
                     kernel_factory::OpKernelRegistrar::Factory f)
      : def(d), kernel_class_name(c.ToString()), factory(f) {}
  const KernelDef def;
  const string kernel_class_name;
  const kernel_factory::OpKernelRegistrar::Factory factory;
};

auto jvmCallbackOpInitializer = []{
  auto* reg = reinterpret_cast<std::unordered_multimap<string, KernelRegistration>*>(GlobalKernelRegistry());
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_CPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_CPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_CPU), JVMCallbackOp);
  }
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_GPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_GPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_GPU), JVMCallbackOp);
  }
  return 0;
}();
}
}  // namespace tensorflow
<commit_msg>[JNI] Another fix related to GRPC linking.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "jvm_callback_op.h"

#include "exception.h"
#include "utilities.h"

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api.h"
#include "tensorflow/core/common_runtime/eager/tensor_handle.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/kernel_def.pb_text.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/lib/core/coding.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"

namespace tensorflow {
REGISTER_OP("JVMCallback")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >=0")
    .SetIsStateful()
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
Invokes a JVM callback function, `f` to compute `f(input)->output`.

This operation is considered stateful. For a stateless version, see
`JVMCallback`.

id: A unique ID representing a registered JVM callback function
  in this address space.
jvm_pointer: A pointer to an existing JVM instance represented as a
  string. This is the JVM that will be used when invoking this JVM
  callback.
registry_pointer: Pointer to the JVM callbacks registry class.
input: List of tensors that will provide input to the op.
output: Output tensors from the op.
Tin: Data types of the inputs to the op.
Tout: Data types of the outputs from the op.
      The length of the list specifies the number of outputs.
)doc");

REGISTER_OP("JVMCallbackStateless")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >= 0")
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
A stateless version of `JVMCallback`.
)doc");

struct TFE_TensorHandle {
  TFE_TensorHandle(const tensorflow::Tensor& t, tensorflow::Device* d,
                   tensorflow::Device* op_device)
      : handle(new tensorflow::TensorHandle(t, d, op_device, nullptr)) {}

  TFE_TensorHandle(tensorflow::uint64 node_id, tensorflow::DataType dtype,
                   tensorflow::EagerContext* ctx)
      : handle(new tensorflow::TensorHandle(node_id, dtype, ctx)) {}

  TFE_TensorHandle(tensorflow::TensorHandle* handle) : handle(handle) {}

  tensorflow::TensorHandle* handle;
};

namespace {
  // Given the 'call', prepares the inputs as a JNI long array that is appropriate for calling the registry.
  jlongArray MakeInputs(JVMCall* call) {
    unsigned long n = call->inputs.size();
    jlongArray inputs = call->env->NewLongArray(static_cast<jsize>(n));
    jlong* inputs_array = call->env->GetLongArrayElements(inputs, nullptr);
    for (int64 i = 0; i < n; ++i) {
      const Tensor& t = call->inputs[i];
      TFE_TensorHandle* tensor;
      if (call->gpu) {
        tensor = new TFE_TensorHandle(t, call->device, call->device);
      } else {
        tensor = new TFE_TensorHandle(t, nullptr, nullptr);
      }
      inputs_array[i] = reinterpret_cast<jlong>(tensor);
    }
    call->env->ReleaseLongArrayElements(inputs, inputs_array, 0);
    return inputs;
  }

  // Process the return values by converting them back to TensorFlow tensors and adding them to the call outputs.
  void ProcessOutputs(JVMCall* call, jlongArray call_outputs, TF_Status* status) {
    call->outputs.clear();
    jsize n = call->env->GetArrayLength(call_outputs);
    jlong* outputs_array = call->env->GetLongArrayElements(call_outputs, nullptr);
    for (int i = 0; i < n; ++i) {
      static_assert(sizeof(jlong) >= sizeof(TFE_TensorHandle*), "Cannot package C object pointers as a Java long");
      if (outputs_array[i] == 0) {
        status->status = errors::InvalidArgument("One of the op output tensors has been disposed already.");
        return;
      }
      auto* h = reinterpret_cast<TFE_TensorHandle*>(outputs_array[i]);
      if (h == nullptr) {
        status->status = errors::InvalidArgument("Could not obtain tensor handle to one of the outputs.");
        return;
      }
      if (!status->status.ok()) return;
      const tensorflow::Tensor* t = nullptr;
      status->status = h->handle->Tensor(&t);
      call->outputs.push_back(*t);
    }
    call->env->ReleaseLongArrayElements(call_outputs, outputs_array, 0);
  }

  // Calls the registered JVM function through the registry.
  Status CallJVMFunction(JVMCall* call) {
    // Prepare the call arguments.
    jlongArray call_inputs = MakeInputs(call);

    // Invoke the registry 'call' method.
    if (call->registry != nullptr && call->call_method_id != nullptr) {
      auto outputs = (jlongArray) call->env->CallStaticObjectMethod(
          call->registry, call->call_method_id, call->id, call_inputs);
      jthrowable exc(call->env->ExceptionOccurred());
      if (exc) {
        // Get the exception string representation to use as the error message.
        jclass excObjCls(call->env->GetObjectClass(exc));
        jmethodID toString = call->env->GetMethodID(excObjCls, "toString", "()Ljava/lang/String;");
        jstring excString = (jstring) call->env->CallObjectMethod(exc, toString);
        const char* excCString = call->env->GetStringUTFChars(excString, 0);
        std::string excCppString(excCString);
        tensorflow::StringPiece tf_exc_string(excCppString);
        call->env->ReleaseStringUTFChars(excString, excCString);

        // Get the exception class name and convert it to a TensorFlow error code.
        jclass classCls(call->env->FindClass("java/lang/Class"));
        jmethodID getName(call->env->GetMethodID(classCls, "getName", "()Ljava/lang/String;"));
        jstring clsName(static_cast<jstring>(call->env->CallObjectMethod(excObjCls, getName)));
        const char* clsNameCString = call->env->GetStringUTFChars(clsName, 0);
        std::string clsNameCppString(clsNameCString);
        int error_code = tf_error_code(clsNameCppString);
        call->env->ReleaseStringUTFChars(clsName, clsNameCString);
        call->env->ExceptionClear();
        return tensorflow::Status((tensorflow::error::Code) error_code, tf_exc_string);
      }

      if (outputs == nullptr) {
        return errors::Unknown("Failed to run JVM callback function.");
      }

      // Process the return values and convert them back to TensorFlow tensors.
      auto* status = new TF_Status;
      ProcessOutputs(call, outputs, status);
      return status->status;
    } else {
      return errors::Unknown("Failed to run JVM callback function. Could not find registry class or its 'call' method.");
    }
  }

  struct JVMWrapper {
    JavaVM* jvm_;
    mutex lock;

	  JVMWrapper(JavaVM* jvm_) : jvm_(jvm_) { }
  };

  static std::vector<JVMWrapper*> jvms;

  struct JVMThreadHelper {
    JNIEnv* env;
	  JavaVM* jvm_;

    JVMThreadHelper() : jvm_(nullptr) { }

    ~JVMThreadHelper() {
      if (jvm_ != nullptr)
        jvm_->DetachCurrentThread();
    }

    void set_jvm(JavaVM* jvm) {
      if (jvm_ != nullptr) {
        if (jvm_ != jvm)
          throw "Multiple JVMs detected per thread.";
	       return;
      }
      jvm_ = jvm;
      int jvmEnvStatus = jvm_->GetEnv((void**) &env, JNI_VERSION_1_6);
      if (jvmEnvStatus == JNI_EDETACHED)
        jvm_->AttachCurrentThread((void**) &env, nullptr);
	  }
  };

  JVMWrapper& get_jvm_wrapper(JavaVM* jvm_) {
    for (JVMWrapper* wrapper : jvms)
      if (wrapper->jvm_ == jvm_)
        return *wrapper;

    /* the JVM isn't in the array */
    jvms.push_back(new JVMWrapper(jvm_));
    return **jvms.rbegin();
  }

  thread_local JVMThreadHelper jvm_thread;
}  // namespace

class JVMCallbackOp : public OpKernel {

public:
  explicit JVMCallbackOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("id", &id_));
    std::string jvm_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("jvm_pointer", &jvm_pointer));
    jvm_ = pointerFromString<JavaVM*>(jvm_pointer);
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;
    std::string registry_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("registry_pointer", &registry_pointer));
    registry_ = pointerFromString<jclass>(registry_pointer);
    call_method_id_ = env->GetStaticMethodID(registry_, "call", "(I[J)[J");
    gpu_ = ctx->device_type().type_string() == DEVICE_GPU;
  }

  void Compute(OpKernelContext* ctx) override {
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;

    JVMCall call;
    call.env = env;
    call.registry = registry_;
    call.call_method_id = call_method_id_;
    call.device = dynamic_cast<Device*>(ctx->device());
    call.gpu = gpu_;
    call.id = id_;
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      call.inputs.push_back(ctx->input(i));
    }

    Status s = CallJVMFunction(&call);

    OP_REQUIRES_OK(ctx, s);

    OP_REQUIRES(ctx, static_cast<int32>(call.outputs.size()) == ctx->num_outputs(),
                errors::InvalidArgument(id_, " returns ", call.outputs.size(),
                                        " values, but expects to see ",
                                        ctx->num_outputs(), " values."));
    for (size_t i = 0; i < call.outputs.size(); ++i) {
      const auto& t = call.outputs[i];
      OP_REQUIRES(
          ctx, t.dtype() == output_type(i),
          errors::InvalidArgument(i, "-th value returned by ", id_, " is ",
                                  DataTypeString(t.dtype()), ", but expects ",
                                  DataTypeString(output_type(i))));
      ctx->set_output(i, t);
    }
  }

private:
  int id_;
  JavaVM* jvm_;
  jclass registry_;
  jmethodID call_method_id_;

  // True if and only if this op has been placed on a GPU.
  bool gpu_;

  TF_DISALLOW_COPY_AND_ASSIGN(JVMCallbackOp);
};

namespace kernel_factory {
struct KernelRegistration {
  KernelRegistration(const KernelDef& d, StringPiece c,
                     kernel_factory::OpKernelRegistrar::Factory f)
      : def(d), kernel_class_name(c.ToString()), factory(f) {}
  const KernelDef def;
  const string kernel_class_name;
  const kernel_factory::OpKernelRegistrar::Factory factory;
};

auto jvmCallbackOpInitializer = []{
  auto* reg = reinterpret_cast<std::unordered_multimap<string, KernelRegistration>*>(GlobalKernelRegistry());
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_CPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_CPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_CPU), JVMCallbackOp);
  }
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_GPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_GPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_GPU), JVMCallbackOp);
  }
  return 0;
}();
}
}  // namespace tensorflow
<|endoftext|>


<commit_before>#include "../src/colors.h"
#include <gtest/gtest.h>

Colors::Color water = "#0743c832";
Colors::Color dummy = "#ffffff";

TEST(TestColor, TestCreate) {
  Colors::Color c;

  ASSERT_TRUE(c.empty());

  ASSERT_FALSE(water.empty());
  ASSERT_EQ(water.R, 7);
  ASSERT_EQ(water.G, 52);
  ASSERT_EQ(water.B, 200);
  ASSERT_EQ(water.ALPHA, 50);
}

TEST(TestColor, TestEmpty) {
  Colors::Color c;
  c.R = c.G = c.B = c.ALPHA;
  ASSERT_TRUE(c.empty());

  Colors::Color cR = c, cG = c, cB = c;
  cR.R = 1;
  cG.G = 1;
  cB.B = 1;

  ASSERT_FALSE(cR.empty());
  ASSERT_FALSE(cG.empty());
  ASSERT_FALSE(cB.empty());
}

TEST(TestColor, TestOpacity) {
  Colors::Color c;
  ASSERT_TRUE(c.transparent());
  ASSERT_FALSE(c.opaque());

  c.ALPHA = 1;
  ASSERT_FALSE(c.transparent());
  ASSERT_FALSE(c.opaque());

  c.ALPHA = 255;
  ASSERT_FALSE(c.transparent());
  ASSERT_TRUE(c.opaque());
}

TEST(TestBlock, TestCreate) {
  Colors::Block b;

  ASSERT_TRUE(b.type == Colors::FULL);
  ASSERT_TRUE(b.primary.empty());
  ASSERT_TRUE(b.secondary.empty());

  b = Colors::Block(Colors::drawSlab, dummy);
  ASSERT_TRUE(b.type == Colors::drawSlab);
  ASSERT_FALSE(b.primary.empty());
  ASSERT_TRUE(b.secondary.empty());

  b = Colors::Block(Colors::drawStair, dummy, dummy);
  ASSERT_TRUE(b.type == Colors::drawStair);
  ASSERT_FALSE(b.primary.empty());
  ASSERT_FALSE(b.secondary.empty());
}

TEST(TestBlock, TestEqualOperator) {
  Colors::Block b1 = Colors::Block(Colors::drawBeam, dummy), b2 = b1;

  ASSERT_EQ(b1, b2);
  b1.type = Colors::drawTransparent;
  ASSERT_NE(b1, b2);

  b2 = Colors::Block(Colors::drawTransparent, dummy, dummy);
  ASSERT_NE(b1, b2);
}

TEST(TestBlock, TestJson) {
  Colors::Block b, translated;
  b = Colors::Block(Colors::drawStair, dummy, dummy);
  translated = nlohmann::json(b).get<Colors::Block>();

  ASSERT_EQ(b, translated);
}

TEST(TestPalette, TestJson) {
  Colors::Palette p, translated;
  p.insert(std::pair("minecraft:water", Colors::Block(Colors::FULL, water)));
  translated = nlohmann::json(p).get<Colors::Palette>();

  ASSERT_EQ(p, translated);
}

TEST(TestPalette, TestLoad) {
  Colors::Palette p;

  ASSERT_TRUE(Colors::load(&p));
  ASSERT_TRUE(p.size());
}

TEST(TestPalette, TestLoadFile) {
  Colors::Palette p;

  ASSERT_TRUE(Colors::load(&p));
  ASSERT_TRUE(p.size());
  ASSERT_TRUE(Colors::load("tests/nowater.json", &p));
  ASSERT_TRUE(p.size());
  ASSERT_TRUE(p.find("minecraft:water") != p.end());
  ASSERT_TRUE(p["minecraft:water"].primary.transparent());
}
<commit_msg>Updated test<commit_after>#include "../src/colors.h"
#include <gtest/gtest.h>

Colors::Color water = "#0743c832";
Colors::Color dummy = "#ffffff";

TEST(TestColor, TestCreate) {
  Colors::Color c;

  ASSERT_TRUE(c.empty());

  ASSERT_FALSE(water.empty());
  ASSERT_EQ(water.R, 7);
  ASSERT_EQ(water.G, 67);
  ASSERT_EQ(water.B, 200);
  ASSERT_EQ(water.ALPHA, 50);
}

TEST(TestColor, TestEmpty) {
  Colors::Color c;
  c.R = c.G = c.B = c.ALPHA;
  ASSERT_TRUE(c.empty());

  Colors::Color cR = c, cG = c, cB = c;
  cR.R = 1;
  cG.G = 1;
  cB.B = 1;

  ASSERT_FALSE(cR.empty());
  ASSERT_FALSE(cG.empty());
  ASSERT_FALSE(cB.empty());
}

TEST(TestColor, TestOpacity) {
  Colors::Color c;
  ASSERT_TRUE(c.transparent());
  ASSERT_FALSE(c.opaque());

  c.ALPHA = 1;
  ASSERT_FALSE(c.transparent());
  ASSERT_FALSE(c.opaque());

  c.ALPHA = 255;
  ASSERT_FALSE(c.transparent());
  ASSERT_TRUE(c.opaque());
}

TEST(TestBlock, TestCreate) {
  Colors::Block b;

  ASSERT_TRUE(b.type == Colors::FULL);
  ASSERT_TRUE(b.primary.empty());
  ASSERT_TRUE(b.secondary.empty());

  b = Colors::Block(Colors::drawSlab, dummy);
  ASSERT_TRUE(b.type == Colors::drawSlab);
  ASSERT_FALSE(b.primary.empty());
  ASSERT_TRUE(b.secondary.empty());

  b = Colors::Block(Colors::drawStair, dummy, dummy);
  ASSERT_TRUE(b.type == Colors::drawStair);
  ASSERT_FALSE(b.primary.empty());
  ASSERT_FALSE(b.secondary.empty());
}

TEST(TestBlock, TestEqualOperator) {
  Colors::Block b1 = Colors::Block(Colors::drawBeam, dummy), b2 = b1;

  ASSERT_EQ(b1, b2);
  b1.type = Colors::drawTransparent;
  ASSERT_NE(b1, b2);

  b2 = Colors::Block(Colors::drawTransparent, dummy, dummy);
  ASSERT_NE(b1, b2);
}

TEST(TestBlock, TestJson) {
  Colors::Block b, translated;
  b = Colors::Block(Colors::drawStair, dummy, dummy);
  translated = nlohmann::json(b).get<Colors::Block>();

  ASSERT_EQ(b, translated);
}

TEST(TestPalette, TestJson) {
  Colors::Palette p, translated;
  p.insert(std::pair("minecraft:water", Colors::Block(Colors::FULL, water)));
  translated = nlohmann::json(p).get<Colors::Palette>();

  ASSERT_EQ(p, translated);
}

TEST(TestPalette, TestLoad) {
  Colors::Palette p;

  ASSERT_TRUE(Colors::load(&p));
  ASSERT_TRUE(p.size());
}

TEST(TestPalette, TestLoadFile) {
  Colors::Palette p;

  ASSERT_TRUE(Colors::load(&p));
  ASSERT_TRUE(p.size());
  ASSERT_TRUE(Colors::load("tests/nowater.json", &p));
  ASSERT_TRUE(p.size());
  ASSERT_TRUE(p.find("minecraft:water") != p.end());
  ASSERT_TRUE(p["minecraft:water"].primary.transparent());
}
<|endoftext|>


<commit_before>#include "../filesystem/fs_operations.h"
#include "../filesystem/directory_iterator.h"
#include "../filesystem/file_status.h"
#include "../filesystem/path.h"

#include <unistd.h>

#include <string>
#include <functional>

#include "cppunit-header.h"

namespace fs = filesystem::v1;

class Test_fs_operations : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE(Test_fs_operations);
	CPPUNIT_TEST(absolute);
	CPPUNIT_TEST(canonical);
	CPPUNIT_TEST(copy_file);
	CPPUNIT_TEST(create_directories);
	CPPUNIT_TEST(get_current_path);
	CPPUNIT_TEST(set_current_path);
	CPPUNIT_TEST(is_empty);
	CPPUNIT_TEST(status);
	CPPUNIT_TEST(symlink_status);
	CPPUNIT_TEST(last_write_time);
	CPPUNIT_TEST(temp_directory_path);
	CPPUNIT_TEST(remove_all);
	CPPUNIT_TEST_SUITE_END();

 public:
	Test_fs_operations()
	 : file("/etc/passwd")
	 , nonexistent_file("/tmp/badpath")
	 , non_accessible_file("/root/badpath")
		{ }

	void setUp()
	{
		savedCWD = fs::current_path();
	}

	void tearDown()
	{
		// restore the old CWD
		fs::current_path(savedCWD);

		if (config::verbose)
			std::cout << "\n-> RESTORING CWD: " << fs::current_path().c_str()
			          << std::endl;
	}

 protected:
	const fs::path file;
	const fs::path nonexistent_file;
	const fs::path non_accessible_file;

	fs::path savedCWD;

	void test_fs_exception_thrown(std::function<void(void)> f,
	                              fs::path target)
	{
		bool caught = false;
		try {
			try {
				f();
			} catch (fs::filesystem_error & e) {
				CPPUNIT_ASSERT(e.path1() == target);
				CPPUNIT_ASSERT(e.code());
				if (config::verbose)
					std::cout << "Caught exception: " << e.what() << std::endl;
				caught = true;
			} catch (...) {
				throw;
			}
		} catch (std::exception & e) {
			if (config::verbose)
				std::cout << "Caught exception: " << e.what() << std::endl;
			throw;
		} catch (...) {
			if (config::verbose)
				std::cout << "Caught unknown exception!" << std::endl;
			throw;
		}

		CPPUNIT_ASSERT(caught);
	}

	void absolute()
	{
		fs::path full_path{"/usr/local"};
		fs::path base_path{"/usr"};
		fs::path a{"local"};
		fs::path b{"/usr/local"};

		CPPUNIT_ASSERT(fs::absolute(a, base_path) == full_path);
		CPPUNIT_ASSERT(fs::absolute(b, base_path) == full_path);

	}

	void canonical()
	{
		fs::path bad_path{"/badpath"};
		fs::path full_path{"/usr/local"};
		fs::path base_path{"/usr"};
		fs::path a{"local/../local/bin/.."};
		fs::path b{"/usr/local/../../../../usr/./local"};

		CPPUNIT_ASSERT_THROW(fs::canonical(bad_path), fs::filesystem_error);

		CPPUNIT_ASSERT(fs::canonical(a, base_path) == full_path);
		CPPUNIT_ASSERT(fs::canonical(b, base_path) == full_path);

		std::error_code ec;
		current_path(base_path, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::canonical(a, ec) == full_path);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::canonical(b, ec) == full_path);
		CPPUNIT_ASSERT(!ec);

	}

	void copy_file()
	{
		using CO = fs::copy_options;
		std::vector<fs::copy_options> bad_options{
			CO::skip_existing | CO::overwrite_existing,
			CO::skip_existing | CO::update_existing,
			CO::overwrite_existing | CO::update_existing,
			CO::skip_existing | CO::overwrite_existing | CO::update_existing,
			CO::copy_symlinks | CO::skip_symlinks,

			CO::directories_only | CO::create_symlinks,
			CO::directories_only | CO::create_hard_links,
			CO::create_symlinks | CO::create_hard_links,
			CO::directories_only | CO::create_symlinks | CO::create_hard_links,
		};

		for (const auto & o : bad_options)
		{
			CPPUNIT_ASSERT_THROW(fs::copy_file("a", "b", o),
			                     fs::filesystem_error);
		}

		std::error_code ec;
		CPPUNIT_ASSERT(!fs::copy_file("/badpath", "b", ec));
		CPPUNIT_ASSERT(ec);

		CPPUNIT_ASSERT(fs::copy_file("/etc/fstab", "/tmp/xxx",
		                             fs::copy_options::overwrite_existing));
		CPPUNIT_ASSERT_THROW(fs::copy_file("/etc/fstab", "/tmp/xxx"),
		                     fs::filesystem_error);
	}

	void create_directories()
	{
		bool rc = false;
		std::error_code ec;
		fs::path p{"/usr/local/bin"};
		rc = fs::create_directories(p, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(rc == false);

		ec.clear();
		fs::path tmp{"/tmp/foo" + std::to_string(getpid()) + "/bar"};
		rc = fs::create_directories(tmp, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(rc == true);
	}

	void get_current_path()
	{
// XXX - How do we get this to fail?
		fs::path p = fs::current_path();

		if (config::verbose)
			std::cout << "\nCurrent path = '" << p.c_str() << "'" << std::endl;
	}

	void set_current_path()
	{
		test_fs_exception_thrown(
			[&]() { fs::current_path(non_accessible_file); },
			non_accessible_file);

		CPPUNIT_ASSERT_NO_THROW(fs::current_path(fs::temp_directory_path()));
	}

	void is_empty()
	{
		// Test directory
		fs::path p = fs::temp_directory_path() / "emptydir";
		std::error_code ec;
		try { fs::remove_all(p); } catch (...) { }
		fs::create_directory(p, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::is_empty(p));
		CPPUNIT_ASSERT(fs::create_directories(p / "subdir"));
		CPPUNIT_ASSERT(!fs::is_empty(p));

		// Test File
		fs::path q = p / "foo";
		CPPUNIT_ASSERT(fs::copy_file("/etc/fstab", q));
		CPPUNIT_ASSERT(!fs::is_empty(q));
		CPPUNIT_ASSERT_NO_THROW(fs::resize_file(q, 0));
		CPPUNIT_ASSERT(fs::is_empty(q));
		CPPUNIT_ASSERT(fs::remove_all(p) != 0);
	}

	void status()
	{
		fs::file_status s;

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(nonexistent_file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::not_found);

		test_fs_exception_thrown(
			[&]() {s = fs::status(non_accessible_file); },
			non_accessible_file);

		std::error_code ec;
		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::status(file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(nonexistent_file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::not_found);

		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::status(non_accessible_file, ec));
		CPPUNIT_ASSERT(ec && s.type() == fs::file_type::none);
	}

	void symlink_status()
	{
		fs::file_status s;

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(nonexistent_file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::not_found);

		test_fs_exception_thrown(
			[&]() {s = fs::symlink_status(non_accessible_file); },
			non_accessible_file);

		std::error_code ec;
		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(nonexistent_file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::not_found);

		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(
			s = fs::symlink_status(non_accessible_file, ec));
		CPPUNIT_ASSERT(ec && s.type() == fs::file_type::none);
	}

	void last_write_time()
	{
		fs::path p{"/etc/passwd"};
		auto t0 = fs::file_time_type::min();
		auto tp = fs::last_write_time(p);
		CPPUNIT_ASSERT(tp > t0);
	}

	void remove_all()
	{
		std::error_code ec;
		fs::path tmp{fs::temp_directory_path() / "foo"};
		tmp += std::to_string(getpid());

		CPPUNIT_ASSERT_NO_THROW(fs::remove_all(tmp, ec));
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(!exists(tmp));

		fs::path longpath(tmp / "bar" / "ugg");
		CPPUNIT_ASSERT(fs::create_directories(longpath));
		CPPUNIT_ASSERT(fs::remove_all(tmp) > 0);
		CPPUNIT_ASSERT(!exists(tmp));

		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(!exists(tmp));

	}

	void temp_directory_path()
	{
		fs::path p = fs::temp_directory_path();
//		std::cout << "default temporary directory = "
//		          << p.c_str() << std::endl;

		CPPUNIT_ASSERT(p == "/tmp");
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(Test_fs_operations);
<commit_msg>added create_directory and space tests<commit_after>#include "../filesystem/fs_operations.h"
#include "../filesystem/directory_iterator.h"
#include "../filesystem/file_status.h"
#include "../filesystem/path.h"

#include <unistd.h>

#include <string>
#include <functional>

#include "cppunit-header.h"

namespace fs = filesystem::v1;

class Test_fs_operations : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE(Test_fs_operations);
	CPPUNIT_TEST(absolute);
	CPPUNIT_TEST(canonical);
	CPPUNIT_TEST(copy_file);
	CPPUNIT_TEST(create_directories);
	CPPUNIT_TEST(create_directory);
	CPPUNIT_TEST(get_current_path);
	CPPUNIT_TEST(set_current_path);
	CPPUNIT_TEST(is_empty);
	CPPUNIT_TEST(space);
	CPPUNIT_TEST(status);
	CPPUNIT_TEST(symlink_status);
	CPPUNIT_TEST(last_write_time);
	CPPUNIT_TEST(temp_directory_path);
	CPPUNIT_TEST(remove_all);
	CPPUNIT_TEST_SUITE_END();

 public:
	Test_fs_operations()
	 : file("/etc/passwd")
	 , nonexistent_file("/tmp/badpath")
	 , non_accessible_file("/root/badpath")
		{ }

	void setUp()
	{
		savedCWD = fs::current_path();
	}

	void tearDown()
	{
		// restore the old CWD
		fs::current_path(savedCWD);

		if (config::verbose)
			std::cout << "\n-> RESTORING CWD: " << fs::current_path().c_str()
			          << std::endl;
	}

 protected:
	const fs::path file;
	const fs::path nonexistent_file;
	const fs::path non_accessible_file;

	fs::path savedCWD;

	void test_fs_exception_thrown(std::function<void(void)> f,
	                              fs::path target)
	{
		bool caught = false;
		try {
			try {
				f();
			} catch (fs::filesystem_error & e) {
				CPPUNIT_ASSERT(e.path1() == target);
				CPPUNIT_ASSERT(e.code());
				if (config::verbose)
					std::cout << "Caught exception: " << e.what() << std::endl;
				caught = true;
			} catch (...) {
				throw;
			}
		} catch (std::exception & e) {
			if (config::verbose)
				std::cout << "Caught exception: " << e.what() << std::endl;
			throw;
		} catch (...) {
			if (config::verbose)
				std::cout << "Caught unknown exception!" << std::endl;
			throw;
		}

		CPPUNIT_ASSERT(caught);
	}

	void absolute()
	{
		fs::path full_path{"/usr/local"};
		fs::path base_path{"/usr"};
		fs::path a{"local"};
		fs::path b{"/usr/local"};

		CPPUNIT_ASSERT(fs::absolute(a, base_path) == full_path);
		CPPUNIT_ASSERT(fs::absolute(b, base_path) == full_path);

	}

	void canonical()
	{
		fs::path bad_path{"/badpath"};
		fs::path full_path{"/usr/local"};
		fs::path base_path{"/usr"};
		fs::path a{"local/../local/bin/.."};
		fs::path b{"/usr/local/../../../../usr/./local"};

		CPPUNIT_ASSERT_THROW(fs::canonical(bad_path), fs::filesystem_error);

		CPPUNIT_ASSERT(fs::canonical(a, base_path) == full_path);
		CPPUNIT_ASSERT(fs::canonical(b, base_path) == full_path);

		std::error_code ec;
		current_path(base_path, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::canonical(a, ec) == full_path);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::canonical(b, ec) == full_path);
		CPPUNIT_ASSERT(!ec);

	}

	void copy_file()
	{
		using CO = fs::copy_options;
		std::vector<fs::copy_options> bad_options{
			CO::skip_existing | CO::overwrite_existing,
			CO::skip_existing | CO::update_existing,
			CO::overwrite_existing | CO::update_existing,
			CO::skip_existing | CO::overwrite_existing | CO::update_existing,
			CO::copy_symlinks | CO::skip_symlinks,

			CO::directories_only | CO::create_symlinks,
			CO::directories_only | CO::create_hard_links,
			CO::create_symlinks | CO::create_hard_links,
			CO::directories_only | CO::create_symlinks | CO::create_hard_links,
		};

		for (const auto & o : bad_options)
		{
			CPPUNIT_ASSERT_THROW(fs::copy_file("a", "b", o),
			                     fs::filesystem_error);
		}

		std::error_code ec;
		CPPUNIT_ASSERT(!fs::copy_file("/badpath", "b", ec));
		CPPUNIT_ASSERT(ec);

		CPPUNIT_ASSERT(fs::copy_file("/etc/fstab", "/tmp/xxx",
		                             fs::copy_options::overwrite_existing));
		CPPUNIT_ASSERT_THROW(fs::copy_file("/etc/fstab", "/tmp/xxx"),
		                     fs::filesystem_error);

		CPPUNIT_ASSERT(fs::remove("/tmp/xxx"));
	}

	void create_directories()
	{
		bool rc = false;
		std::error_code ec;
		fs::path p{"/usr/local/bin"};
		rc = fs::create_directories(p, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(rc == false);

		ec.clear();
		fs::path tmp{"/tmp/foo" + std::to_string(getpid()) + "/bar"};
		rc = fs::create_directories(tmp, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(rc == true);
		fs::path bad{"/root/subdir/bad"};
		CPPUNIT_ASSERT_THROW(fs::create_directories(bad),
		                     fs::filesystem_error);
	}

	void create_directory()
	{
		fs::path dir(fs::temp_directory_path() / "created_dir");
		fs::path file(fs::temp_directory_path() / "created_dir/file");
		CPPUNIT_ASSERT_NO_THROW(fs::create_directory(dir));
		CPPUNIT_ASSERT(fs::is_directory(dir));
		CPPUNIT_ASSERT_NO_THROW(fs::create_directory(dir));
		CPPUNIT_ASSERT(fs::copy_file("/etc/fstab", file));
		CPPUNIT_ASSERT_THROW(fs::create_directory(file), fs::filesystem_error);
		CPPUNIT_ASSERT(fs::remove_all(dir) > 0);
	}

	void get_current_path()
	{
// XXX - How do we get this to fail?
		fs::path p = fs::current_path();

		if (config::verbose)
			std::cout << "\nCurrent path = '" << p.c_str() << "'" << std::endl;
	}

	void set_current_path()
	{
		test_fs_exception_thrown(
			[&]() { fs::current_path(non_accessible_file); },
			non_accessible_file);

		CPPUNIT_ASSERT_NO_THROW(fs::current_path(fs::temp_directory_path()));
	}

	void is_empty()
	{
		// Test directory
		fs::path p = fs::temp_directory_path() / "emptydir";
		std::error_code ec;
		try { fs::remove_all(p); } catch (...) { }
		fs::create_directory(p, ec);
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(fs::is_empty(p));
		CPPUNIT_ASSERT(fs::create_directories(p / "subdir"));
		CPPUNIT_ASSERT(!fs::is_empty(p));

		// Test File
		fs::path q = p / "foo";
		CPPUNIT_ASSERT(fs::copy_file("/etc/fstab", q));
		CPPUNIT_ASSERT(!fs::is_empty(q));
		CPPUNIT_ASSERT_NO_THROW(fs::resize_file(q, 0));
		CPPUNIT_ASSERT(fs::is_empty(q));
		CPPUNIT_ASSERT(fs::remove_all(p) != 0);
	}

	void space()
	{
		fs::space_info s;
		fs::path goodpath{fs::temp_directory_path()};
		fs::path badpath{"/badpath"};

		CPPUNIT_ASSERT_NO_THROW(s = fs::space(fs::temp_directory_path()));
		CPPUNIT_ASSERT(  s.capacity > 0
		              && s.capacity != static_cast<uintmax_t>(-1));
		CPPUNIT_ASSERT(  s.available > 0
		              && s.available != static_cast<uintmax_t>(-1));
		CPPUNIT_ASSERT(  s.free > 0
		              && s.free != static_cast<uintmax_t>(-1));
		// not sure what else we can test here
		CPPUNIT_ASSERT(s.capacity >= s.available);
		CPPUNIT_ASSERT(s.capacity >= s.free);

		CPPUNIT_ASSERT_THROW(fs::space(badpath), fs::filesystem_error);
	}

	void status()
	{
		fs::file_status s;

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(nonexistent_file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::not_found);

		test_fs_exception_thrown(
			[&]() {s = fs::status(non_accessible_file); },
			non_accessible_file);

		std::error_code ec;
		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::status(file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::status(nonexistent_file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::not_found);

		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::status(non_accessible_file, ec));
		CPPUNIT_ASSERT(ec && s.type() == fs::file_type::none);
	}

	void symlink_status()
	{
		fs::file_status s;

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(nonexistent_file));
		CPPUNIT_ASSERT(s.type() == fs::file_type::not_found);

		test_fs_exception_thrown(
			[&]() {s = fs::symlink_status(non_accessible_file); },
			non_accessible_file);

		std::error_code ec;
		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::regular);

		CPPUNIT_ASSERT_NO_THROW(s = fs::symlink_status(nonexistent_file, ec));
		CPPUNIT_ASSERT(!ec && s.type() == fs::file_type::not_found);

		s = fs::file_status{};
		CPPUNIT_ASSERT_NO_THROW(
			s = fs::symlink_status(non_accessible_file, ec));
		CPPUNIT_ASSERT(ec && s.type() == fs::file_type::none);
	}

	void last_write_time()
	{
		fs::path p{"/etc/passwd"};
		auto t0 = fs::file_time_type::min();
		auto tp = fs::last_write_time(p);
		CPPUNIT_ASSERT(tp > t0);
	}

	void remove_all()
	{
		std::error_code ec;
		fs::path tmp{fs::temp_directory_path() / "foo"};
		tmp += std::to_string(getpid());

		CPPUNIT_ASSERT_NO_THROW(fs::remove_all(tmp, ec));
		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(!exists(tmp));

		fs::path longpath(tmp / "bar" / "ugg");
		CPPUNIT_ASSERT(fs::create_directories(longpath));
		CPPUNIT_ASSERT(fs::remove_all(tmp) > 0);
		CPPUNIT_ASSERT(!exists(tmp));

		CPPUNIT_ASSERT(!ec);
		CPPUNIT_ASSERT(!exists(tmp));

	}

	void temp_directory_path()
	{
		fs::path p = fs::temp_directory_path();
//		std::cout << "default temporary directory = "
//		          << p.c_str() << std::endl;

		CPPUNIT_ASSERT(p == "/tmp");
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(Test_fs_operations);
<|endoftext|>


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

/**
 * response_frame.cpp
 *
 * Response Frame class stores the current frame whenever the replace_frame() method is called
 */

#include "response_frame.h"
#include "log_imp.h"
#include "enumeration.h"
#include "avdecc-lib_build.h"
#include <stdlib.h>
#include <iostream>

namespace avdecc_lib
{

response_frame::response_frame(const uint8_t * frame, size_t size, size_t pos)
{
    desc_frame_size = size;
    desc_position = pos;
    desc_buffer = (uint8_t *)malloc(desc_frame_size * sizeof(uint8_t));
    memcpy(desc_buffer, frame, desc_frame_size);
}

response_frame::~response_frame()
{
    typedef std::map<uint16_t, struct cmd_resp_frame_info *>::iterator it;
    for (it i = cmd_resp_buffers.begin(); i != cmd_resp_buffers.end(); i++)
    {
        free(i->second->buffer);
        delete i->second;
    }

    free(desc_buffer);
}

int response_frame::store_cmd_resp_frame(uint16_t cmd_type, const uint8_t *frame, size_t pos, size_t size)
{
    uint8_t * buffer = NULL;
    std::map<uint16_t, struct cmd_resp_frame_info * >::iterator it = cmd_resp_buffers.find(cmd_type);
    if (it != cmd_resp_buffers.end())
    {
        free(it->second->buffer);
        delete it->second;
    }

    buffer = (uint8_t *)malloc(size * sizeof(uint8_t));
    if (!buffer)
    {
        log_imp_ref->post_log_msg(LOGGING_LEVEL_ERROR, "Error allocating memory for response buffer");
        free(buffer);
        return -1;
    }

    memcpy(buffer, frame, size);
    cmd_resp_buffers[cmd_type] = new cmd_resp_frame_info(buffer, size, pos);

    return 0;
}

int response_frame::replace_desc_frame(const uint8_t * frame, size_t pos, size_t size)
{
    uint8_t * replaced_buffer = NULL;

    if (size <= desc_frame_size)
    {
        assert(size <= desc_frame_size);
        memcpy(desc_buffer, frame, size);
    }
    else
    {
        replaced_buffer = (uint8_t *)realloc(desc_buffer, size * sizeof(uint8_t));
        if (replaced_buffer != NULL)
        {
            desc_buffer = replaced_buffer;
            memcpy(desc_buffer, frame, size);
        }
        else
        {
            log_imp_ref->post_log_msg(LOGGING_LEVEL_ERROR, "Error reallocating memory");
            free(desc_buffer);
            return -1;
        }
    }
    desc_position = pos;
    desc_frame_size = size;

    return 0;
}

uint8_t * response_frame::get_desc_buffer()
{
    return desc_buffer;
}

size_t response_frame::get_desc_pos()
{
    return desc_position;
}

size_t response_frame::get_desc_size()
{
    return desc_frame_size;
}

struct cmd_resp_frame_info * response_frame::get_cmd_resp_frame_info(uint16_t cmd_type)
{
    std::map<uint16_t, struct cmd_resp_frame_info * >::iterator it = cmd_resp_buffers.find(cmd_type);
    if (it != cmd_resp_buffers.end())
        return it->second;

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

/**
 * response_frame.cpp
 *
 * Response Frame class stores the current frame whenever the replace_frame() method is called
 */

#include "response_frame.h"
#include "log_imp.h"
#include "enumeration.h"
#include "avdecc-lib_build.h"
#include <stdlib.h>
#include <iostream>

namespace avdecc_lib
{

response_frame::response_frame(const uint8_t * frame, size_t size, size_t pos)
{
    desc_frame_size = size;
    desc_position = pos;
    desc_buffer = (uint8_t *)malloc(desc_frame_size * sizeof(uint8_t));
    memcpy(desc_buffer, frame, desc_frame_size);
}

response_frame::~response_frame()
{
    typedef std::map<uint16_t, struct cmd_resp_frame_info *>::iterator it;
    for (it i = cmd_resp_buffers.begin(); i != cmd_resp_buffers.end(); i++)
    {
        free(i->second->buffer);
        delete i->second;
    }

    free(desc_buffer);
}

int response_frame::store_cmd_resp_frame(uint16_t cmd_type, const uint8_t *frame, size_t pos, size_t size)
{
    uint8_t * buffer = NULL;
    std::map<uint16_t, struct cmd_resp_frame_info * >::iterator it = cmd_resp_buffers.find(cmd_type);
    if (it != cmd_resp_buffers.end())
    {
        free(it->second->buffer);
        delete it->second;
    }

    buffer = (uint8_t *)malloc(size * sizeof(uint8_t));
    if (!buffer)
    {
        log_imp_ref->post_log_msg(LOGGING_LEVEL_ERROR, "Error allocating memory for response buffer");
        free(buffer);
        return -1;
    }

    memcpy(buffer, frame, size);
    cmd_resp_buffers[cmd_type] = new cmd_resp_frame_info(buffer, size, pos);

    return 0;
}

int response_frame::replace_desc_frame(const uint8_t * frame, size_t pos, size_t size)
{
    uint8_t * replaced_buffer = NULL;

    if (size <= desc_frame_size)
    {
        assert(size <= desc_frame_size);
        memcpy(desc_buffer, frame, size);
    }
    else
    {
        replaced_buffer = (uint8_t *)realloc(desc_buffer, size * sizeof(uint8_t));
        if (replaced_buffer != NULL)
        {
            desc_buffer = replaced_buffer;
            memcpy(desc_buffer, frame, size);
        }
        else
        {
            log_imp_ref->post_log_msg(LOGGING_LEVEL_ERROR, "Error reallocating memory");
            free(desc_buffer);
            return -1;
        }
    }
    desc_position = pos;
    desc_frame_size = size;

    return 0;
}

uint8_t * response_frame::get_desc_buffer()
{
    return desc_buffer;
}

size_t response_frame::get_desc_pos()
{
    return desc_position;
}

size_t response_frame::get_desc_size()
{
    return desc_frame_size;
}

struct cmd_resp_frame_info * response_frame::get_cmd_resp_frame_info(uint16_t cmd_type)
{
    std::map<uint16_t, struct cmd_resp_frame_info * >::iterator it = cmd_resp_buffers.find(cmd_type);
    if (it != cmd_resp_buffers.end())
        return it->second;

    return NULL;
}
}
<|endoftext|>


<commit_before>#include "TestBase.h"

const char	*testForEachUserType =
"class array\r\n\
{\r\n\
	int[] arr;\r\n\
}\r\n\
class array_iterator\r\n\
{\r\n\
	array ref arr;\r\n\
	int pos;\r\n\
}\r\n\
auto array(int size)\r\n\
{\r\n\
	array ret;\r\n\
	ret.arr = new int[size];\r\n\
	return ret;\r\n\
}\r\n\
auto array:start()\r\n\
{\r\n\
	array_iterator iter;\r\n\
	iter.arr = this;\r\n\
	iter.pos = 0;\r\n\
	return iter;\r\n\
}\r\n\
int ref array_iterator:next()\r\n\
{\r\n\
	return &arr.arr[pos++];\r\n\
}\r\n\
int array_iterator:hasnext()\r\n\
{\r\n\
	return pos < arr.arr.size;\r\n\
}\r\n\
\r\n\
array arr = array(16);\r\n\
int u = 1;\r\n\
for(i in arr)\r\n\
	i = u++;\r\n\
for(i in arr)\r\n\
	i *= i;\r\n\
int sum = 0;\r\n\
for(i in arr)\r\n\
	sum += i;\r\n\
return sum;";
TEST_RESULT("For each on user type", testForEachUserType, "1496");

const char	*testForEach2 =
"import std.vector;\r\n\
auto a = vector<int>();\r\n\
a.push_back(4);\r\n\
a.push_back(8);\r\n\
a.push_back(14);\r\n\
\r\n\
int sum = 0;\r\n\
for(int i in a)\r\n\
{\r\n\
	sum += i;\r\n\
	i *= 2;\r\n\
}\r\n\
int sum2 = 0;\r\n\
for(int i in a)\r\n\
{\r\n\
	sum2 += i;\r\n\
}\r\n\
return sum + sum2;";
TEST_RESULT("For each with specified element type", testForEach2, "78");

const char	*testForEach3 =
"int[] arr1 = { 2, 6, 7 };\r\n\
int sum1 = 0;\r\n\
for(i in arr1)\r\n\
	sum1 += i;\r\n\
\r\n\
int[3] arr2 = { 2, 6, 7 };\r\n\
int sum2 = 0;\r\n\
for(i in arr2)\r\n\
	sum2 += i;\r\n\
\r\n\
auto arr3 = { { 2, 6, 7 }, { 1, 2, 3 } };\r\n\
int sum3 = 0;\r\n\
for(line in arr3)\r\n\
	for(num in line)\r\n\
		sum3 += num;\r\n\
\r\n\
auto func(){ return { 1, 6 }; }\r\n\
int sum4 = 0;\r\n\
for(i in func())\r\n\
	sum4 += i;\r\n\
\r\n\
int sum5 = 0;\r\n\
for(i in { 1, 2, 3 })\r\n\
	sum5 += i;\r\n\
return 0;";
TEST("For each for standard arrays", testForEach3, "0")
{
	CHECK_INT("sum1", 0, 15, lastFailed);
	CHECK_INT("sum2", 0, 15, lastFailed);
	CHECK_INT("sum3", 0, 21, lastFailed);
	CHECK_INT("sum4", 0, 7, lastFailed);
	CHECK_INT("sum5", 0, 6, lastFailed);
}

const char	*testForEach4 =
"int sum = 0;\r\n\
for(i in { 1, 2, 3 }, j in { 4, 5, 6, 7 })\r\n\
	sum += i + j;\r\n\
\r\n\
int[3] arr1 = { 2, 6, 7 };\r\n\
int[] arr2 = { 8, -4, 2 };\r\n\
for(i in arr1, j in arr2)\r\n\
	i += j;\r\n\
return sum;";
TEST("For each with multiple arrays", testForEach4, "21")
{
	CHECK_INT("arr1", 0, 10, lastFailed);
	CHECK_INT("arr1", 1, 2, lastFailed);
	CHECK_INT("arr1", 2, 9, lastFailed);
}

const char	*testRangeIterator =
"class range_iterator\r\n\
{\r\n\
	int pos;\r\n\
	int max;\r\n\
}\r\n\
auto range_iterator:start()\r\n\
{\r\n\
	return *this;\r\n\
}\r\n\
int range_iterator:next()\r\n\
{\r\n\
	pos++;\r\n\
	return pos - 1;\r\n\
}\r\n\
int range_iterator:hasnext()\r\n\
{\r\n\
	return pos != max;\r\n\
}\r\n\
auto range(int min, max)\r\n\
{\r\n\
	range_iterator r;\r\n\
	r.pos = min;\r\n\
	r.max = max + 1;\r\n\
	return r;\r\n\
}\r\n\
int factorial(int v)\r\n\
{\r\n\
	int fact = 1;\r\n\
	for(i in range(1, v))\r\n\
		fact *= i;\r\n\
	return fact;\r\n\
}\r\n\
return factorial(10);";
TEST_RESULT("Extra node wrapping in for each with function call in array part", testRangeIterator, "3628800");

const char	*testForEach5 =
"import std.vector;\r\n\
class Test\r\n\
{\r\n\
	vector<int> a;\r\n\
}\r\n\
auto a = new Test;\r\n\
a.a = vector<int>();\r\n\
a.a.push_back(10);\r\n\
a.a.push_back(105);\r\n\
int sum = 0;\r\n\
for(int i in a.a) sum += i;\r\n\
return sum;";
TEST_RESULT("For each on a member of a type that we had a reference to", testForEach5, "115");

const char	*testIllFormedForEach =
"import std.vector;\r\n\
\r\n\
auto a = vector<vector<int>>(3);\r\n\
a.push_back(vector<int>());\r\n\
a.back().push_back(1);\r\n\
a.back().push_back(2);\r\n\
a.push_back(vector<int>());\r\n\
a.back().push_back(3);\r\n\
a.back().push_back(4);\r\n\
int z = 1;\r\n\
for(vector<int> i in a, int x in i)\r\n\
	z = 0;\r\n\
return z;";
TEST_RESULT("Ill-formed for_each test", testIllFormedForEach, "1");

const char	*testInOperatorOnArray =
"int[] arr = { 1, 2, 3, 4 };\r\n\
return (3 in arr) && !(6 in arr);";
TEST_RESULT("in operator on array", testInOperatorOnArray, "1");
<commit_msg>Additional test where for each iterator has an 'auto' type<commit_after>#include "TestBase.h"

const char	*testForEachUserType =
"class array\r\n\
{\r\n\
	int[] arr;\r\n\
}\r\n\
class array_iterator\r\n\
{\r\n\
	array ref arr;\r\n\
	int pos;\r\n\
}\r\n\
auto array(int size)\r\n\
{\r\n\
	array ret;\r\n\
	ret.arr = new int[size];\r\n\
	return ret;\r\n\
}\r\n\
auto array:start()\r\n\
{\r\n\
	array_iterator iter;\r\n\
	iter.arr = this;\r\n\
	iter.pos = 0;\r\n\
	return iter;\r\n\
}\r\n\
int ref array_iterator:next()\r\n\
{\r\n\
	return &arr.arr[pos++];\r\n\
}\r\n\
int array_iterator:hasnext()\r\n\
{\r\n\
	return pos < arr.arr.size;\r\n\
}\r\n\
\r\n\
array arr = array(16);\r\n\
int u = 1;\r\n\
for(i in arr)\r\n\
	i = u++;\r\n\
for(i in arr)\r\n\
	i *= i;\r\n\
int sum = 0;\r\n\
for(i in arr)\r\n\
	sum += i;\r\n\
return sum;";
TEST_RESULT("For each on user type", testForEachUserType, "1496");

const char	*testForEach2 =
"import std.vector;\r\n\
auto a = vector<int>();\r\n\
a.push_back(4);\r\n\
a.push_back(8);\r\n\
a.push_back(14);\r\n\
\r\n\
int sum = 0;\r\n\
for(int i in a)\r\n\
{\r\n\
	sum += i;\r\n\
	i *= 2;\r\n\
}\r\n\
int sum2 = 0;\r\n\
for(int i in a)\r\n\
{\r\n\
	sum2 += i;\r\n\
}\r\n\
return sum + sum2;";
TEST_RESULT("For each with specified element type", testForEach2, "78");

const char	*testForEach2b =
"import std.vector;\r\n\
auto a = vector<int>();\r\n\
a.push_back(4);\r\n\
a.push_back(8);\r\n\
a.push_back(14);\r\n\
\r\n\
int sum = 0;\r\n\
for(auto i in a)\r\n\
{\r\n\
	sum += i;\r\n\
	i *= 2;\r\n\
}\r\n\
int sum2 = 0;\r\n\
for(auto i in a)\r\n\
{\r\n\
	sum2 += i;\r\n\
}\r\n\
return sum + sum2;";
TEST_RESULT("For each with specified element type 2", testForEach2b, "78");

const char	*testForEach3 =
"int[] arr1 = { 2, 6, 7 };\r\n\
int sum1 = 0;\r\n\
for(i in arr1)\r\n\
	sum1 += i;\r\n\
\r\n\
int[3] arr2 = { 2, 6, 7 };\r\n\
int sum2 = 0;\r\n\
for(i in arr2)\r\n\
	sum2 += i;\r\n\
\r\n\
auto arr3 = { { 2, 6, 7 }, { 1, 2, 3 } };\r\n\
int sum3 = 0;\r\n\
for(line in arr3)\r\n\
	for(num in line)\r\n\
		sum3 += num;\r\n\
\r\n\
auto func(){ return { 1, 6 }; }\r\n\
int sum4 = 0;\r\n\
for(i in func())\r\n\
	sum4 += i;\r\n\
\r\n\
int sum5 = 0;\r\n\
for(i in { 1, 2, 3 })\r\n\
	sum5 += i;\r\n\
return 0;";
TEST("For each for standard arrays", testForEach3, "0")
{
	CHECK_INT("sum1", 0, 15, lastFailed);
	CHECK_INT("sum2", 0, 15, lastFailed);
	CHECK_INT("sum3", 0, 21, lastFailed);
	CHECK_INT("sum4", 0, 7, lastFailed);
	CHECK_INT("sum5", 0, 6, lastFailed);
}

const char	*testForEach4 =
"int sum = 0;\r\n\
for(i in { 1, 2, 3 }, j in { 4, 5, 6, 7 })\r\n\
	sum += i + j;\r\n\
\r\n\
int[3] arr1 = { 2, 6, 7 };\r\n\
int[] arr2 = { 8, -4, 2 };\r\n\
for(i in arr1, j in arr2)\r\n\
	i += j;\r\n\
return sum;";
TEST("For each with multiple arrays", testForEach4, "21")
{
	CHECK_INT("arr1", 0, 10, lastFailed);
	CHECK_INT("arr1", 1, 2, lastFailed);
	CHECK_INT("arr1", 2, 9, lastFailed);
}

const char	*testRangeIterator =
"class range_iterator\r\n\
{\r\n\
	int pos;\r\n\
	int max;\r\n\
}\r\n\
auto range_iterator:start()\r\n\
{\r\n\
	return *this;\r\n\
}\r\n\
int range_iterator:next()\r\n\
{\r\n\
	pos++;\r\n\
	return pos - 1;\r\n\
}\r\n\
int range_iterator:hasnext()\r\n\
{\r\n\
	return pos != max;\r\n\
}\r\n\
auto range(int min, max)\r\n\
{\r\n\
	range_iterator r;\r\n\
	r.pos = min;\r\n\
	r.max = max + 1;\r\n\
	return r;\r\n\
}\r\n\
int factorial(int v)\r\n\
{\r\n\
	int fact = 1;\r\n\
	for(i in range(1, v))\r\n\
		fact *= i;\r\n\
	return fact;\r\n\
}\r\n\
return factorial(10);";
TEST_RESULT("Extra node wrapping in for each with function call in array part", testRangeIterator, "3628800");

const char	*testForEach5 =
"import std.vector;\r\n\
class Test\r\n\
{\r\n\
	vector<int> a;\r\n\
}\r\n\
auto a = new Test;\r\n\
a.a = vector<int>();\r\n\
a.a.push_back(10);\r\n\
a.a.push_back(105);\r\n\
int sum = 0;\r\n\
for(int i in a.a) sum += i;\r\n\
return sum;";
TEST_RESULT("For each on a member of a type that we had a reference to", testForEach5, "115");

const char	*testIllFormedForEach =
"import std.vector;\r\n\
\r\n\
auto a = vector<vector<int>>(3);\r\n\
a.push_back(vector<int>());\r\n\
a.back().push_back(1);\r\n\
a.back().push_back(2);\r\n\
a.push_back(vector<int>());\r\n\
a.back().push_back(3);\r\n\
a.back().push_back(4);\r\n\
int z = 1;\r\n\
for(vector<int> i in a, int x in i)\r\n\
	z = 0;\r\n\
return z;";
TEST_RESULT("Ill-formed for_each test", testIllFormedForEach, "1");

const char	*testInOperatorOnArray =
"int[] arr = { 1, 2, 3, 4 };\r\n\
return (3 in arr) && !(6 in arr);";
TEST_RESULT("in operator on array", testInOperatorOnArray, "1");
<|endoftext|>


<commit_before>#include "viewnormaleditor.h"
#include "ui_viewnormaleditor.h"
#include "mainwindow.h"
#include "preference.h"
#include "autosizetextedit.h"
#include "twitter.h"
#include "accountlist.h"
#include "postprogress.h"
#include <QMessageBox>

ViewNormalEditor::ViewNormalEditor(QWidget *parent)
    : PageSelectorView(parent)
    , ui(new Ui::ViewNormalEditor)
    , m_preference(nullptr)
    , postProgress(new PostProgress(this))
    , currentAccount(nullptr)
{
    ui->setupUi(this);
    ui->prefixFreeText->setVisible(false);
    ui->prefixContinueText->setVisible(false);
    ui->prefixFinishedText->setVisible(false);
    ui->postfixFreeText->setVisible(false);
    ui->postfixContinueText->setVisible(false);
    ui->postfixFinishedText->setVisible(false);
}

ViewNormalEditor::~ViewNormalEditor()
{
    delete postProgress;
    delete ui;
}

QWidget *ViewNormalEditor::getPostTextWidget(Preference::PostTextType type)
{
    switch (type) {
    case Preference::postTextPrefixFreeText:
        return ui->prefixFreeText;
    case Preference::postTextPrefixContinue:
        ui->prefixContinueText->setText(m_preference->postPrefixContinueText());
        return ui->prefixContinueText;
    case Preference::postTextPrefixFinished:
        ui->prefixFinishedText->setText(m_preference->postPrefixFinishedText());
        return ui->prefixFinishedText;
    case Preference::postTextPostfixFreeText:
        return ui->postfixFreeText;
    case Preference::postTextPostfixContinue:
        ui->postfixContinueText->setText(m_preference->postPostfixContinueText());
        return ui->postfixContinueText;
    case Preference::postTextPostfixFinished:
        ui->postfixFinishedText->setText(m_preference->postPostfixFinishedText());
        return ui->postfixFinishedText;
    default:
        return nullptr;
    }
}

TwitterTextSplitter::TextTypeValue ViewNormalEditor::textTypeValueFromPostType(Preference::PostTextType type)
{
    switch (type) {
    case Preference::postTextPrefixFreeText:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixFreeText,
                    ui->prefixFreeText->toPlainText()
                );
    case Preference::postTextPrefixContinue:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixContinue,
                    m_preference->postPrefixContinueText()
                );
    case Preference::postTextPrefixFinished:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixFinished,
                    m_preference->postPrefixFinishedText()
                );
    case Preference::postTextPostfixFreeText:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixFreeText,
                    ui->postfixFreeText->toPlainText()
                );
    case Preference::postTextPostfixContinue:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixContinue,
                    m_preference->postPostfixContinueText()
                );
    case Preference::postTextPostfixFinished:
            return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixFinished,
                    m_preference->postPostfixFinishedText()
                );
    default:
        return TwitterTextSplitter::TextTypeValue((TwitterTextSplitter::TextType)-1, "");
    }
}

void ViewNormalEditor::setPreference(Preference *preference)
{
    m_preference = preference;

    connect(m_preference, &Preference::update,
            this, &ViewNormalEditor::onPreferenceUpdate);

//    ui->prefix->blockSignals(true);
//    ui->postfix->blockSignals(true);
    onPreferenceUpdate();
//    ui->prefix->blockSignals(false);
//    ui->postfix->blockSignals(false);
}

void ViewNormalEditor::setDocument(PageSelectorDocument *document)
{
    PageSelectorView::setDocument(document);

    currentAccount = nullptr;

    if (EditorPageDocument *document_
            = qobject_cast<EditorPageDocument*>( document )) {
        if (const TwitterAccount *account_
                = qobject_cast<const TwitterAccount*>(document_->account())) {

            currentAccount = account_->twitter();

            connect(currentAccount, &Twitter::tweeted,
                  this, &ViewNormalEditor::onTwitterTweeted);
            connect(currentAccount, &Twitter::tweetFailure,
                  this, &ViewNormalEditor::onTwitterTweetFailure);

            ui->tweetButton->setEnabled(currentAccount->isAuthenticated());
            ui->prefixFreeText->setPlainText("");
            ui->tweetEditor->setPlainText("");
            ui->postfixFreeText->setPlainText("");
            ui->tweetEditor->setFocus();
        }
    }
}

void ViewNormalEditor::updateSplitStatus()
{
    TwitterTextSplitter splitter;
    QString text = ui->tweetEditor->toPlainText();

    QList<TwitterTextSplitter::TextTypeValue> list;

    list.clear();
    foreach (auto type, m_preference->postPrefixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPrefix(list);

    splitter.setText(text);

    list.clear();
    foreach (auto type, m_preference->postPostfixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPostfix(list);

    ui->tweetEditor->setTextSplitter(splitter);

    tweetQueue = splitter.split();

    ui->partitionCount->setText(QString("%1").arg(tweetQueue.size()));
    ui->textLength->setText(QString("%1").arg(text.size()));
    ui->totalLength->setText(QString("%1").arg(splitter.size()));
}

void ViewNormalEditor::startPost()
{
    postProgress->setCount( tweetQueue.size() );
    postProgress->show();
    postProgress->raise();
    postProgress->activateWindow();
}

void ViewNormalEditor::stepPost()
{
    postProgress->step();
}

void ViewNormalEditor::finishPost()
{
    postProgress->step();
    postProgress->hide();
}

void ViewNormalEditor::onPreferenceUpdate()
{
    for (QLayoutItem *child;
         (child = ui->prefixLayout->takeAt(0)) != 0;) {
        child->widget()->setVisible(false);
        delete child;
    }
    foreach (auto type, m_preference->postPrefixText()) {
        QWidget *w = getPostTextWidget(type);
        w->setVisible(true);
        ui->prefixLayout->addWidget(w);
    }

    for (QLayoutItem *child;
         (child = ui->postfixLayout->takeAt(0)) != 0;) {
        child->widget()->setVisible(false);
        delete child;
    }
    foreach (auto type, m_preference->postPostfixText()) {
        QWidget *w = getPostTextWidget(type);
        w->setVisible(true);
        ui->postfixLayout->addWidget(w);
    }

    updateSplitStatus();
}

void ViewNormalEditor::onTwitterTweeted(const QString &tweetId)
{
    qDebug() << "add tweet" << tweetId;

    if (tweetQueue.isEmpty()) {
        finishPost();
        QMessageBox::information(this, qAppName(), "投稿を完了しました。");
        return;
    }

    // まだ残りがあれば投稿する

    stepPost();

    QString tweetText = tweetQueue.front().toString();
    tweetQueue.pop_front();

    currentAccount->tweet(tweetText, tweetId);
}

void ViewNormalEditor::onTwitterTweetFailure(const QList<QPair<int, QString> > & errors)
{
    qDebug() << "error post tweet" << errors;

    QMessageBox::warning(this, qAppName(), "投稿に失敗しました。");

    finishPost();
}

// ツイート投稿
void ViewNormalEditor::on_tweetButton_clicked()
{
    tweetQueue.clear();

    // まずは認証済みかチェック
    if (!currentAccount ||
        !currentAccount->isAuthenticated()) {
        return;
    }

    TwitterTextSplitter splitter;

    QList<TwitterTextSplitter::TextTypeValue> list;

    list.clear();
    foreach (auto type, m_preference->postPrefixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPrefix(list);

    splitter.setText( ui->tweetEditor->toPlainText() );

    list.clear();
    foreach (auto type, m_preference->postPostfixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPostfix(list);

    ui->tweetEditor->setTextSplitter(splitter);

    tweetQueue = splitter.split();
    if (tweetQueue.isEmpty()) {
        return;
    }

    startPost();

#ifndef QT_NO_DEBUG
    QString msg;
    QList<SplittedItem>::iterator i;
    for (i = tweetQueue.begin(); i != tweetQueue.end(); ++i) {
        qDebug() << QString("[%1]\n").arg(i->toString());
        msg += QString("[%1]\n").arg(i->toString());
    }
    QMessageBox::information(this, "", msg);
#endif

    QString tweetText = tweetQueue.front().toString();
    tweetQueue.pop_front();

    currentAccount->tweet(tweetText);
}

void ViewNormalEditor::on_prefixFreeText_textChanged()
{
    updateSplitStatus();
}

void ViewNormalEditor::on_postfixFreeText_textChanged()
{
    updateSplitStatus();
}

void ViewNormalEditor::on_tweetEditor_textChanged()
{
    updateSplitStatus();
}
<commit_msg>デフォルトで設定で指定された値を設定するようにした<commit_after>#include "viewnormaleditor.h"
#include "ui_viewnormaleditor.h"
#include "mainwindow.h"
#include "preference.h"
#include "autosizetextedit.h"
#include "twitter.h"
#include "accountlist.h"
#include "postprogress.h"
#include <QMessageBox>

ViewNormalEditor::ViewNormalEditor(QWidget *parent)
    : PageSelectorView(parent)
    , ui(new Ui::ViewNormalEditor)
    , m_preference(nullptr)
    , postProgress(new PostProgress(this))
    , currentAccount(nullptr)
{
    ui->setupUi(this);
    ui->prefixFreeText->setVisible(false);
    ui->prefixContinueText->setVisible(false);
    ui->prefixFinishedText->setVisible(false);
    ui->postfixFreeText->setVisible(false);
    ui->postfixContinueText->setVisible(false);
    ui->postfixFinishedText->setVisible(false);
}

ViewNormalEditor::~ViewNormalEditor()
{
    delete postProgress;
    delete ui;
}

QWidget *ViewNormalEditor::getPostTextWidget(Preference::PostTextType type)
{
    switch (type) {
    case Preference::postTextPrefixFreeText:
        return ui->prefixFreeText;
    case Preference::postTextPrefixContinue:
        ui->prefixContinueText->setText(m_preference->postPrefixContinueText());
        return ui->prefixContinueText;
    case Preference::postTextPrefixFinished:
        ui->prefixFinishedText->setText(m_preference->postPrefixFinishedText());
        return ui->prefixFinishedText;
    case Preference::postTextPostfixFreeText:
        return ui->postfixFreeText;
    case Preference::postTextPostfixContinue:
        ui->postfixContinueText->setText(m_preference->postPostfixContinueText());
        return ui->postfixContinueText;
    case Preference::postTextPostfixFinished:
        ui->postfixFinishedText->setText(m_preference->postPostfixFinishedText());
        return ui->postfixFinishedText;
    default:
        return nullptr;
    }
}

TwitterTextSplitter::TextTypeValue ViewNormalEditor::textTypeValueFromPostType(Preference::PostTextType type)
{
    switch (type) {
    case Preference::postTextPrefixFreeText:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixFreeText,
                    ui->prefixFreeText->toPlainText()
                );
    case Preference::postTextPrefixContinue:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixContinue,
                    m_preference->postPrefixContinueText()
                );
    case Preference::postTextPrefixFinished:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPrefixFinished,
                    m_preference->postPrefixFinishedText()
                );
    case Preference::postTextPostfixFreeText:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixFreeText,
                    ui->postfixFreeText->toPlainText()
                );
    case Preference::postTextPostfixContinue:
        return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixContinue,
                    m_preference->postPostfixContinueText()
                );
    case Preference::postTextPostfixFinished:
            return TwitterTextSplitter::TextTypeValue(
                    TwitterTextSplitter::textPostfixFinished,
                    m_preference->postPostfixFinishedText()
                );
    default:
        return TwitterTextSplitter::TextTypeValue((TwitterTextSplitter::TextType)-1, "");
    }
}

void ViewNormalEditor::setPreference(Preference *preference)
{
    m_preference = preference;

    connect(m_preference, &Preference::update,
            this, &ViewNormalEditor::onPreferenceUpdate);

//    ui->prefix->blockSignals(true);
//    ui->postfix->blockSignals(true);
    onPreferenceUpdate();
//    ui->prefix->blockSignals(false);
//    ui->postfix->blockSignals(false);
}

void ViewNormalEditor::setDocument(PageSelectorDocument *document)
{
    PageSelectorView::setDocument(document);

    currentAccount = nullptr;

    if (EditorPageDocument *document_
            = qobject_cast<EditorPageDocument*>( document )) {
        if (const TwitterAccount *account_
                = qobject_cast<const TwitterAccount*>(document_->account())) {

            currentAccount = account_->twitter();

            connect(currentAccount, &Twitter::tweeted,
                  this, &ViewNormalEditor::onTwitterTweeted);
            connect(currentAccount, &Twitter::tweetFailure,
                  this, &ViewNormalEditor::onTwitterTweetFailure);

            ui->tweetButton->setEnabled(currentAccount->isAuthenticated());
            ui->prefixFreeText->setPlainText(m_preference->postPrefixFreeText());
            ui->tweetEditor->setPlainText("");
            ui->postfixFreeText->setPlainText(m_preference->postPostfixFreeText());
            ui->tweetEditor->setFocus();
        }
    }
}

void ViewNormalEditor::updateSplitStatus()
{
    TwitterTextSplitter splitter;
    QString text = ui->tweetEditor->toPlainText();

    QList<TwitterTextSplitter::TextTypeValue> list;

    list.clear();
    foreach (auto type, m_preference->postPrefixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPrefix(list);

    splitter.setText(text);

    list.clear();
    foreach (auto type, m_preference->postPostfixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPostfix(list);

    ui->tweetEditor->setTextSplitter(splitter);

    tweetQueue = splitter.split();

    ui->partitionCount->setText(QString("%1").arg(tweetQueue.size()));
    ui->textLength->setText(QString("%1").arg(text.size()));
    ui->totalLength->setText(QString("%1").arg(splitter.size()));
}

void ViewNormalEditor::startPost()
{
    postProgress->setCount( tweetQueue.size() );
    postProgress->show();
    postProgress->raise();
    postProgress->activateWindow();
}

void ViewNormalEditor::stepPost()
{
    postProgress->step();
}

void ViewNormalEditor::finishPost()
{
    postProgress->step();
    postProgress->hide();
}

void ViewNormalEditor::onPreferenceUpdate()
{
    for (QLayoutItem *child;
         (child = ui->prefixLayout->takeAt(0)) != 0;) {
        child->widget()->setVisible(false);
        delete child;
    }
    foreach (auto type, m_preference->postPrefixText()) {
        QWidget *w = getPostTextWidget(type);
        w->setVisible(true);
        ui->prefixLayout->addWidget(w);
    }

    for (QLayoutItem *child;
         (child = ui->postfixLayout->takeAt(0)) != 0;) {
        child->widget()->setVisible(false);
        delete child;
    }
    foreach (auto type, m_preference->postPostfixText()) {
        QWidget *w = getPostTextWidget(type);
        w->setVisible(true);
        ui->postfixLayout->addWidget(w);
    }

    updateSplitStatus();
}

void ViewNormalEditor::onTwitterTweeted(const QString &tweetId)
{
    qDebug() << "add tweet" << tweetId;

    if (tweetQueue.isEmpty()) {
        finishPost();
        QMessageBox::information(this, qAppName(), "投稿を完了しました。");
        return;
    }

    // まだ残りがあれば投稿する

    stepPost();

    QString tweetText = tweetQueue.front().toString();
    tweetQueue.pop_front();

    currentAccount->tweet(tweetText, tweetId);
}

void ViewNormalEditor::onTwitterTweetFailure(const QList<QPair<int, QString> > & errors)
{
    qDebug() << "error post tweet" << errors;

    QMessageBox::warning(this, qAppName(), "投稿に失敗しました。");

    finishPost();
}

// ツイート投稿
void ViewNormalEditor::on_tweetButton_clicked()
{
    tweetQueue.clear();

    // まずは認証済みかチェック
    if (!currentAccount ||
        !currentAccount->isAuthenticated()) {
        return;
    }

    TwitterTextSplitter splitter;

    QList<TwitterTextSplitter::TextTypeValue> list;

    list.clear();
    foreach (auto type, m_preference->postPrefixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPrefix(list);

    splitter.setText( ui->tweetEditor->toPlainText() );

    list.clear();
    foreach (auto type, m_preference->postPostfixText()) {
        list.append(textTypeValueFromPostType(type));
    }
    splitter.setPostfix(list);

    ui->tweetEditor->setTextSplitter(splitter);

    tweetQueue = splitter.split();
    if (tweetQueue.isEmpty()) {
        return;
    }

    startPost();

#ifndef QT_NO_DEBUG
    QString msg;
    QList<SplittedItem>::iterator i;
    for (i = tweetQueue.begin(); i != tweetQueue.end(); ++i) {
        qDebug() << QString("[%1]\n").arg(i->toString());
        msg += QString("[%1]\n").arg(i->toString());
    }
    QMessageBox::information(this, "", msg);
#endif

    QString tweetText = tweetQueue.front().toString();
    tweetQueue.pop_front();

    currentAccount->tweet(tweetText);
}

void ViewNormalEditor::on_prefixFreeText_textChanged()
{
    updateSplitStatus();
}

void ViewNormalEditor::on_postfixFreeText_textChanged()
{
    updateSplitStatus();
}

void ViewNormalEditor::on_tweetEditor_textChanged()
{
    updateSplitStatus();
}
<|endoftext|>


<commit_before>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */

#include "pagefactory.h"

#include <DcpPage>
#include <DcpMainPage>
#include <DcpAppletPage>
#include <DcpAppletDb>
#include <DcpAppletIf>
#include <DcpAppletMetadata>
#include <DcpAppletCategoryPage>
#include <MainTranslations>

#include <DuiApplication>
#include <DuiAction>

#define DEBUG
#include "dcpdebug.h"

PageFactory *PageFactory::sm_Instance = 0;

PageFactory::PageFactory ():
    QObject (),
    m_CurrentPage (0),
    m_MainPage (0),
    m_AppletPage (0),
    m_AppletCategoryPage (0)
{
    DcpAppletMetadataList list;

    /*
     * Whenever someone wants an applet to be activated (started, shown on the
     * screen atc.) a signal is sent to the applet's metadata. Then the applets
     * metadata will send a signal, so the pagefactory can open a new page for
     * it.
     *
     * If an applet wants to start up an other applet we also got a signal here
     * so we can start up the applet. In this case we will have an external
     * referer for the applets main page.
     */
    list = DcpAppletDb::instance()->list();
    foreach (DcpAppletMetadata *item, list) {
        DCP_DEBUG ("*** applet '%s'", DCP_STR (item->name()));
        connect (item, SIGNAL (activate ()),
                this, SLOT (appletWantsToStart ()));
        connect (item, SIGNAL (activateWithReferer (const QString &, int)),
                this, SLOT (appletWantsToStartWithReferer (const QString &, int)));
    }
}

PageFactory *
PageFactory::instance ()
{
    if (!sm_Instance)
        sm_Instance = new PageFactory();
    return sm_Instance;
}

DcpPage*
PageFactory::create (
        const PageHandle &handle)
{
    DcpPage *page = 0;

    DCP_DEBUG ("Creating page %s", DCP_STR (handle.getStringVariant()));
    switch (handle.id) {
        case PageHandle::NOPAGE:
	    case PageHandle::MAIN:
            /*
             * Creating the main page. If the ID is is PageHandle::NOPAGE we
             * also create the main page, for which we can handle pages that has
             * no valid referer. These pages are going to open the main page
             * when closed.
             */
            DCP_DEBUG ("## MAIN ##");
            page = createMainPage();
            break;

        case PageHandle::APPLETCATEGORY:
            // when coming back
            DCP_DEBUG ("## APPLETCATEGORY ##");
            Q_ASSERT (m_AppletCategoryPage);
            page = m_AppletCategoryPage;
            break;

        case PageHandle::APPLET:
            DCP_DEBUG ("## APPLET ##");
            page = createAppletPage (
			    DcpAppletDb::instance()->applet (handle.param));
            break;

        default:
            DCP_DEBUG ("## CATEGORY_PAGEID_XXX ##");
            Q_ASSERT(handle.id > PageHandle::CATEGORY_PAGEID_START
                     && handle.id < PageHandle::CATEGORY_PAGEID_END);
            page = createAppletCategoryPage (handle.id);
            if (page)
                page->setHandle (handle);
    }

    if (page) {
        page->setHandle (handle);

        if (m_CurrentPage)
            m_CurrentPage->disconnectSignals ();

        page->connectSignals ();

		if (page->isContentCreated())
            page->reload();

        m_CurrentPage = page;
    }

    return page;
}

DcpPage* PageFactory::createMainPage ()
{
    if (!m_MainPage) {
        m_MainPage = new DcpMainPage ();
        initPage (m_MainPage);
    }

    return m_MainPage;
}

DcpPage *
PageFactory::createAppletPage (
		DcpAppletMetadata *metadata)
{
    if (!m_AppletPage) {
        m_AppletPage = new DcpAppletPage(metadata);
        initPage (m_AppletPage);
    } else {
        m_AppletPage->setMetadata (metadata);
    }

    // page has to be loaded to know if the applet provides page or not
    if (m_AppletPage->isContentCreated ()) {
        m_AppletPage->reload ();
    } else {
        m_AppletPage->createContent ();
    }

    if (!m_AppletPage->hasWidget() && !m_AppletPage->hasError()) {
        // the applet does not provide a page (eg. just a dialog)
        return 0;
    } else {
        return m_AppletPage;
    }
}

DcpPage *
PageFactory::createAppletCategoryPage (
        PageHandle::PageTypeId id)
{
    const DcpCategoryInfo *info;
    int                    n;

    DCP_DEBUG ("*** id = %d", (int) id);

    for (n = 0; ; ++n) {
        info = & DcpMain::CategoryInfos[n];

        if (info->titleId == 0) {
            DCP_DEBUG ("Category info for page type %d not found.", (int) id);
            return 0;
        }

        if (info->subPageId == id)
            break;
    }

    if (!m_AppletCategoryPage){
        m_AppletCategoryPage = new DcpAppletCategoryPage (info->appletCategory);
        initPage (m_AppletCategoryPage);
    } else {
        m_AppletCategoryPage->setAppletCategory (info->appletCategory);
    }

    m_AppletCategoryPage->setTitle (qtTrId (info->titleId));

    return m_AppletCategoryPage;
}

void
PageFactory::changePage (
        PageHandle handle)
{
    DcpPage *page = create (handle);

    DCP_DEBUG ("*** handle = %s", DCP_STR (handle.getStringVariant ()));
    if (page) {
        page->appear (DuiSceneWindow::KeepWhenDone);
    }
}

void
PageFactory::changePageWithReferer (
        const PageHandle     &handle,
        const QString        &refererName,
        int                   refererWidgetId)
{
    DCP_DEBUG ("Creating page '%s'/%d for referer '%s'/%d",
            DCP_STR (handle.param), handle.id,
            DCP_STR (refererName), refererWidgetId);

    DcpPage *page = create (handle);

    if (page) {
        PageHandle referer (PageHandle::APPLET,
                refererName, refererWidgetId);

        page->setReferer (referer);
        page->appearNow (DuiSceneWindow::KeepWhenDone);
    }
}

/*
 * FIXME: the PageFactory::appletWantsToStart and the
 * PageFactory::appletWantsToStartWithReferer could be implemented with default
 * parameters or something.
 */
void
PageFactory::appletWantsToStart ()
{
    DcpAppletMetadata *metadata = qobject_cast<DcpAppletMetadata *> (sender());

    Q_ASSERT (metadata != 0);
    DCP_DEBUG ("Applet '%s' wants to start.", DCP_STR(metadata->name()));
    PageHandle handle (
            PageHandle::APPLET,
            metadata->name(),
            metadata->getMainWidgetId ());

    changePage (handle);
}

void
PageFactory::appletWantsToStartWithReferer (
        const QString &refererName,
        int            refererWidgetId)
{
    DcpAppletMetadata *metadata = qobject_cast<DcpAppletMetadata *> (sender());

    Q_ASSERT (metadata != 0);
    DCP_DEBUG ("Applet '%s' wants to start.", DCP_STR(metadata->name()));
    PageHandle handle (
            PageHandle::APPLET,
            metadata->name(),
            metadata->getMainWidgetId ());

    changePageWithReferer (handle, refererName, refererWidgetId);
}

void
PageFactory::initPage (
        DcpPage* page)
{
    connect (page, SIGNAL(openSubPage (PageHandle)),
            this, SLOT(changePage(PageHandle)));

    connect (
            page,
            SIGNAL (openSubPageWithReferer(const PageHandle &, const QString &, int)),
            this,
            SLOT (changePageWithReferer(const PageHandle &, const QString &, int)));

    if (page != m_MainPage) {
        // closeAction TODO XXX on language change, move into to the page?
        DuiAction *quitAction = new DuiAction(qtTrId(DcpMain::quitMenuItemTextId), page);
        quitAction->setLocation((DuiAction::Location)4 /* FIXME DuiAction::ApplicationMenu */);

        connect(quitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));

        // Add actions to page
        page->addAction(quitAction);
    }
}

<commit_msg>The first applet page comes back bug fixed.<commit_after>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */

#include "pagefactory.h"

#include <DcpPage>
#include <DcpMainPage>
#include <DcpAppletPage>
#include <DcpAppletDb>
#include <DcpAppletIf>
#include <DcpAppletMetadata>
#include <DcpAppletCategoryPage>
#include <MainTranslations>

#include <DuiApplication>
#include <DuiAction>

#define DEBUG
#include "dcpdebug.h"

PageFactory *PageFactory::sm_Instance = 0;

PageFactory::PageFactory ():
    QObject (),
    m_CurrentPage (0),
    m_MainPage (0),
    m_AppletPage (0),
    m_AppletCategoryPage (0)
{
    DcpAppletMetadataList list;

    /*
     * Whenever someone wants an applet to be activated (started, shown on the
     * screen atc.) a signal is sent to the applet's metadata. Then the applets
     * metadata will send a signal, so the pagefactory can open a new page for
     * it.
     *
     * If an applet wants to start up an other applet we also got a signal here
     * so we can start up the applet. In this case we will have an external
     * referer for the applets main page.
     */
    list = DcpAppletDb::instance()->list();
    foreach (DcpAppletMetadata *item, list) {
        DCP_DEBUG ("*** applet '%s'", DCP_STR (item->name()));
        connect (item, SIGNAL (activate ()),
                this, SLOT (appletWantsToStart ()));
        connect (item, SIGNAL (activateWithReferer (const QString &, int)),
                this, SLOT (appletWantsToStartWithReferer (const QString &, int)));
    }
}

PageFactory *
PageFactory::instance ()
{
    if (!sm_Instance)
        sm_Instance = new PageFactory();
    return sm_Instance;
}

DcpPage*
PageFactory::create (
        const PageHandle &handle)
{
    DcpPage *page = 0;

    DCP_DEBUG ("Creating page %s", DCP_STR (handle.getStringVariant()));
    switch (handle.id) {
        case PageHandle::NOPAGE:
	    case PageHandle::MAIN:
            /*
             * Creating the main page. If the ID is is PageHandle::NOPAGE we
             * also create the main page, for which we can handle pages that has
             * no valid referer. These pages are going to open the main page
             * when closed.
             */
            DCP_DEBUG ("## MAIN ##");
            page = createMainPage();
            break;

        case PageHandle::APPLETCATEGORY:
            // when coming back
            DCP_DEBUG ("## APPLETCATEGORY ##");
            Q_ASSERT (m_AppletCategoryPage);
            page = m_AppletCategoryPage;
            break;

        case PageHandle::APPLET:
            DCP_DEBUG ("## APPLET ##");
            page = createAppletPage (
			    DcpAppletDb::instance()->applet (handle.param));
            break;

        default:
            DCP_DEBUG ("## CATEGORY_PAGEID_XXX ##");
            Q_ASSERT(handle.id > PageHandle::CATEGORY_PAGEID_START
                     && handle.id < PageHandle::CATEGORY_PAGEID_END);
            page = createAppletCategoryPage (handle.id);
    }

    if (page) {
        page->setHandle (handle);

        if (m_CurrentPage)
            m_CurrentPage->disconnectSignals ();

        page->connectSignals ();

		if (page->isContentCreated())
            page->reload();

        m_CurrentPage = page;
    }

    return page;
}

DcpPage* PageFactory::createMainPage ()
{
    if (!m_MainPage) {
        m_MainPage = new DcpMainPage ();
        initPage (m_MainPage);
    }

    return m_MainPage;
}

DcpPage *
PageFactory::createAppletPage (
		DcpAppletMetadata *metadata)
{
    /*
     * I found a serious bug some change of the code triggered. When activating
     * the second applet the old page came up. I managed to fix the bug with
     * this change, but then we will not re-use the applet page. I will find a
     * better solution, but until I do this change will fix the bug.
     */
#if 0
    if (!m_AppletPage) {
        m_AppletPage = new DcpAppletPage(metadata);
        initPage (m_AppletPage);
    } else {
        m_AppletPage->setMetadata (metadata);
    }
#else
    if (m_AppletPage)
        delete m_AppletPage;

    m_AppletPage = new DcpAppletPage (metadata);
    initPage (m_AppletPage);
#endif

    // page has to be loaded to know if the applet provides page or not
    if (m_AppletPage->isContentCreated ()) {
        m_AppletPage->reload ();
    } else {
        m_AppletPage->createContent ();
    }

    if (!m_AppletPage->hasWidget() && !m_AppletPage->hasError()) {
        // the applet does not provide a page (eg. just a dialog)
        return 0;
    } else {
        return m_AppletPage;
    }
}

DcpPage *
PageFactory::createAppletCategoryPage (
        PageHandle::PageTypeId id)
{
    const DcpCategoryInfo *info;
    int                    n;

    DCP_DEBUG ("*** id = %d", (int) id);

    for (n = 0; ; ++n) {
        info = & DcpMain::CategoryInfos[n];

        if (info->titleId == 0) {
            DCP_DEBUG ("Category info for page type %d not found.", (int) id);
            return 0;
        }

        if (info->subPageId == id)
            break;
    }

    if (!m_AppletCategoryPage){
        m_AppletCategoryPage = new DcpAppletCategoryPage (info->appletCategory);
        initPage (m_AppletCategoryPage);
    } else {
        m_AppletCategoryPage->setAppletCategory (info->appletCategory);
    }

    m_AppletCategoryPage->setTitle (qtTrId (info->titleId));

    return m_AppletCategoryPage;
}

void
PageFactory::changePage (
        PageHandle handle)
{
    DcpPage *page = create (handle);

    DCP_DEBUG ("*** handle = %s", DCP_STR (handle.getStringVariant ()));
    if (page) {
        page->appear (DuiSceneWindow::KeepWhenDone);
    }
}

void
PageFactory::changePageWithReferer (
        const PageHandle     &handle,
        const QString        &refererName,
        int                   refererWidgetId)
{
    DCP_DEBUG ("Creating page '%s'/%d for referer '%s'/%d",
            DCP_STR (handle.param), handle.id,
            DCP_STR (refererName), refererWidgetId);

    DcpPage *page = create (handle);

    if (page) {
        PageHandle referer (PageHandle::APPLET,
                refererName, refererWidgetId);

        page->setReferer (referer);
        page->appearNow (DuiSceneWindow::KeepWhenDone);
    }
}

/*
 * FIXME: the PageFactory::appletWantsToStart and the
 * PageFactory::appletWantsToStartWithReferer could be implemented with default
 * parameters or something.
 */
void
PageFactory::appletWantsToStart ()
{
    DcpAppletMetadata *metadata = qobject_cast<DcpAppletMetadata *> (sender());

    Q_ASSERT (metadata != 0);
    DCP_DEBUG ("Applet '%s' wants to start.", DCP_STR(metadata->name()));
    PageHandle handle (
            PageHandle::APPLET,
            metadata->name(),
            metadata->getMainWidgetId ());

    changePage (handle);
}

void
PageFactory::appletWantsToStartWithReferer (
        const QString &refererName,
        int            refererWidgetId)
{
    DcpAppletMetadata *metadata = qobject_cast<DcpAppletMetadata *> (sender());

    Q_ASSERT (metadata != 0);
    DCP_DEBUG ("Applet '%s' wants to start.", DCP_STR(metadata->name()));
    PageHandle handle (
            PageHandle::APPLET,
            metadata->name(),
            metadata->getMainWidgetId ());

    changePageWithReferer (handle, refererName, refererWidgetId);
}

void
PageFactory::initPage (
        DcpPage* page)
{
    connect (page, SIGNAL(openSubPage (PageHandle)),
            this, SLOT(changePage(PageHandle)));

    connect (
            page,
            SIGNAL (openSubPageWithReferer(const PageHandle &, const QString &, int)),
            this,
            SLOT (changePageWithReferer(const PageHandle &, const QString &, int)));

    if (page != m_MainPage) {
        // closeAction TODO XXX on language change, move into to the page?
        DuiAction *quitAction = new DuiAction (
                qtTrId(DcpMain::quitMenuItemTextId), page);
        /* FIXME DuiAction::ApplicationMenu */
        quitAction->setLocation((DuiAction::Location) 4);

        connect(quitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));

        // Add actions to page
        page->addAction(quitAction);
    }
}

<|endoftext|>