<commit_before>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file linuxJoystickDevice.cxx
 * @author rdb
 * @date 2015-08-21
 */

#include "linuxJoystickDevice.h"
#include "evdevInputDevice.h"

#ifdef PHAVE_LINUX_INPUT_H

#include "gamepadButton.h"

#include <fcntl.h>
#include <linux/joystick.h>

TypeHandle LinuxJoystickDevice::_type_handle;

/**
 * Creates a new device using the Linux joystick device with the given index.
 */
LinuxJoystickDevice::
LinuxJoystickDevice(int index) :
  _fd(-1),
  _index(index),
  _dpad_x_axis(-1),
  _dpad_y_axis(-1),
  _dpad_left_button(-1),
  _dpad_up_button(-1),
  _ltrigger_button(-1),
  _rtrigger_button(-1)
{
  LightMutexHolder holder(_lock);
  if (!open_device()) {
    device_cat.error()
      << "Could not open joystick device /dev/input/js" << index
      << ": " << strerror(errno) << "\n";
  }
}

/**
 *
 */
LinuxJoystickDevice::
~LinuxJoystickDevice() {
  if (_fd != -1) {
    close(_fd);
    _fd = -1;
  }
}

/**
 * Returns true if there are pending events.
 */
bool LinuxJoystickDevice::
check_events() const {
  unsigned int avail = 0;
  ioctl(_fd, FIONREAD, &avail);
  return (avail != 0);
}

/**
 * Polls the input device for new activity, to ensure it contains the latest
 * events.  This will only have any effect for some types of input devices;
 * others may be updated automatically, and this method will be a no-op.
 */
void LinuxJoystickDevice::
do_poll() {
  if (_fd != -1 && process_events()) {
    while (process_events()) {}

    // If we got events, we are obviously connected.  Mark us so.
    if (!_is_connected) {
      _is_connected = true;
      InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
      mgr->add_device(this);
    }
  }
}

/**
 * Opens or reopens the joystick device, and reads out the button and axis
 * mappings.  Assumes the lock is already held.
 */
bool LinuxJoystickDevice::
open_device() {
  nassertr(_lock.debug_is_locked(), false);

  char path[64];
  sprintf(path, "/dev/input/js%d", _index);

  _fd = open(path, O_RDONLY | O_NONBLOCK);

  if (_fd == -1) {
    _is_connected = false;
    return false;
  }

  // Read the name from the device.  We'll later try to use sysfs to read
  // the proper product name from the device, but this is a good fallback.
  char name[128];
  name[0] = 0;
  ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
  _name = name;

  bool have_analog_triggers = false;

  // Get the number of axes.
  uint8_t num_axes = 0, num_buttons = 0;
  ioctl(_fd, JSIOCGAXES, &num_axes);
  ioctl(_fd, JSIOCGBUTTONS, &num_buttons);

  _buttons.resize(num_buttons);
  _controls.resize(num_axes);

  if (num_buttons > 0) {
    uint16_t btnmap[512];
    ioctl(_fd, JSIOCGBTNMAP, btnmap);

    for (uint8_t i = 0; i < num_buttons; ++i) {
      ButtonHandle handle = EvdevInputDevice::map_button(btnmap[i]);
      if (handle == ButtonHandle::none()) {
        if (device_cat.is_debug()) {
          device_cat.debug() << "Unmapped /dev/input/js" << _index
            << " button " << (int)i << ": 0x" << hex << btnmap[i] << "\n";
        }
      } else if (handle == GamepadButton::action_a()) {
        _device_class = DC_gamepad;
      } else if (handle == GamepadButton::ltrigger()) {
        _ltrigger_button = i;
      } else if (handle == GamepadButton::rtrigger()) {
        _rtrigger_button = i;
      }
      _buttons[i].handle = handle;
    }
  }

  if (num_axes > 0) {
    uint8_t axmap[512];
    ioctl(_fd, JSIOCGAXMAP, axmap);

    for (uint8_t i = 0; i < num_axes; ++i) {
      ControlAxis axis = C_none;

      switch (axmap[i]) {
      case ABS_X:
        if (_device_class == DC_gamepad) {
          axis = C_left_x;
        } else {
          axis = C_x;
        }
        break;

      case ABS_Y:
        if (_device_class == DC_gamepad) {
          axis = C_left_y;
        } else {
          axis = C_y;
        }
        break;

      case ABS_Z:
        if (_device_class == DC_gamepad) {
          axis = C_left_trigger;
        } else {
          //axis = C_trigger;
        }
        break;

      case ABS_RX:
        axis = C_right_x;
        break;

      case ABS_RY:
        axis = C_right_y;
        break;

      case ABS_RZ:
        axis = C_right_trigger;
        break;

      case ABS_THROTTLE:
        axis = InputDevice::C_throttle;
        break;

      case ABS_RUDDER:
        axis = InputDevice::C_rudder;
        break;

      case ABS_WHEEL:
        axis = InputDevice::C_wheel;
        break;

      case ABS_GAS:
        axis = InputDevice::C_accelerator;
        break;

      case ABS_BRAKE:
        axis = InputDevice::C_brake;
        break;

      case ABS_HAT0X:
        if (_dpad_left_button == -1) {
          // Emulate D-Pad or hat switch.
          _dpad_x_axis = i;
          _dpad_left_button = (int)_buttons.size();
          if (_device_class == DC_gamepad) {
            _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
            _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
          } else {
            _buttons.push_back(ButtonState(GamepadButton::hat_left()));
            _buttons.push_back(ButtonState(GamepadButton::hat_right()));
          }
          axis = C_none;
        }
        break;

      case ABS_HAT0Y:
        if (_dpad_up_button == -1) {
          // Emulate D-Pad.
          _dpad_y_axis = i;
          _dpad_up_button = (int)_buttons.size();
          if (_device_class == DC_gamepad) {
            _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
            _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
          } else {
            _buttons.push_back(ButtonState(GamepadButton::hat_up()));
            _buttons.push_back(ButtonState(GamepadButton::hat_down()));
          }
          axis = C_none;
        }
        break;

      default:
        if (device_cat.is_debug()) {
          device_cat.debug() << "Unmapped /dev/input/js" << _index
            << " axis " << (int)i << ": 0x" << hex << (int)axmap[i] << "\n";
        }
        axis = C_none;
        break;
      }
      _controls[i].axis = axis;

      if (axis == C_left_trigger || axis == C_right_trigger) {
        // We'd like to use 0.0 to indicate the resting position.
        _controls[i]._scale = 1.0 / 65534.0;
        _controls[i]._bias = 0.5;
        have_analog_triggers = true;
      } else if (axis == C_left_y || axis == C_right_y || axis == C_y) {
        _controls[i]._scale = 1.0 / -32767.0;
        _controls[i]._bias = 0.0;
      } else {
        _controls[i]._scale = 1.0 / 32767.0;
        _controls[i]._bias = 0.0;
      }
    }
  }

  if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
    // Emulate analog triggers.
    _ltrigger_control = (int)_controls.size();
    add_control(C_left_trigger, 0, 1, false);
    add_control(C_right_trigger, 0, 1, false);
  } else {
    _ltrigger_button = -1;
    _rtrigger_button = -1;
  }

  // Get additional information from sysfs.
  sprintf(path, "/sys/class/input/js%d/device/id/vendor", _index);
  FILE *f = fopen(path, "r");
  if (f) {
    if (fscanf(f, "%hx", &_vendor_id) < 1) {
      _vendor_id = 0;
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/id/product", _index);
  f = fopen(path, "r");
  if (f) {
    if (fscanf(f, "%hx", &_product_id) < 1) {
      _product_id = 0;
    }
    fclose(f);
  }
  char buffer[256];
  sprintf(path, "/sys/class/input/js%d/device/device/../product", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      if (buffer[0] != 0) {
        _name.assign(buffer);
      }
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/device/../manufacturer", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      _manufacturer.assign(buffer);
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/device/../serial", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      _serial_number.assign(buffer);
    }
    fclose(f);
  }

  // Read the init events.
  while (process_events()) {};

  // Special case handling for the wireless Xbox receiver - the Linux
  // joystick API doesn't seem to have a way to report whether the device
  // is actually turned on.  The best we can do is check whether the axes
  // are all 0, which indicates that the driver hasn't received any data for
  // this gamepad yet (which means it hasn't been plugged in for this session)
  if (strncmp(name, "Xbox 360 Wireless Receiver", 26) == 0) {
    for (int i = 0; i < _controls.size(); ++i) {
      if (_controls[i].state != 0.0) {
        _is_connected = true;
        return true;
      }
    }
    _is_connected = false;
  } else {
    _is_connected = true;
  }

  return true;
}

/**
 * Reads a number of events from the device.  Returns true if events were
 * read, meaning this function should keep being called until it returns
 * false.
 */
bool LinuxJoystickDevice::
process_events() {
  // Read 8 events at a time.
  struct js_event events[8];

  int n_read = read(_fd, events, sizeof(events));
  if (n_read < 0) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      // No data available for now.

    } else if (errno == ENODEV) {
      // The device ceased to exist, so we better close it.  No need
      // to worry about removing it from the InputDeviceManager, as it
      // will get an inotify event sooner or later about this.
      close(_fd);
      _fd = -1;
      //_is_connected = false;
      errno = 0;

    } else {
      device_cat.error() << "read: " << strerror(errno) << "\n";
    }
    return false;
  }

  if (n_read == 0) {
    return false;
  }

  n_read /= sizeof(struct js_event);

  for (int i = 0; i < n_read; ++i) {
    int index = events[i].number;

    if (events[i].type & JS_EVENT_BUTTON) {
      if (index == _ltrigger_button) {
        control_changed(_ltrigger_control, events[i].value);
      } else if (index == _rtrigger_button) {
        control_changed(_ltrigger_control + 1, events[i].value);
      }
      button_changed(index, (events[i].value != 0));

    } else if (events[i].type & JS_EVENT_AXIS) {
      if (index == _dpad_x_axis) {
        button_changed(_dpad_left_button, events[i].value < -1000);
        button_changed(_dpad_left_button+1, events[i].value > 1000);
      } else if (index == _dpad_y_axis) {
        button_changed(_dpad_up_button, events[i].value < -1000);
        button_changed(_dpad_up_button+1, events[i].value > 1000);
      }

      control_changed(index, events[i].value);
    }
  }

  return true;
}

#endif
<commit_msg>input: fixes for joysticks in Linux compatibility mode<commit_after>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file linuxJoystickDevice.cxx
 * @author rdb
 * @date 2015-08-21
 */

#include "linuxJoystickDevice.h"
#include "evdevInputDevice.h"

#ifdef PHAVE_LINUX_INPUT_H

#include "gamepadButton.h"

#include <fcntl.h>
#include <linux/joystick.h>

TypeHandle LinuxJoystickDevice::_type_handle;

/**
 * Creates a new device using the Linux joystick device with the given index.
 */
LinuxJoystickDevice::
LinuxJoystickDevice(int index) :
  _fd(-1),
  _index(index),
  _dpad_x_axis(-1),
  _dpad_y_axis(-1),
  _dpad_left_button(-1),
  _dpad_up_button(-1),
  _ltrigger_button(-1),
  _rtrigger_button(-1)
{
  LightMutexHolder holder(_lock);
  if (!open_device()) {
    device_cat.error()
      << "Could not open joystick device /dev/input/js" << index
      << ": " << strerror(errno) << "\n";
  }
}

/**
 *
 */
LinuxJoystickDevice::
~LinuxJoystickDevice() {
  if (_fd != -1) {
    close(_fd);
    _fd = -1;
  }
}

/**
 * Returns true if there are pending events.
 */
bool LinuxJoystickDevice::
check_events() const {
  unsigned int avail = 0;
  ioctl(_fd, FIONREAD, &avail);
  return (avail != 0);
}

/**
 * Polls the input device for new activity, to ensure it contains the latest
 * events.  This will only have any effect for some types of input devices;
 * others may be updated automatically, and this method will be a no-op.
 */
void LinuxJoystickDevice::
do_poll() {
  if (_fd != -1 && process_events()) {
    while (process_events()) {}

    // If we got events, we are obviously connected.  Mark us so.
    if (!_is_connected) {
      _is_connected = true;
      InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
      mgr->add_device(this);
    }
  }
}

/**
 * Opens or reopens the joystick device, and reads out the button and axis
 * mappings.  Assumes the lock is already held.
 */
bool LinuxJoystickDevice::
open_device() {
  nassertr(_lock.debug_is_locked(), false);

  char path[64];
  sprintf(path, "/dev/input/js%d", _index);

  _fd = open(path, O_RDONLY | O_NONBLOCK);

  if (_fd == -1) {
    _is_connected = false;
    return false;
  }

  // Read the name from the device.  We'll later try to use sysfs to read
  // the proper product name from the device, but this is a good fallback.
  char name[128];
  name[0] = 0;
  ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
  _name = name;

  bool have_analog_triggers = false;

  // Get the number of axes.
  uint8_t num_axes = 0, num_buttons = 0;
  ioctl(_fd, JSIOCGAXES, &num_axes);
  ioctl(_fd, JSIOCGBUTTONS, &num_buttons);

  _buttons.resize(num_buttons);
  _controls.resize(num_axes);

  if (num_buttons > 0) {
    uint16_t btnmap[512];
    ioctl(_fd, JSIOCGBTNMAP, btnmap);

    for (uint8_t i = 0; i < num_buttons; ++i) {
      ButtonHandle handle = EvdevInputDevice::map_button(btnmap[i]);
      if (handle == ButtonHandle::none()) {
        if (device_cat.is_debug()) {
          device_cat.debug() << "Unmapped /dev/input/js" << _index
            << " button " << (int)i << ": 0x" << hex << btnmap[i] << "\n";
        }
      } else if (handle == GamepadButton::action_a()) {
        _device_class = DC_gamepad;
      } else if (handle == GamepadButton::trigger()) {
        _device_class = DC_flight_stick;
      } else if (handle == GamepadButton::ltrigger()) {
        _ltrigger_button = i;
      } else if (handle == GamepadButton::rtrigger()) {
        _rtrigger_button = i;
      }
      _buttons[i].handle = handle;
    }
  }

  if (num_axes > 0) {
    uint8_t axmap[512];
    ioctl(_fd, JSIOCGAXMAP, axmap);

    for (uint8_t i = 0; i < num_axes; ++i) {
      ControlAxis axis = C_none;

      switch (axmap[i]) {
      case ABS_X:
        if (_device_class == DC_gamepad) {
          axis = InputDevice::C_left_x;
        } else if (_device_class == DC_flight_stick) {
          axis = InputDevice::C_roll;
        } else {
          axis = InputDevice::C_x;
        }
        break;

      case ABS_Y:
        if (_device_class == DC_gamepad) {
          axis = InputDevice::C_left_y;
        } else if (_device_class == DC_flight_stick) {
          axis = InputDevice::C_pitch;
        } else {
          axis = InputDevice::C_y;
        }
        break;

      case ABS_Z:
        if (_device_class == DC_gamepad) {
          axis = C_left_trigger;
        } else {
          //axis = C_trigger;
        }
        break;

      case ABS_RX:
        axis = C_right_x;
        break;

      case ABS_RY:
        axis = C_right_y;
        break;

      case ABS_RZ:
        if (_device_class == DC_gamepad) {
          axis = InputDevice::C_right_trigger;
        } else {
          axis = InputDevice::C_yaw;
        }
        break;

      case ABS_THROTTLE:
        axis = InputDevice::C_throttle;
        break;

      case ABS_RUDDER:
        axis = InputDevice::C_rudder;
        break;

      case ABS_WHEEL:
        axis = InputDevice::C_wheel;
        break;

      case ABS_GAS:
        axis = InputDevice::C_accelerator;
        break;

      case ABS_BRAKE:
        axis = InputDevice::C_brake;
        break;

      case ABS_HAT0X:
        if (_dpad_left_button == -1) {
          // Emulate D-Pad or hat switch.
          _dpad_x_axis = i;
          _dpad_left_button = (int)_buttons.size();
          if (_device_class == DC_gamepad) {
            _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
            _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
          } else {
            _buttons.push_back(ButtonState(GamepadButton::hat_left()));
            _buttons.push_back(ButtonState(GamepadButton::hat_right()));
          }
          axis = C_none;
        }
        break;

      case ABS_HAT0Y:
        if (_dpad_up_button == -1) {
          // Emulate D-Pad.
          _dpad_y_axis = i;
          _dpad_up_button = (int)_buttons.size();
          if (_device_class == DC_gamepad) {
            _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
            _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
          } else {
            _buttons.push_back(ButtonState(GamepadButton::hat_up()));
            _buttons.push_back(ButtonState(GamepadButton::hat_down()));
          }
          axis = C_none;
        }
        break;

      default:
        if (device_cat.is_debug()) {
          device_cat.debug() << "Unmapped /dev/input/js" << _index
            << " axis " << (int)i << ": 0x" << hex << (int)axmap[i] << "\n";
        }
        axis = C_none;
        break;
      }
      _controls[i].axis = axis;

      if (axis == C_left_trigger || axis == C_right_trigger) {
        // We'd like to use 0.0 to indicate the resting position.
        _controls[i]._scale = 1.0 / 65534.0;
        _controls[i]._bias = 0.5;
        have_analog_triggers = true;
      } else if (axis == C_left_y || axis == C_right_y || axis == C_y) {
        _controls[i]._scale = 1.0 / -32767.0;
        _controls[i]._bias = 0.0;
      } else {
        _controls[i]._scale = 1.0 / 32767.0;
        _controls[i]._bias = 0.0;
      }
    }
  }

  if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
    // Emulate analog triggers.
    _ltrigger_control = (int)_controls.size();
    add_control(C_left_trigger, 0, 1, false);
    add_control(C_right_trigger, 0, 1, false);
  } else {
    _ltrigger_button = -1;
    _rtrigger_button = -1;
  }

  // Get additional information from sysfs.
  sprintf(path, "/sys/class/input/js%d/device/id/vendor", _index);
  FILE *f = fopen(path, "r");
  if (f) {
    if (fscanf(f, "%hx", &_vendor_id) < 1) {
      _vendor_id = 0;
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/id/product", _index);
  f = fopen(path, "r");
  if (f) {
    if (fscanf(f, "%hx", &_product_id) < 1) {
      _product_id = 0;
    }
    fclose(f);
  }
  char buffer[256];
  sprintf(path, "/sys/class/input/js%d/device/device/../product", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      if (buffer[0] != 0) {
        _name.assign(buffer);
      }
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/device/../manufacturer", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      _manufacturer.assign(buffer);
    }
    fclose(f);
  }
  sprintf(path, "/sys/class/input/js%d/device/device/../serial", _index);
  f = fopen(path, "r");
  if (f) {
    if (fgets(buffer, sizeof(buffer), f) != NULL) {
      buffer[strcspn(buffer, "\r\n")] = 0;
      _serial_number.assign(buffer);
    }
    fclose(f);
  }

  // Read the init events.
  while (process_events()) {};

  // Special case handling for the wireless Xbox receiver - the Linux
  // joystick API doesn't seem to have a way to report whether the device
  // is actually turned on.  The best we can do is check whether the axes
  // are all 0, which indicates that the driver hasn't received any data for
  // this gamepad yet (which means it hasn't been plugged in for this session)
  if (strncmp(name, "Xbox 360 Wireless Receiver", 26) == 0) {
    for (int i = 0; i < _controls.size(); ++i) {
      if (_controls[i].state != 0.0) {
        _is_connected = true;
        return true;
      }
    }
    _is_connected = false;
  } else {
    _is_connected = true;
  }

  return true;
}

/**
 * Reads a number of events from the device.  Returns true if events were
 * read, meaning this function should keep being called until it returns
 * false.
 */
bool LinuxJoystickDevice::
process_events() {
  // Read 8 events at a time.
  struct js_event events[8];

  int n_read = read(_fd, events, sizeof(events));
  if (n_read < 0) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      // No data available for now.

    } else if (errno == ENODEV) {
      // The device ceased to exist, so we better close it.  No need
      // to worry about removing it from the InputDeviceManager, as it
      // will get an inotify event sooner or later about this.
      close(_fd);
      _fd = -1;
      //_is_connected = false;
      errno = 0;

    } else {
      device_cat.error() << "read: " << strerror(errno) << "\n";
    }
    return false;
  }

  if (n_read == 0) {
    return false;
  }

  n_read /= sizeof(struct js_event);

  for (int i = 0; i < n_read; ++i) {
    int index = events[i].number;

    if (events[i].type & JS_EVENT_BUTTON) {
      if (index == _ltrigger_button) {
        control_changed(_ltrigger_control, events[i].value);
      } else if (index == _rtrigger_button) {
        control_changed(_ltrigger_control + 1, events[i].value);
      }
      button_changed(index, (events[i].value != 0));

    } else if (events[i].type & JS_EVENT_AXIS) {
      if (index == _dpad_x_axis) {
        button_changed(_dpad_left_button, events[i].value < -1000);
        button_changed(_dpad_left_button+1, events[i].value > 1000);
      } else if (index == _dpad_y_axis) {
        button_changed(_dpad_up_button, events[i].value < -1000);
        button_changed(_dpad_up_button+1, events[i].value > 1000);
      }

      control_changed(index, events[i].value);
    }
  }

  return true;
}

#endif
<|endoftext|>


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

using std::string;

CFloatingPointParameterType::CFloatingPointParameterType(const string& strName)
    : base(strName),
      _fMin(-std::numeric_limits<float>::max()),
      _fMax(std::numeric_limits<float>::max())
{
}

string CFloatingPointParameterType::getKind() const
{
    return "FloatingPointParameter";
}

// Element properties
void CFloatingPointParameterType::showProperties(string& strResult) const
{
    base::showProperties(strResult);

    strResult += "Min:" + CUtility::toString(_fMin) + "\n" +
                 "Max:" + CUtility::toString(_fMax) + "\n";
}

void CFloatingPointParameterType::handleValueSpaceAttribute(
        CXmlElement& xmlConfigurableElementSettingsElement,
        CConfigurationAccessContext& configurationAccessContext) const
{
    if (!configurationAccessContext.serializeOut()) {

        string strValueSpace;

        if (xmlConfigurableElementSettingsElement.getAttribute("ValueSpace", strValueSpace)) {

            configurationAccessContext.setValueSpaceRaw(strValueSpace == "Raw");
        } else {

            configurationAccessContext.setValueSpaceRaw(false);
        }
    } else {
        // Set the value space only if it is raw (i.e. not the default one)
        if (configurationAccessContext.valueSpaceIsRaw()) {

            xmlConfigurableElementSettingsElement.setAttribute("ValueSpace", "Raw");
        }
    }
}

bool CFloatingPointParameterType::fromXml(const CXmlElement& xmlElement,
                                          CXmlSerializingContext& serializingContext)
{
    // Size. The XSD fixes it to 32
    uint32_t uiSizeInBits = 32;
    xmlElement.getAttribute("Size", uiSizeInBits);

    // Size support check: only floats are supported
    // (e.g. doubles are not supported)
    if (uiSizeInBits != sizeof(float) * CHAR_BIT) {

        serializingContext.setError("Unsupported size (" + CUtility::toString(uiSizeInBits) +
            ") for " + getKind() + " " + xmlElement.getPath() + ". For now, only 32 is supported.");

        return false;
    }

    setSize(uiSizeInBits / CHAR_BIT);

    xmlElement.getAttribute("Min", _fMin);
    xmlElement.getAttribute("Max", _fMax);

    if (_fMin > _fMax) {
        serializingContext.setError("Min (" + CUtility::toString(_fMin) +
                ") can't be greater than Max (" + CUtility::toString(_fMax) + ")");
        return false;
    }

    return base::fromXml(xmlElement, serializingContext);
}

bool CFloatingPointParameterType::toBlackboard(
        const string& strValue,
        uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    // Check Value integrity
    if (CUtility::isHexadecimal(strValue) && !parameterAccessContext.valueSpaceIsRaw()) {

        parameterAccessContext.setError("Hexadecimal values are not supported for "
                                        + getKind()
                                        + " when selected value space is real: "
                                        + strValue);

        return false;
    }

    if (parameterAccessContext.valueSpaceIsRaw()) {

        uint32_t uiData;

        // Raw value: interpret the user input as the memory content of the
        // parameter
        if (!convertTo(strValue, uiData)) {

            parameterAccessContext.setError("Value '" + strValue + "' is invalid");
            return false;
        }

        float fData = reinterpret_cast<const float&>(uiData);

        // Check against NaN or infinity
        if (!std::isfinite(fData)) {

            parameterAccessContext.setError("Value " + strValue + " is not a finite number");
            return false;
        }

        if (!checkValueAgainstRange(fData)) {

            setOutOfRangeError(strValue, parameterAccessContext);
            return false;
        }

        uiValue = uiData;
        return true;
    }
    else {

        float fValue;

        // Interpret the user input as float
        if (!convertTo(strValue, fValue)) {

            parameterAccessContext.setError("Value " + strValue + " is invalid");
            return false;
        }

        if (!checkValueAgainstRange(fValue)) {

            setOutOfRangeError(strValue, parameterAccessContext);
            return false;
        }

        // Move to the "raw memory" value space
        uiValue = reinterpret_cast<const uint32_t&>(fValue);
        return true;
    }
}

void CFloatingPointParameterType::setOutOfRangeError(
        const string& strValue,
        CParameterAccessContext& parameterAccessContext) const
{
    // error message buffer
    std::ostringstream ostrStream;

    ostrStream << "Value " << strValue << " standing out of admitted ";

    if (!parameterAccessContext.valueSpaceIsRaw()) {

        ostrStream << "real range [" << _fMin << ", " << _fMax << "]";
    } else {

        uint32_t uiMin = reinterpret_cast<const uint32_t&>(_fMin);
        uint32_t uiMax = reinterpret_cast<const uint32_t&>(_fMax);

        if (CUtility::isHexadecimal(strValue)) {

            ostrStream << std::showbase << std::hex
                       << std::setw(getSize() * 2) << std::setfill('0');
        }

        ostrStream << "raw range [" << uiMin << ", " << uiMax << "]";
    }
    ostrStream << " for " << getKind();

    parameterAccessContext.setError(ostrStream.str());
}

bool CFloatingPointParameterType::fromBlackboard(
        string& strValue,
        const uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    std::ostringstream ostrStream;

    if (parameterAccessContext.valueSpaceIsRaw()) {

        if (parameterAccessContext.outputRawFormatIsHex()) {

            ostrStream << std::showbase << std::hex
                       << std::setw(getSize() * 2) << std::setfill('0');
        }

        ostrStream << uiValue;
    } else {

        // Move from "raw memory" value space to real space
        float fValue = reinterpret_cast<const float&>(uiValue);

        ostrStream << fValue;
    }

    strValue = ostrStream.str();

    return true;
}

// Value access
bool CFloatingPointParameterType::toBlackboard(
        double dUserValue,
        uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    if (!checkValueAgainstRange(dUserValue)) {

        parameterAccessContext.setError("Value out of range");
        return false;
    }

    // Cast is fine because dValue has been checked against the value range
    float fValue = static_cast<float>(dUserValue);

    uiValue = reinterpret_cast<const uint32_t&>(fValue);
    return true;
}

bool CFloatingPointParameterType::fromBlackboard(
        double& dUserValue,
        uint32_t uiValue,
        CParameterAccessContext& /*ctx*/) const
{
    // Move from "raw memory" value space to real space
    float fValue = reinterpret_cast<const float&>(uiValue);

    dUserValue = fValue;
    return true;
}

bool CFloatingPointParameterType::checkValueAgainstRange(double dValue) const
{
    // Check that dValue can safely be cast to a float
    // (otherwise, behaviour is undefined)
    if ((dValue < -std::numeric_limits<float>::max())
        || (dValue > std::numeric_limits<float>::max())) {
            return false;
    }

    return checkValueAgainstRange(static_cast<float>(dValue));
}

bool CFloatingPointParameterType::checkValueAgainstRange(float fValue) const
{
    return fValue <= _fMax && fValue >= _fMin;
}

void CFloatingPointParameterType::toXml(CXmlElement& xmlElement,
                                        CXmlSerializingContext& serializingContext) const
{
    xmlElement.setAttribute("Size", getSize() * CHAR_BIT);
    xmlElement.setAttribute("Min", _fMin);
    xmlElement.setAttribute("Max", _fMax);

    base::toXml(xmlElement, serializingContext);
}
<commit_msg>Fixed the low value param<commit_after>/*
 * Copyright (c) 2014-2015, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, Value, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "FloatingPointParameterType.h"
#include <sstream>
#include <iomanip>
#include "ParameterAccessContext.h"
#include "ConfigurationAccessContext.h"
#include <limits>
#include <climits>
#include "convert.hpp"
#include "Utility.h"

using std::string;

CFloatingPointParameterType::CFloatingPointParameterType(const string& strName)
    : base(strName),
      _fMin(std::numeric_limits<float>::lowest()),
      _fMax(std::numeric_limits<float>::max())
{
}

string CFloatingPointParameterType::getKind() const
{
    return "FloatingPointParameter";
}

// Element properties
void CFloatingPointParameterType::showProperties(string& strResult) const
{
    base::showProperties(strResult);

    strResult += "Min:" + CUtility::toString(_fMin) + "\n" +
                 "Max:" + CUtility::toString(_fMax) + "\n";
}

void CFloatingPointParameterType::handleValueSpaceAttribute(
        CXmlElement& xmlConfigurableElementSettingsElement,
        CConfigurationAccessContext& configurationAccessContext) const
{
    if (!configurationAccessContext.serializeOut()) {

        string strValueSpace;

        if (xmlConfigurableElementSettingsElement.getAttribute("ValueSpace", strValueSpace)) {

            configurationAccessContext.setValueSpaceRaw(strValueSpace == "Raw");
        } else {

            configurationAccessContext.setValueSpaceRaw(false);
        }
    } else {
        // Set the value space only if it is raw (i.e. not the default one)
        if (configurationAccessContext.valueSpaceIsRaw()) {

            xmlConfigurableElementSettingsElement.setAttribute("ValueSpace", "Raw");
        }
    }
}

bool CFloatingPointParameterType::fromXml(const CXmlElement& xmlElement,
                                          CXmlSerializingContext& serializingContext)
{
    // Size. The XSD fixes it to 32
    uint32_t uiSizeInBits = 32;
    xmlElement.getAttribute("Size", uiSizeInBits);

    // Size support check: only floats are supported
    // (e.g. doubles are not supported)
    if (uiSizeInBits != sizeof(float) * CHAR_BIT) {

        serializingContext.setError("Unsupported size (" + CUtility::toString(uiSizeInBits) +
            ") for " + getKind() + " " + xmlElement.getPath() + ". For now, only 32 is supported.");

        return false;
    }

    setSize(uiSizeInBits / CHAR_BIT);

    xmlElement.getAttribute("Min", _fMin);
    xmlElement.getAttribute("Max", _fMax);

    if (_fMin > _fMax) {
        serializingContext.setError("Min (" + CUtility::toString(_fMin) +
                ") can't be greater than Max (" + CUtility::toString(_fMax) + ")");
        return false;
    }

    return base::fromXml(xmlElement, serializingContext);
}

bool CFloatingPointParameterType::toBlackboard(
        const string& strValue,
        uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    // Check Value integrity
    if (CUtility::isHexadecimal(strValue) && !parameterAccessContext.valueSpaceIsRaw()) {

        parameterAccessContext.setError("Hexadecimal values are not supported for "
                                        + getKind()
                                        + " when selected value space is real: "
                                        + strValue);

        return false;
    }

    if (parameterAccessContext.valueSpaceIsRaw()) {

        uint32_t uiData;

        // Raw value: interpret the user input as the memory content of the
        // parameter
        if (!convertTo(strValue, uiData)) {

            parameterAccessContext.setError("Value '" + strValue + "' is invalid");
            return false;
        }

        float fData = reinterpret_cast<const float&>(uiData);

        // Check against NaN or infinity
        if (!std::isfinite(fData)) {

            parameterAccessContext.setError("Value " + strValue + " is not a finite number");
            return false;
        }

        if (!checkValueAgainstRange(fData)) {

            setOutOfRangeError(strValue, parameterAccessContext);
            return false;
        }

        uiValue = uiData;
        return true;
    }
    else {

        float fValue;

        // Interpret the user input as float
        if (!convertTo(strValue, fValue)) {

            parameterAccessContext.setError("Value " + strValue + " is invalid");
            return false;
        }

        if (!checkValueAgainstRange(fValue)) {

            setOutOfRangeError(strValue, parameterAccessContext);
            return false;
        }

        // Move to the "raw memory" value space
        uiValue = reinterpret_cast<const uint32_t&>(fValue);
        return true;
    }
}

void CFloatingPointParameterType::setOutOfRangeError(
        const string& strValue,
        CParameterAccessContext& parameterAccessContext) const
{
    // error message buffer
    std::ostringstream ostrStream;

    ostrStream << "Value " << strValue << " standing out of admitted ";

    if (!parameterAccessContext.valueSpaceIsRaw()) {

        ostrStream << "real range [" << _fMin << ", " << _fMax << "]";
    } else {

        uint32_t uiMin = reinterpret_cast<const uint32_t&>(_fMin);
        uint32_t uiMax = reinterpret_cast<const uint32_t&>(_fMax);

        if (CUtility::isHexadecimal(strValue)) {

            ostrStream << std::showbase << std::hex
                       << std::setw(getSize() * 2) << std::setfill('0');
        }

        ostrStream << "raw range [" << uiMin << ", " << uiMax << "]";
    }
    ostrStream << " for " << getKind();

    parameterAccessContext.setError(ostrStream.str());
}

bool CFloatingPointParameterType::fromBlackboard(
        string& strValue,
        const uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    std::ostringstream ostrStream;

    if (parameterAccessContext.valueSpaceIsRaw()) {

        if (parameterAccessContext.outputRawFormatIsHex()) {

            ostrStream << std::showbase << std::hex
                       << std::setw(getSize() * 2) << std::setfill('0');
        }

        ostrStream << uiValue;
    } else {

        // Move from "raw memory" value space to real space
        float fValue = reinterpret_cast<const float&>(uiValue);

        ostrStream << fValue;
    }

    strValue = ostrStream.str();

    return true;
}

// Value access
bool CFloatingPointParameterType::toBlackboard(
        double dUserValue,
        uint32_t& uiValue,
        CParameterAccessContext& parameterAccessContext) const
{
    if (!checkValueAgainstRange(dUserValue)) {

        parameterAccessContext.setError("Value out of range");
        return false;
    }

    // Cast is fine because dValue has been checked against the value range
    float fValue = static_cast<float>(dUserValue);

    uiValue = reinterpret_cast<const uint32_t&>(fValue);
    return true;
}

bool CFloatingPointParameterType::fromBlackboard(
        double& dUserValue,
        uint32_t uiValue,
        CParameterAccessContext& /*ctx*/) const
{
    // Move from "raw memory" value space to real space
    float fValue = reinterpret_cast<const float&>(uiValue);

    dUserValue = fValue;
    return true;
}

bool CFloatingPointParameterType::checkValueAgainstRange(double dValue) const
{
    // Check that dValue can safely be cast to a float
    // (otherwise, behaviour is undefined)
    if ((dValue < -std::numeric_limits<float>::max())
        || (dValue > std::numeric_limits<float>::max())) {
            return false;
    }

    return checkValueAgainstRange(static_cast<float>(dValue));
}

bool CFloatingPointParameterType::checkValueAgainstRange(float fValue) const
{
    return fValue <= _fMax && fValue >= _fMin;
}

void CFloatingPointParameterType::toXml(CXmlElement& xmlElement,
                                        CXmlSerializingContext& serializingContext) const
{
    xmlElement.setAttribute("Size", getSize() * CHAR_BIT);
    xmlElement.setAttribute("Min", _fMin);
    xmlElement.setAttribute("Max", _fMax);

    base::toXml(xmlElement, serializingContext);
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/patch.h>

#include <primitives/exceptions.h>

#include <boost/algorithm/string.hpp>

#include <regex>

namespace primitives::patch
{

static
void add_line(String &r, int &ni, const String &s)
{
    r += s + "\n";
    ni++;
}

static
std::optional<String> patch_hunk(
    const Strings &lines, Strings::iterator &i, const Strings::iterator end, int options,
    int &oi, int &ni, int os, int ns)
{
    // we are in hunk right now

    String r;

    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        if (os == 0 && ns == 0)
            return r;
        if (os < 0 || ns < 0)
            return {}; // hunk info is bad

        switch ((*i)[0])
        {
        case ' ':
            if (lines[oi] != i->substr(1))
                return {}; // context lines do not match
            add_line(r, ni, lines[oi]);
            os--;
            ns--;
            oi++;
            break;
        case '+':
            add_line(r, ni, i->substr(1));
            ns--;
            break;
        case '-':
            if (lines[oi] != i->substr(1))
                return {}; // context lines do not match
            os--;
            oi++;
            break;
        default:
            return {}; // bad symbol
        }
    }

    if (os == 0 && ns == 0)
        return r;
    return {}; // hunk info is bad
}

static std::optional<String> patch_file(
    const Strings &lines, Strings::iterator &i, const Strings::iterator end, int options,
    int &oi)
{
    String r;

    int ni = 0; // new index

    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        switch ((*i)[0])
        {
        case '+':
            if (i->find("+++") == 0)
                continue;
            return {}; // + is out of hunk
        case '-':
            if (i->find("---") == 0)
                continue;
            return {}; // - is out of hunk
        case '@':
        {
            // @@ -1,3 +1,9 @@ some extra info here (git uses this)
            static const std::regex r_hunk("@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+) @@.*");
            std::smatch m;
            if (!std::regex_match(*i, m, r_hunk))
                return {}; // bad hunk description

            auto ol = std::stoi(m[1].str()) - 1;
            auto os = std::stoi(m[2].str());
            auto nl = std::stoi(m[3].str()) - 1;
            auto ns = std::stoi(m[4].str());

            // copy lines up to hunk
            for (; oi < ol; oi++)
                add_line(r, ni, lines[oi]);

            if (nl != ni)
                return {}; // nl is wrong

            auto r2 = patch_hunk(lines, ++i, end, options, oi, ni, os, ns);
            if (!r2)
                return {}; // bad hunk
            r += *r2;
            --i;
        }
            break;
        // git stuff
        case 'd':
        case 'i':
            if (options & PatchOption::Git)
                return r; // handle outside
        default:
            return {};
        }
    }

    return r;
}

static
auto prepare_lines(const String &s)
{
    Strings v;
    boost::split(v, s, boost::is_any_of("\n"));
    for (auto &l : v)
    {
        while (!l.empty() && l.back() == '\r')
            l.resize(l.size() - 1);
    }
    return v;
}

static
std::optional<String> patch(const String &text, Strings::iterator &i, const Strings::iterator end, int options)
{
    auto lines = prepare_lines(text);

    int oi = 0; // old index
    auto r = patch_file(lines, i, end, options, oi);

    if (!r)
        return {}; // bad file

    // add rest of the file
    for (; oi < lines.size(); oi++)
        *r += lines[oi] + "\n";

    if (!r->empty())
        r->resize(r->size() - 1); // remove last '\n'

    return r;
}

std::optional<String> patch(const String &text, const String &unidiff, int options)
{
    auto diff = prepare_lines(unidiff);

    auto begin = diff.begin();
    return patch(text, begin, diff.end(), options);
}

static
bool patch(const path &root_dir, Strings::iterator &i, const Strings::iterator end, int options)
{
    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        switch ((*i)[0])
        {
        // git stuff
        case 'd':
            if (options & PatchOption::Git)
            {
                // diff --git a/cppan.yml b/cppan.yml
                static const std::regex r_d("diff --git a/([^[[:space:]]]).*");
                std::smatch m;
                if (!std::regex_match(*i, m, r_d))
                    return {}; // bad diff description

                path fn = m[1].str();
                if (fn.is_absolute())
                    return {}; // not allowed

                auto text = read_file(root_dir / fn);
                auto r = patch(text, i, end, options);
                if (!r)
                    return {}; // cannot patch this file

                continue;
            }
            [[fallthrough]];
        case 'i':
            if (options & PatchOption::Git)
                continue;
            [[fallthrough]];
        default:
            return false;
        }
    }

    return true;
}

bool patch(const path &root_dir, const String &unidiff, int options)
{
    throw SW_RUNTIME_ERROR("not tested");

    auto diff = prepare_lines(unidiff);

    auto begin = diff.begin();
    return patch(root_dir, begin, diff.end(), options);
}

}
<commit_msg>Fix pvs-studio warnings.<commit_after>// Copyright (C) 2019 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/patch.h>

#include <primitives/exceptions.h>

#include <boost/algorithm/string.hpp>

#include <regex>

namespace primitives::patch
{

static
void add_line(String &r, int &ni, const String &s)
{
    r += s + "\n";
    ni++;
}

static
std::optional<String> patch_hunk(
    const Strings &lines, Strings::iterator &i, const Strings::iterator end, int options,
    size_t &oi, int &ni, int os, int ns)
{
    // we are in hunk right now

    String r;

    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        if (os == 0 && ns == 0)
            return r;
        if (os < 0 || ns < 0)
            return {}; // hunk info is bad

        switch ((*i)[0])
        {
        case ' ':
            if (lines[oi] != i->substr(1))
                return {}; // context lines do not match
            add_line(r, ni, lines[oi]);
            os--;
            ns--;
            oi++;
            break;
        case '+':
            add_line(r, ni, i->substr(1));
            ns--;
            break;
        case '-':
            if (lines[oi] != i->substr(1))
                return {}; // context lines do not match
            os--;
            oi++;
            break;
        default:
            return {}; // bad symbol
        }
    }

    if (os == 0 && ns == 0)
        return r;
    return {}; // hunk info is bad
}

static std::optional<String> patch_file(
    const Strings &lines, Strings::iterator &i, const Strings::iterator end, int options,
    size_t &oi)
{
    String r;

    int ni = 0; // new index

    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        switch ((*i)[0])
        {
        case '+':
            if (i->find("+++") == 0)
                continue;
            return {}; // + is out of hunk
        case '-':
            if (i->find("---") == 0)
                continue;
            return {}; // - is out of hunk
        case '@':
        {
            // @@ -1,3 +1,9 @@ some extra info here (git uses this)
            static const std::regex r_hunk("@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+) @@.*");
            std::smatch m;
            if (!std::regex_match(*i, m, r_hunk))
                return {}; // bad hunk description

            auto ol = std::stoi(m[1].str()) - 1;
            auto os = std::stoi(m[2].str());
            auto nl = std::stoi(m[3].str()) - 1;
            auto ns = std::stoi(m[4].str());

            // copy lines up to hunk
            for (; oi < (size_t)ol; oi++)
                add_line(r, ni, lines[oi]);

            if (nl != ni)
                return {}; // nl is wrong

            auto r2 = patch_hunk(lines, ++i, end, options, oi, ni, os, ns);
            if (!r2)
                return {}; // bad hunk
            r += *r2;
            --i;
        }
            break;
        // git stuff
        case 'd':
        case 'i':
            if (options & PatchOption::Git)
                return r; // handle outside
        default:
            return {};
        }
    }

    return r;
}

static
auto prepare_lines(const String &s)
{
    Strings v;
    boost::split(v, s, boost::is_any_of("\n"));
    for (auto &l : v)
    {
        while (!l.empty() && l.back() == '\r')
            l.resize(l.size() - 1);
    }
    return v;
}

static
std::optional<String> patch(const String &text, Strings::iterator &i, const Strings::iterator end, int options)
{
    auto lines = prepare_lines(text);

    size_t oi = 0; // old index
    auto r = patch_file(lines, i, end, options, oi);

    if (!r)
        return {}; // bad file

    // add rest of the file
    for (; oi < lines.size(); oi++)
        *r += lines[oi] + "\n";

    if (!r->empty())
        r->resize(r->size() - 1); // remove last '\n'

    return r;
}

std::optional<String> patch(const String &text, const String &unidiff, int options)
{
    auto diff = prepare_lines(unidiff);

    auto begin = diff.begin();
    return patch(text, begin, diff.end(), options);
}

static
bool patch(const path &root_dir, Strings::iterator &i, const Strings::iterator end, int options)
{
    for (; i != end; i++)
    {
        if (i->empty())
            return {}; // empty lines not allowed

        switch ((*i)[0])
        {
        // git stuff
        case 'd':
            if (options & PatchOption::Git)
            {
                // diff --git a/cppan.yml b/cppan.yml
                static const std::regex r_d("diff --git a/([^[[:space:]]]).*");
                std::smatch m;
                if (!std::regex_match(*i, m, r_d))
                    return {}; // bad diff description

                path fn = m[1].str();
                if (fn.is_absolute())
                    return {}; // not allowed

                auto text = read_file(root_dir / fn);
                auto r = patch(text, i, end, options);
                if (!r)
                    return {}; // cannot patch this file

                continue;
            }
            [[fallthrough]];
        case 'i':
            if (options & PatchOption::Git)
                continue;
            [[fallthrough]];
        default:
            return false;
        }
    }

    return true;
}

bool patch(const path &root_dir, const String &unidiff, int options)
{
    throw SW_RUNTIME_ERROR("not tested");

    auto diff = prepare_lines(unidiff);

    auto begin = diff.begin();
    return patch(root_dir, begin, diff.end(), options);
}

}
<|endoftext|>


<commit_before>/*!
*	@file	subdivision.cpp
*	@brief	Main file for demo program for Subdivision Surfaces
*/

#include <iostream>

#include <GL/glut.h>

#include "subdivision.h"
#include "mesh.h"
#include "zpr.h"

using namespace std;

mesh scene_mesh;
string input;
bool draw_lines = true;

void display_scene();
void init_scene();
void reshape_scene(int, int);
void keyboard_callback(unsigned char, int, int);

int main(int argc, char* argv[])
{
	// quick and dirty
	if(argc == 1)
	{
		cout << "Loading default mesh...\n";
		input = "";
	}
	else
	{
		cout << "Load user-specified mesh \"" << argv[1] << "\"\n";
		input = argv[1];
	}

	glutInit(&argc, argv);
	glutInitWindowPosition(WIN_X, WIN_Y);
	glutInitWindowSize(WIN_WIDTH, WIN_HEIGHT);

	glutInitDisplayMode(	GLUT_RGBA 	|
				GLUT_DOUBLE 	|
				GLUT_DEPTH);

	glutCreateWindow(WIN_TITLE);

	init_scene();

	glutDisplayFunc(display_scene);
	glutReshapeFunc(reshape_scene);
	glutKeyboardFunc(keyboard_callback);

	glutPostRedisplay();
	glutMainLoop();
	return(0);
}

/*!
*	Initialization for the scene. Loads the mesh and sets some display
*	parameters.
*/

void init_scene(void)
{
	zprInit();

	glClearColor(0.0, 0.0, 0.0, 1.0);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_POLYGON_SMOOTH);

	glPointSize(5.0);

	scene_mesh.load(input.c_str());

	gluLookAt(	0.0, 1.0, 2.0,
		      	0.0, 0.0,-1.0,
			0.0, 1.0, 0.0);

}

/*!
*	Displays the scene, i.e. the current mesh and its subdivisions.
*/

void display_scene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f(1.0, 1.0, 1.0);

	glMatrixMode(GL_MODELVIEW);
		scene_mesh.draw();
	glutSwapBuffers();
}

/*!
*
*	Reshape function for the scene. Maintains aspect ratio.
*
*	@param width  New width of window
*	@param height New height of window
*
*/

void reshape_scene(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0, width/static_cast<GLfloat>(height), WIN_Z_NEAR, WIN_Z_FAR);
}

/*!
*	Keyboard callback function for program.
*
*	@param key	Key that has been pressed
*	@param x	x position of mouse cursor
*	@param y	y position of mouse cursor
*
*/

void keyboard_callback(unsigned char key, int x, int y)
{
	switch(tolower(key))
	{
		case 'e':
			scene_mesh.save("subdivided_"+input);
			break;

		case 'c':
			scene_mesh.subdivide_catmull_clark();
			break;

		case 'l':
			scene_mesh.subdivide_loop();
			break;

		case 'd':
			scene_mesh.subdivide_doo_sabin();
			break;

		case 'r':
			scene_mesh.load(input.c_str());
			break;

		case '\t':
			draw_lines = !draw_lines;
			break;

		case 'q':
			glutDestroyWindow(glutGetWindow());
			exit(0);
			break;

		default:
			break;
	}

	glutPostRedisplay();
}
<commit_msg>Working draft for main program<commit_after>/*!
*	@file	subdivision.cpp
*	@brief	Main file for demo program for Subdivision Surfaces
*/

#include <iostream>
#include <sstream>

#include <GL/glut.h>
#include <unistd.h>
#include <getopt.h>

#include "subdivision.h"
#include "mesh.h"
#include "zpr.h"

using namespace std;

mesh scene_mesh;
string input;
string output;

bool draw_lines = true;

void display_scene();
void init_scene();
void reshape_scene(int, int);
void keyboard_callback(unsigned char, int, int);

int main(int argc, char* argv[])
{
	static option cmd_line_opts[] =
	{
		{"output",	required_argument,	NULL,	'o'},
		{"steps",	required_argument,	NULL,	'n'},
		{"type",	required_argument,	NULL,	't'},

		{"help",	no_argument,		NULL,	'h'},

		{NULL, 0, NULL, 0}
	};

	short type	= mesh::TYPE_EXT;
	size_t steps	= 1;

	int option = 0;
	while((option = getopt_long(argc, argv, "o:n:t:h", cmd_line_opts, NULL)) != -1)
	{
		switch(option)
		{
			case 'o':
				output = optarg;
				break;

			case 't':
			{
				string type_str = optarg;
				transform(type_str.begin(), type_str.end(), type_str.begin(), (int(*)(int)) tolower);

				if(type_str == "ply")
					type = mesh::TYPE_PLY;
				else if(type_str == "obj")
					type = mesh::TYPE_OBJ;
				else if(type_str == "off")
					type = mesh::TYPE_OFF;
				else
					cerr << "WTF? UNKNOWN TYPE.\n"; // FIXME

				break;
			}

			case 'n':
			{
				istringstream converter(optarg);
				converter >> steps;
				if(converter.fail())
				{
					// FIXME
					cerr << "CONVERTER ERROR\n";
				}
				break;
			}

			case 'h':
			case '?':
				// NYI
				break;
		}
	}

	// Read further command-line parameters; these are all supposed to be
	// input files. If the user already specified an output file, only one
	// input file will be accepted.

	vector<string> files;
	while(optind < argc)
	{
		files.push_back(argv[optind++]);
		if(output.length() != 0 && files.size() > 1)
		{
			cerr << "WTF?\n"; // FIXME
			return(0);
		}
	}

	// Replace the special file "-" by an empty string, thereby signalling
	// that standard input and standard output are to be used as file
	// streams.

	for(vector<string>::iterator it = files.begin(); it != files.end(); it++)
	{
		if(it->length() == 1 && (*it)[0] == '-')
			*it = "";
	}

	bool output_set = (output.length() > 0);
	if(output.length() == 1 && output[0] == '-')
		output = "";

	// Apply subdivision algorithm to all files

	for(vector<string>::iterator it = files.begin(); it != files.end(); it++)
	{
		scene_mesh.load(*it, type);

		// FIXME: Let algorithm be chosen
		for(size_t i = 0; i < steps; i++)
			scene_mesh.subdivide_catmull_clark();

		// If an output file has been set (even if it is empty), it
		// will be used.
		if(output_set)
			scene_mesh.save(output, type);

		// If no output file has been set and the input file name is
		// not empty, the output will be written to a file.
		else if(it->length() > 0)
		{
			// FIXME: File name can be determined automatically
			scene_mesh.save(*it+".subdivided", type);
		}

		// If no output file has been set and the input file name is
		// empty, the output will be written to STDOUT.
		else
			scene_mesh.save("", type);
	}

	glutInit(&argc, argv);
	glutInitWindowPosition(WIN_X, WIN_Y);
	glutInitWindowSize(WIN_WIDTH, WIN_HEIGHT);

	glutInitDisplayMode(	GLUT_RGBA 	|
				GLUT_DOUBLE 	|
				GLUT_DEPTH);

	glutCreateWindow(WIN_TITLE);

	init_scene();

	glutDisplayFunc(display_scene);
	glutReshapeFunc(reshape_scene);
	glutKeyboardFunc(keyboard_callback);

	glutPostRedisplay();
	glutMainLoop();
	return(0);
}

/*!
*	Initialization for the scene. Loads the mesh and sets some display
*	parameters.
*/

void init_scene(void)
{
	zprInit();

	glClearColor(0.0, 0.0, 0.0, 1.0);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_POLYGON_SMOOTH);

	glPointSize(5.0);

	scene_mesh.load(input.c_str());

	gluLookAt(	0.0, 1.0, 2.0,
		      	0.0, 0.0,-1.0,
			0.0, 1.0, 0.0);

}

/*!
*	Displays the scene, i.e. the current mesh and its subdivisions.
*/

void display_scene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f(1.0, 1.0, 1.0);

	glMatrixMode(GL_MODELVIEW);
		scene_mesh.draw();
	glutSwapBuffers();
}

/*!
*
*	Reshape function for the scene. Maintains aspect ratio.
*
*	@param width  New width of window
*	@param height New height of window
*
*/

void reshape_scene(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0, width/static_cast<GLfloat>(height), WIN_Z_NEAR, WIN_Z_FAR);
}

/*!
*	Keyboard callback function for program.
*
*	@param key	Key that has been pressed
*	@param x	x position of mouse cursor
*	@param y	y position of mouse cursor
*
*/

void keyboard_callback(unsigned char key, int x, int y)
{
	switch(tolower(key))
	{
		case 'e':
			scene_mesh.save("subdivided_"+input);
			break;

		case 'c':
			scene_mesh.subdivide_catmull_clark();
			break;

		case 'l':
			scene_mesh.subdivide_loop();
			break;

		case 'd':
			scene_mesh.subdivide_doo_sabin();
			break;

		case 'r':
			scene_mesh.load(input.c_str());
			break;

		case '\t':
			draw_lines = !draw_lines;
			break;

		case 'q':
			glutDestroyWindow(glutGetWindow());
			exit(0);
			break;

		default:
			break;
	}

	glutPostRedisplay();
}
<|endoftext|>


<commit_before><commit_msg>before and after<commit_after><|endoftext|>


<commit_before><commit_msg>Fix extra slash typo in comment.<commit_after><|endoftext|>


<commit_before>// Begin CVS Header
//   $Source: /fs/turing/cvs/copasi_dev/cvs_admin/addHeader,v $
//   $Revision: 1.10 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2008/04/11 15:21:36 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Sven Sahle and University of Heidelberg
// All rights reserved.

#include "Expression2PresentationMMLUnits.h"
#include <sbml/Model.h>
#include "CUnitInterfaceSBML.h"

Expression2PresentationMMLUnits::Expression2PresentationMMLUnits(const ASTNode* rootnode)
    : Expression2PresentationMML(rootnode),
    mpUnitInterface(NULL)
{
}

void Expression2PresentationMMLUnits::setUnitInterface(CUnitInterfaceSBML * unitInterface, CUnitInterfaceSBML * unitInterface2)
{
  mpUnitInterface = unitInterface;
  mpUnitInterface2 = unitInterface2;
  mpModel = mpUnitInterface->getModel();
}

#define SPC(level) std::string(level, ' ')

void Expression2PresentationMMLUnits::writeMathMLName(std::ostream & out, const ASTNode* node, unsigned int l) const
{
  std::string reactionID = mpReaction ? mpReaction->getId() : "";
  CUnitInformation* unitInformation = mpUnitInterface->getMappedUnitFromIdentifier(node->getName(),
                                      CUnitInterfaceSBML::CEnvironmentInformation(reactionID));

  if (!unitInformation)
    {
      Expression2PresentationMML::writeMathMLName(out, node, l + 1);
      return;
    }

  CUnitInformation* unitInformation2;

  if (mpUnitInterface2)
    unitInformation2 = mpUnitInterface2->getMappedUnitFromIdentifier(node->getName(),
                       CUnitInterfaceSBML::CEnvironmentInformation(reactionID));

  std::ostringstream oss;
  Expression2PresentationMML::writeMathMLName(oss, node, l + 2);

  if (unitInformation2)
    {
      if (CUnitInformation::isEqual(*unitInformation, *unitInformation2))
        writeMathMLBox(out, oss.str(), getMathML(*unitInformation), "", "#d0d0e0", l);
      else
        writeMathMLBox(out, oss.str(), getMathML(*unitInformation), getMathML(*unitInformation2), "#d0d0e0", l);
    }
  else
    writeMathMLBox(out, oss.str(), getMathML(*unitInformation), "#d0d0e0", l);
}

void Expression2PresentationMMLUnits::writeMathMLNumber(std::ostream & out, const ASTNode* node, unsigned int l) const
{
  CUnitInformation* unitInformation = mpUnitInterface->getMappedUnitFromNumberNode(node);
  CUnitInformation* unitInformation2 = NULL;

  if (mpUnitInterface2)
    unitInformation2 = mpUnitInterface2->getMappedUnitFromNumberNode(node);

  if (!unitInformation && !unitInformation2)
    {
      Expression2PresentationMML::writeMathMLNumber(out, node, l + 1);
      return;
    }

  std::ostringstream oss;
  Expression2PresentationMML::writeMathMLNumber(oss, node, l + 2);

  if (!unitInformation && unitInformation2)
    {
      writeMathMLBox(out, oss.str(), "<mi>unknown</mi>", getMathML(*unitInformation2), "#d0e0d0", l);
    }

  if (unitInformation && !unitInformation2)
    {
      writeMathMLBox(out, oss.str(),  getMathML(*unitInformation), "#d0e0d0", l);
    }

  if (unitInformation && unitInformation2)
    {
      writeMathMLBox(out, oss.str(),  getMathML(*unitInformation), getMathML(*unitInformation2), "#d0e0d0", l);
    }
}

std::string Expression2PresentationMMLUnits::getMathML(const CUnitInformation & ui) const
{
  std::string tmp;
  tmp += "<mrow>";

  if (ui.isConflict())
    tmp += "<mi mathcolor = \"red\">conflict!</mi>";

  if (ui.getSymbolicExpExp() > 0)
    tmp += "<mfenced>";

  switch (ui.getInfo())
    {
      case CUnitInformation::UNKNOWN:
        tmp += "<mi mathcolor = \"orange\">unknown</mi>";
        break;

      case CUnitInformation::DEFAULT:
        tmp += "<mstyle mathcolor=\"#000060\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::GLOBAL:
        tmp += "<mstyle mathcolor=\"#0000a0\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::PROVIDED:
        tmp += "<mstyle mathcolor=\"#2020ff\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::DERIVED:
        tmp += "<mstyle mathcolor=\"green\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;
    }

  if (ui.getSymbolicExpExp() > 0)
    {
      std::ostringstream ttt; ttt << ui.getSymbolicExpExp();
      tmp += "</mfenced><mo>^</mo><mi mathcolor = \"#c06000\">" + ui.getSymbolicExponent() + "</mi><mo>^</mo><mn>" + ttt.str() + "</mn>";
    }

  tmp += "</mrow>";
  return tmp;
}

void Expression2PresentationMMLUnits::writeMathMLBox(std::ostream & out, const std::string & a, const std::string & b,
    const std::string & color, unsigned int l) const
{
  out << SPC(l) << "<mstyle background=\"" << color << "\"><mfrac linethickness=\"0\">" << std::endl;
  out << a;
  out << SPC(l + 1) << "<mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << b << std::endl;
  out << SPC(l + 1) << "</mstyle>" << std::endl;
  out << SPC(l) << "</mfrac></mstyle>" << std::endl;

  //formating is done so that it looks best when a is already indented nicely while b
  //is just a one line string
}

void Expression2PresentationMMLUnits::writeMathMLBox(std::ostream & out, const std::string & a, const std::string & b, const std::string & c,
    const std::string & color, unsigned int l) const
{
  out << SPC(l) << "<mstyle background=\"" << color << "\"><mtable rowspacing=\"0.1 ex\">" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd>" << std::endl;
  out << a;
  out << SPC(l + 1) << "</mtd></mtr>" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd><mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << b << std::endl;
  out << SPC(l + 1) << "</mstyle></mtd></mtr>" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd><mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << c << std::endl;
  out << SPC(l + 1) << "</mstyle></mtd></mtr>" << std::endl;
  out << SPC(l) << "</mtable></mstyle>" << std::endl;

  //formating is done so that it looks best when a is already indented nicely while b
  //is just a one line string
}

std::string Expression2PresentationMMLUnits::getMathML(const UnitDefinition & ud) const
{
  std::string tmp;

  /*tmp += "<mrow>";
  unsigned int i, imax = ud.getNumUnits();
  for (i=0; i<imax; ++i)
  {
    if (i) tmp += "<mo>·</mo>";
    tmp += getMathML(ud.getUnit(i), false);
  }
  tmp += "</mrow>";*/

  tmp += "<mrow>";
  bool isFirst = true;
  unsigned int i, imax = ud.getNumUnits();

  for (i = 0; i < imax; ++i)
    {
      if (ud.getUnit(i)->getExponent() >= 0)
        {
          if (!isFirst) tmp += "<mo>·</mo>";

          tmp += getMathML(ud.getUnit(i), true);
          isFirst = false;
        }
    }

  if (isFirst) tmp += "<mn>1</mn>"; //there are no units with pos. exponent

  std::string tmp2;
  isFirst = true;

  for (i = 0; i < imax; ++i)
    {
      if (ud.getUnit(i)->getExponent() < 0)
        {
          if (!isFirst) tmp2 += "<mo>·</mo>";

          tmp2 += getMathML(ud.getUnit(i), true);
          isFirst = false;
        }
    }

  if (!isFirst) tmp += "<mo>/</mo>" + tmp2; //only create fraction when there is a denominator

  tmp += "</mrow>";

  return tmp;
}

std::string Expression2PresentationMMLUnits::getMathML(const Unit * u, bool absExp) const
{
  if (!u)
    return "<mi>EEE</mi>";

  int exponent = absExp ? abs(u->getExponent()) : u->getExponent();
  bool flagExp = (exponent != 1);
  bool flag = (u->getMultiplier() != 1.0) || (u->getScale() != 0); //are brackets needed?

  std::ostringstream tmp;

  if (flagExp) tmp << "<msup>";

  //if (flag) tmp << "<mfenced>";
  tmp << "<mrow>";

  if (flag) tmp << "<mo>(</mo>";

  if (u->getMultiplier() != 1.0)
    tmp << "<mn>" <<  u->getMultiplier() << "</mn><mo>·</mo>";

  if (u->getScale() != 0)
    tmp << "<msup><mn>10</mn><mn>" << u->getScale() << "</mn></msup><mo>·</mo>";

  tmp << "<mi>";
  tmp << UnitKind_toString(u->getKind());
  tmp << "</mi>";

  if (flag) tmp << "<mo>)</mo>";

  tmp << "</mrow>";
  //if (flag) tmp << "</mfenced>";

  if (flagExp) tmp << "<mn>" << u->getExponent() << "</mn>";

  if (flagExp) tmp << "</msup>";

  //tmp << "</mrow>";

  return tmp.str();
}
<commit_msg>small fix in the display of units with exponents in the denominator<commit_after>// Begin CVS Header
//   $Source: /fs/turing/cvs/copasi_dev/cvs_admin/addHeader,v $
//   $Revision: 1.10 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2008/04/11 15:21:36 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Sven Sahle and University of Heidelberg
// All rights reserved.

#include "Expression2PresentationMMLUnits.h"
#include <sbml/Model.h>
#include "CUnitInterfaceSBML.h"

Expression2PresentationMMLUnits::Expression2PresentationMMLUnits(const ASTNode* rootnode)
    : Expression2PresentationMML(rootnode),
    mpUnitInterface(NULL)
{
}

void Expression2PresentationMMLUnits::setUnitInterface(CUnitInterfaceSBML * unitInterface, CUnitInterfaceSBML * unitInterface2)
{
  mpUnitInterface = unitInterface;
  mpUnitInterface2 = unitInterface2;
  mpModel = mpUnitInterface->getModel();
}

#define SPC(level) std::string(level, ' ')

void Expression2PresentationMMLUnits::writeMathMLName(std::ostream & out, const ASTNode* node, unsigned int l) const
{
  std::string reactionID = mpReaction ? mpReaction->getId() : "";
  CUnitInformation* unitInformation = mpUnitInterface->getMappedUnitFromIdentifier(node->getName(),
                                      CUnitInterfaceSBML::CEnvironmentInformation(reactionID));

  if (!unitInformation)
    {
      Expression2PresentationMML::writeMathMLName(out, node, l + 1);
      return;
    }

  CUnitInformation* unitInformation2;

  if (mpUnitInterface2)
    unitInformation2 = mpUnitInterface2->getMappedUnitFromIdentifier(node->getName(),
                       CUnitInterfaceSBML::CEnvironmentInformation(reactionID));

  std::ostringstream oss;
  Expression2PresentationMML::writeMathMLName(oss, node, l + 2);

  if (unitInformation2)
    {
      if (CUnitInformation::isEqual(*unitInformation, *unitInformation2))
        writeMathMLBox(out, oss.str(), getMathML(*unitInformation), "", "#d0d0e0", l);
      else
        writeMathMLBox(out, oss.str(), getMathML(*unitInformation), getMathML(*unitInformation2), "#d0d0e0", l);
    }
  else
    writeMathMLBox(out, oss.str(), getMathML(*unitInformation), "#d0d0e0", l);
}

void Expression2PresentationMMLUnits::writeMathMLNumber(std::ostream & out, const ASTNode* node, unsigned int l) const
{
  CUnitInformation* unitInformation = mpUnitInterface->getMappedUnitFromNumberNode(node);
  CUnitInformation* unitInformation2 = NULL;

  if (mpUnitInterface2)
    unitInformation2 = mpUnitInterface2->getMappedUnitFromNumberNode(node);

  if (!unitInformation && !unitInformation2)
    {
      Expression2PresentationMML::writeMathMLNumber(out, node, l + 1);
      return;
    }

  std::ostringstream oss;
  Expression2PresentationMML::writeMathMLNumber(oss, node, l + 2);

  if (!unitInformation && unitInformation2)
    {
      writeMathMLBox(out, oss.str(), "<mi>unknown</mi>", getMathML(*unitInformation2), "#d0e0d0", l);
    }

  if (unitInformation && !unitInformation2)
    {
      writeMathMLBox(out, oss.str(),  getMathML(*unitInformation), "#d0e0d0", l);
    }

  if (unitInformation && unitInformation2)
    {
      writeMathMLBox(out, oss.str(),  getMathML(*unitInformation), getMathML(*unitInformation2), "#d0e0d0", l);
    }
}

std::string Expression2PresentationMMLUnits::getMathML(const CUnitInformation & ui) const
{
  std::string tmp;
  tmp += "<mrow>";

  if (ui.isConflict())
    tmp += "<mi mathcolor = \"red\">conflict!</mi>";

  if (ui.getSymbolicExpExp() > 0)
    tmp += "<mfenced>";

  switch (ui.getInfo())
    {
      case CUnitInformation::UNKNOWN:
        tmp += "<mi mathcolor = \"orange\">unknown</mi>";
        break;

      case CUnitInformation::DEFAULT:
        tmp += "<mstyle mathcolor=\"#000060\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::GLOBAL:
        tmp += "<mstyle mathcolor=\"#0000a0\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::PROVIDED:
        tmp += "<mstyle mathcolor=\"#2020ff\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;

      case CUnitInformation::DERIVED:
        tmp += "<mstyle mathcolor=\"green\">" + getMathML(ui.getSBMLUnitDefinition()) + "</mstyle>";
        break;
    }

  if (ui.getSymbolicExpExp() > 0)
    {
      std::ostringstream ttt; ttt << ui.getSymbolicExpExp();
      tmp += "</mfenced><mo>^</mo><mi mathcolor = \"#c06000\">" + ui.getSymbolicExponent() + "</mi><mo>^</mo><mn>" + ttt.str() + "</mn>";
    }

  tmp += "</mrow>";
  return tmp;
}

void Expression2PresentationMMLUnits::writeMathMLBox(std::ostream & out, const std::string & a, const std::string & b,
    const std::string & color, unsigned int l) const
{
  out << SPC(l) << "<mstyle background=\"" << color << "\"><mfrac linethickness=\"0\">" << std::endl;
  out << a;
  out << SPC(l + 1) << "<mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << b << std::endl;
  out << SPC(l + 1) << "</mstyle>" << std::endl;
  out << SPC(l) << "</mfrac></mstyle>" << std::endl;

  //formating is done so that it looks best when a is already indented nicely while b
  //is just a one line string
}

void Expression2PresentationMMLUnits::writeMathMLBox(std::ostream & out, const std::string & a, const std::string & b, const std::string & c,
    const std::string & color, unsigned int l) const
{
  out << SPC(l) << "<mstyle background=\"" << color << "\"><mtable rowspacing=\"0.1 ex\">" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd>" << std::endl;
  out << a;
  out << SPC(l + 1) << "</mtd></mtr>" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd><mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << b << std::endl;
  out << SPC(l + 1) << "</mstyle></mtd></mtr>" << std::endl;
  out << SPC(l + 1) << "<mtr><mtd><mstyle scriptlevel=\"+1\">" << std::endl;
  out << SPC(l + 2) << c << std::endl;
  out << SPC(l + 1) << "</mstyle></mtd></mtr>" << std::endl;
  out << SPC(l) << "</mtable></mstyle>" << std::endl;

  //formating is done so that it looks best when a is already indented nicely while b
  //is just a one line string
}

std::string Expression2PresentationMMLUnits::getMathML(const UnitDefinition & ud) const
{
  std::string tmp;

  /*tmp += "<mrow>";
  unsigned int i, imax = ud.getNumUnits();
  for (i=0; i<imax; ++i)
  {
    if (i) tmp += "<mo>·</mo>";
    tmp += getMathML(ud.getUnit(i), false);
  }
  tmp += "</mrow>";*/

  tmp += "<mrow>";
  bool isFirst = true;
  unsigned int i, imax = ud.getNumUnits();

  for (i = 0; i < imax; ++i)
    {
      if (ud.getUnit(i)->getExponent() >= 0)
        {
          if (!isFirst) tmp += "<mo>·</mo>";

          tmp += getMathML(ud.getUnit(i), true);
          isFirst = false;
        }
    }

  if (isFirst) tmp += "<mn>1</mn>"; //there are no units with pos. exponent

  std::string tmp2;
  isFirst = true;

  for (i = 0; i < imax; ++i)
    {
      if (ud.getUnit(i)->getExponent() < 0)
        {
          if (!isFirst) tmp2 += "<mo>·</mo>";

          tmp2 += getMathML(ud.getUnit(i), true);
          isFirst = false;
        }
    }

  if (!isFirst) tmp += "<mo>/</mo>" + tmp2; //only create fraction when there is a denominator

  tmp += "</mrow>";

  return tmp;
}

std::string Expression2PresentationMMLUnits::getMathML(const Unit * u, bool absExp) const
{
  if (!u)
    return "<mi>EEE</mi>";

  int exponent = absExp ? abs(u->getExponent()) : u->getExponent();
  bool flagExp = (exponent != 1);
  bool flag = (u->getMultiplier() != 1.0) || (u->getScale() != 0); //are brackets needed?

  std::ostringstream tmp;

  if (flagExp) tmp << "<msup>";

  //if (flag) tmp << "<mfenced>";
  tmp << "<mrow>";

  if (flag) tmp << "<mo>(</mo>";

  if (u->getMultiplier() != 1.0)
    tmp << "<mn>" <<  u->getMultiplier() << "</mn><mo>·</mo>";

  if (u->getScale() != 0)
    tmp << "<msup><mn>10</mn><mn>" << u->getScale() << "</mn></msup><mo>·</mo>";

  tmp << "<mi>";
  tmp << UnitKind_toString(u->getKind());
  tmp << "</mi>";

  if (flag) tmp << "<mo>)</mo>";

  tmp << "</mrow>";
  //if (flag) tmp << "</mfenced>";

  if (flagExp) tmp << "<mn>" << exponent << "</mn>";

  if (flagExp) tmp << "</msup>";

  //tmp << "</mrow>";

  return tmp.str();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkTypeface.h"

static void test_path(SkCanvas* canvas, const SkPath& path) {
    SkPaint paint;
    paint.setAntiAlias(true);
    canvas->drawPath(path, paint);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setColor(SK_ColorRED);
    canvas->drawPath(path, paint);
}

static void test_rev(SkCanvas* canvas, const SkPath& path) {
    test_path(canvas, path);

    SkPath rev;
    rev.reverseAddPath(path);
    canvas->save();
    canvas->translate(150, 0);
    test_path(canvas, rev);
    canvas->restore();
}

static void test_rev(SkCanvas* canvas) {
    SkRect r = { 10, 10, 100, 60 };

    SkPath path;

    path.addRect(r); test_rev(canvas, path);

    canvas->translate(0, 100);
    path.offset(20, 20);
    path.addRect(r); test_rev(canvas, path);

    canvas->translate(0, 100);
    path.reset();
    path.moveTo(10, 10); path.lineTo(30, 30);
    path.addOval(r);
    r.offset(50, 20);
    path.addOval(r);
    test_rev(canvas, path);

    SkPaint paint;
    paint.setTextSize(SkIntToScalar(100));
    SkTypeface* hira = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal);
    SkSafeUnref(paint.setTypeface(hira));
    path.reset();
    paint.getTextPath("e", 1, 50, 50, &path);
    canvas->translate(0, 100);
    test_rev(canvas, path);
}

namespace skiagm {

class StrokeFillGM : public GM {
public:
    StrokeFillGM() {

    }

protected:
    virtual SkString onShortName() {
        return SkString("stroke-fill");
    }

    virtual SkISize onISize() {
        return make_isize(640, 480);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint;
        const char text[] = "Hello"; // "Hello";
        const size_t len = sizeof(text) - 1;
        paint.setAntiAlias(true);
        paint.setTextSize(SkIntToScalar(100));
        SkTypeface* hira = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro",
                                                      SkTypeface::kNormal);
        paint.setTypeface(hira);
        SkScalar x = 180;
        SkScalar y = 88;

        canvas->drawText(text, len, x, y, paint);
        paint.setFakeBoldText(true);
        canvas->drawText(text, len, x, y + 100, paint);
        paint.setStyle(SkPaint::kStrokeAndFill_Style);
        paint.setStrokeWidth(5);

        SkPath path;
        path.setFillType(SkPath::kWinding_FillType);
        path.addCircle(x, y + 200, 50, SkPath::kCW_Direction);
        path.addCircle(x, y + 200, 40, SkPath::kCCW_Direction);
        canvas->drawPath(path, paint);

        SkPath path2;
        path2.setFillType(SkPath::kWinding_FillType);
        path2.addCircle(x + 120, y + 200, 50, SkPath::kCCW_Direction);
        path2.addCircle(x + 120, y + 200, 40, SkPath::kCW_Direction);
        canvas->drawPath(path2, paint);

        path2.reset();
        path2.addCircle(x + 240, y + 200, 50, SkPath::kCCW_Direction);
        canvas->drawPath(path2, paint);
        SkASSERT(path2.cheapIsDirection(SkPath::kCCW_Direction));

        path2.reset();
        SkASSERT(!path2.cheapComputeDirection(NULL));
        path2.addCircle(x + 360, y + 200, 50, SkPath::kCW_Direction);
        SkASSERT(path2.cheapIsDirection(SkPath::kCW_Direction));
        canvas->drawPath(path2, paint);
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new StrokeFillGM; }
static GMRegistry reg(MyFactory);

}
<commit_msg>use SkIntToScalar<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkTypeface.h"

static void test_path(SkCanvas* canvas, const SkPath& path) {
    SkPaint paint;
    paint.setAntiAlias(true);
    canvas->drawPath(path, paint);

    paint.setStyle(SkPaint::kStroke_Style);
    paint.setColor(SK_ColorRED);
    canvas->drawPath(path, paint);
}

static void test_rev(SkCanvas* canvas, const SkPath& path) {
    test_path(canvas, path);

    SkPath rev;
    rev.reverseAddPath(path);
    canvas->save();
    canvas->translate(SkIntToScalar(150), 0);
    test_path(canvas, rev);
    canvas->restore();
}

static void test_rev(SkCanvas* canvas) {
    SkRect r = { 10, 10, 100, 60 };

    SkPath path;

    path.addRect(r); test_rev(canvas, path);

    canvas->translate(0, 100);
    path.offset(20, 20);
    path.addRect(r); test_rev(canvas, path);

    canvas->translate(0, 100);
    path.reset();
    path.moveTo(10, 10); path.lineTo(30, 30);
    path.addOval(r);
    r.offset(50, 20);
    path.addOval(r);
    test_rev(canvas, path);

    SkPaint paint;
    paint.setTextSize(SkIntToScalar(100));
    SkTypeface* hira = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal);
    SkSafeUnref(paint.setTypeface(hira));
    path.reset();
    paint.getTextPath("e", 1, 50, 50, &path);
    canvas->translate(0, 100);
    test_rev(canvas, path);
}

namespace skiagm {

class StrokeFillGM : public GM {
public:
    StrokeFillGM() {

    }

protected:
    virtual SkString onShortName() {
        return SkString("stroke-fill");
    }

    virtual SkISize onISize() {
        return make_isize(640, 480);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint;
        const char text[] = "Hello"; // "Hello";
        const size_t len = sizeof(text) - 1;
        paint.setAntiAlias(true);
        paint.setTextSize(SkIntToScalar(100));
        SkTypeface* hira = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro",
                                                      SkTypeface::kNormal);
        paint.setTypeface(hira);
        SkScalar x = SkIntToScalar(180);
        SkScalar y = SkIntToScalar(88);

        canvas->drawText(text, len, x, y, paint);
        paint.setFakeBoldText(true);
        canvas->drawText(text, len, x, y + SkIntToScalar(100), paint);
        paint.setStyle(SkPaint::kStrokeAndFill_Style);
        paint.setStrokeWidth(SkIntToScalar(5));

        SkPath path;
        path.setFillType(SkPath::kWinding_FillType);
        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCCW_Direction);
        canvas->drawPath(path, paint);

        SkPath path2;
        path2.setFillType(SkPath::kWinding_FillType);
        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCW_Direction);
        canvas->drawPath(path2, paint);

        path2.reset();
        path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
        canvas->drawPath(path2, paint);
        SkASSERT(path2.cheapIsDirection(SkPath::kCCW_Direction));

        path2.reset();
        SkASSERT(!path2.cheapComputeDirection(NULL));
        path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
        SkASSERT(path2.cheapIsDirection(SkPath::kCW_Direction));
        canvas->drawPath(path2, paint);
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new StrokeFillGM; }
static GMRegistry reg(MyFactory);

}
<|endoftext|>


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

#ifndef _DBA_CORE_USERINFORMATION_HXX_
#define _DBA_CORE_USERINFORMATION_HXX_

#ifndef _COM_SUN_STAR_LANG_LOCALE_HPP_
#include <com/sun/star/lang/Locale.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

class UserInformation
{
    ::com::sun::star::lang::Locale  m_aUserLocale;

public:
    UserInformation();

    ::com::sun::star::lang::Locale      getUserLanguage() const { return m_aUserLocale; }
};

#endif // _DBA_CORE_USERINFORMATION_HXX_

<commit_msg>INTEGRATION: CWS ooo19126 (1.3.494); FILE MERGED 2005/09/05 17:32:32 rt 1.3.494.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: userinformation.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 13:54:28 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _DBA_CORE_USERINFORMATION_HXX_
#define _DBA_CORE_USERINFORMATION_HXX_

#ifndef _COM_SUN_STAR_LANG_LOCALE_HPP_
#include <com/sun/star/lang/Locale.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif

class UserInformation
{
    ::com::sun::star::lang::Locale  m_aUserLocale;

public:
    UserInformation();

    ::com::sun::star::lang::Locale      getUserLanguage() const { return m_aUserLocale; }
};

#endif // _DBA_CORE_USERINFORMATION_HXX_

<|endoftext|>