<commit_before>#include "SFZeroAudioProcessor.h"
#include "SFZeroEditor.h"
#include "SFZSound.h"
#include "SFZVoice.h"
#include "SFZDebug.h"

SFZeroAudioProcessor::SFZeroAudioProcessor()
	: loadProgress(0.0), loadThread(this)
{
#if JUCE_DEBUG
	setupLogging(
		FileLogger::createDefaultAppLogger(
			"SFZero", "SFZero.log", "SFZero started"));
#endif

	formatManager.registerFormat(new WavAudioFormat(), false);
	formatManager.registerFormat(new OggVorbisAudioFormat(), false);

	for (int i = 0; i < 32; ++i)
		synth.addVoice(new SFZVoice());
}

SFZeroAudioProcessor::~SFZeroAudioProcessor()
{
}

const String SFZeroAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

int SFZeroAudioProcessor::getNumParameters()
{
    return 0;
}

float SFZeroAudioProcessor::getParameter(int index)
{
    return 0.0f;
}

void SFZeroAudioProcessor::setParameter(int index, float newValue)
{
}

const String SFZeroAudioProcessor::getParameterName(int index)
{
    return String::empty;
}

const String SFZeroAudioProcessor::getParameterText(int index)
{
    return String::empty;
}

void SFZeroAudioProcessor::setSfzFile(File* newSfzFile)
{
	sfzFile = *newSfzFile;
	loadSound();
}

void SFZeroAudioProcessor::setSfzFileThreaded(File* newSfzFile)
{
	loadThread.stopThread(2000);
	sfzFile = *newSfzFile;
	loadThread.startThread();
}

const String SFZeroAudioProcessor::getInputChannelName(int channelIndex) const
{
    return String(channelIndex + 1);
}

const String SFZeroAudioProcessor::getOutputChannelName(int channelIndex) const
{
    return String(channelIndex + 1);
}

bool SFZeroAudioProcessor::isInputChannelStereoPair(int index) const
{
    return true;
}

bool SFZeroAudioProcessor::isOutputChannelStereoPair(int index) const
{
    return true;
}

bool SFZeroAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
    return true;
#else
    return false;
#endif
}

bool SFZeroAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
    return true;
#else
    return false;
#endif
}

int SFZeroAudioProcessor::getNumPrograms()
{
    return 0;
}

int SFZeroAudioProcessor::getCurrentProgram()
{
    return 0;
}

void SFZeroAudioProcessor::setCurrentProgram(int index)
{
}

const String SFZeroAudioProcessor::getProgramName(int index)
{
    return String::empty;
}

void SFZeroAudioProcessor::changeProgramName(int index, const String& newName)
{
}

void SFZeroAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
	synth.setCurrentPlaybackSampleRate(sampleRate);
	keyboardState.reset();
}

void SFZeroAudioProcessor::releaseResources()
{
	// When playback stops, you can use this as an opportunity to free up any
	// spare memory, etc.
	keyboardState.reset();
}

void SFZeroAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
	int numSamples = buffer.getNumSamples();
	keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true);
	synth.renderNextBlock(buffer, midiMessages, 0, numSamples);
}

bool SFZeroAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

AudioProcessorEditor* SFZeroAudioProcessor::createEditor()
{
	return new SFZeroEditor(this);
}

void SFZeroAudioProcessor::getStateInformation(MemoryBlock& destData)
{
	DynamicObject state;
	state.setProperty("sfzFilePath", sfzFile.getFullPathName());
	MemoryOutputStream out(destData, false);
	JSON::writeToStream(out, &state);
}

void SFZeroAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
{
	MemoryInputStream in(data, sizeInBytes, false);
	var state = JSON::parse(in);
	var pathVar = state["sfzFilePath"];
	if (pathVar.isString()) {
		String sfzFilePath = pathVar.toString();
		if (!sfzFilePath.isEmpty()) {
			File file(sfzFilePath);
			setSfzFile(&file);
			}
		}
}

SFZSound* SFZeroAudioProcessor::getSound()
{
	SynthesiserSound* sound = synth.getSound(0);
	return dynamic_cast<SFZSound*>(sound);
}

#if JUCE_DEBUG
void SFZeroAudioProcessor::relayLogMessages()
{
	relayFifoLogMessages();
}
#endif

void SFZeroAudioProcessor::loadSound(Thread* thread)
{
	loadProgress = 0.0;
	synth.clearSounds();

	if (!sfzFile.existsAsFile()) {
		//***
		return;
		}

	SFZSound* sound = new SFZSound(sfzFile);
	sound->loadRegions();
	sound->loadSamples(&formatManager, &loadProgress, thread);
	if (thread && thread->threadShouldExit()) {
		delete sound;
		return;
		}

	synth.addSound(sound);
}

SFZeroAudioProcessor::LoadThread::LoadThread(SFZeroAudioProcessor* processorIn)
	: Thread("SFZLoad"), processor(processorIn)
{
}

void SFZeroAudioProcessor::LoadThread::run()
{
	processor->loadSound(this);
}

AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new SFZeroAudioProcessor();
}

<commit_msg>SF2: Using SF2Sound when appropriate.<commit_after>#include "SFZeroAudioProcessor.h"
#include "SFZeroEditor.h"
#include "SFZSound.h"
#include "SF2Sound.h"
#include "SFZVoice.h"
#include "SFZDebug.h"

SFZeroAudioProcessor::SFZeroAudioProcessor()
	: loadProgress(0.0), loadThread(this)
{
#if JUCE_DEBUG
	setupLogging(
		FileLogger::createDefaultAppLogger(
			"SFZero", "SFZero.log", "SFZero started"));
#endif

	formatManager.registerFormat(new WavAudioFormat(), false);
	formatManager.registerFormat(new OggVorbisAudioFormat(), false);

	for (int i = 0; i < 32; ++i)
		synth.addVoice(new SFZVoice());
}

SFZeroAudioProcessor::~SFZeroAudioProcessor()
{
}

const String SFZeroAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

int SFZeroAudioProcessor::getNumParameters()
{
    return 0;
}

float SFZeroAudioProcessor::getParameter(int index)
{
    return 0.0f;
}

void SFZeroAudioProcessor::setParameter(int index, float newValue)
{
}

const String SFZeroAudioProcessor::getParameterName(int index)
{
    return String::empty;
}

const String SFZeroAudioProcessor::getParameterText(int index)
{
    return String::empty;
}

void SFZeroAudioProcessor::setSfzFile(File* newSfzFile)
{
	sfzFile = *newSfzFile;
	loadSound();
}

void SFZeroAudioProcessor::setSfzFileThreaded(File* newSfzFile)
{
	loadThread.stopThread(2000);
	sfzFile = *newSfzFile;
	loadThread.startThread();
}

const String SFZeroAudioProcessor::getInputChannelName(int channelIndex) const
{
    return String(channelIndex + 1);
}

const String SFZeroAudioProcessor::getOutputChannelName(int channelIndex) const
{
    return String(channelIndex + 1);
}

bool SFZeroAudioProcessor::isInputChannelStereoPair(int index) const
{
    return true;
}

bool SFZeroAudioProcessor::isOutputChannelStereoPair(int index) const
{
    return true;
}

bool SFZeroAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
    return true;
#else
    return false;
#endif
}

bool SFZeroAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
    return true;
#else
    return false;
#endif
}

int SFZeroAudioProcessor::getNumPrograms()
{
    return 0;
}

int SFZeroAudioProcessor::getCurrentProgram()
{
    return 0;
}

void SFZeroAudioProcessor::setCurrentProgram(int index)
{
}

const String SFZeroAudioProcessor::getProgramName(int index)
{
    return String::empty;
}

void SFZeroAudioProcessor::changeProgramName(int index, const String& newName)
{
}

void SFZeroAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
	synth.setCurrentPlaybackSampleRate(sampleRate);
	keyboardState.reset();
}

void SFZeroAudioProcessor::releaseResources()
{
	// When playback stops, you can use this as an opportunity to free up any
	// spare memory, etc.
	keyboardState.reset();
}

void SFZeroAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
	int numSamples = buffer.getNumSamples();
	keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true);
	synth.renderNextBlock(buffer, midiMessages, 0, numSamples);
}

bool SFZeroAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

AudioProcessorEditor* SFZeroAudioProcessor::createEditor()
{
	return new SFZeroEditor(this);
}

void SFZeroAudioProcessor::getStateInformation(MemoryBlock& destData)
{
	DynamicObject state;
	state.setProperty("sfzFilePath", sfzFile.getFullPathName());
	MemoryOutputStream out(destData, false);
	JSON::writeToStream(out, &state);
}

void SFZeroAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
{
	MemoryInputStream in(data, sizeInBytes, false);
	var state = JSON::parse(in);
	var pathVar = state["sfzFilePath"];
	if (pathVar.isString()) {
		String sfzFilePath = pathVar.toString();
		if (!sfzFilePath.isEmpty()) {
			File file(sfzFilePath);
			setSfzFile(&file);
			}
		}
}

SFZSound* SFZeroAudioProcessor::getSound()
{
	SynthesiserSound* sound = synth.getSound(0);
	return dynamic_cast<SFZSound*>(sound);
}

#if JUCE_DEBUG
void SFZeroAudioProcessor::relayLogMessages()
{
	relayFifoLogMessages();
}
#endif

void SFZeroAudioProcessor::loadSound(Thread* thread)
{
	loadProgress = 0.0;
	synth.clearSounds();

	if (!sfzFile.existsAsFile()) {
		//***
		return;
		}

	SFZSound* sound;
	String extension = sfzFile.getFileExtension();
	if (extension == "sf2" || extension == "SF2")
		sound = new SF2Sound(sfzFile);
	else
		sound = new SFZSound(sfzFile);
	sound->loadRegions();
	sound->loadSamples(&formatManager, &loadProgress, thread);
	if (thread && thread->threadShouldExit()) {
		delete sound;
		return;
		}

	synth.addSound(sound);
}

SFZeroAudioProcessor::LoadThread::LoadThread(SFZeroAudioProcessor* processorIn)
	: Thread("SFZLoad"), processor(processorIn)
{
}

void SFZeroAudioProcessor::LoadThread::run()
{
	processor->loadSound(this);
}

AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new SFZeroAudioProcessor();
}

<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2016 The J-PET Framework 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 find a copy of the License in the LICENCE file.
 *
 *  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.
 *
 *  @file JPetTaskChainExecutor.cpp
 */

#include "JPetTaskChainExecutor.h"

#include <cassert>
#include <memory>
#include <chrono>

#include "JPetTaskChainExecutorUtils.h"
#include "../JPetLoggerInclude.h"

JPetTaskChainExecutor::JPetTaskChainExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, const OptionsPerFile& opts):
  fInputSeqId(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain)
{
  assert(taskGeneratorChain->size() == opts.size());
  /// ParamManager is generated and added to fParams
  fParams = JPetTaskChainExecutorUtils::generateParams(opts);
  assert(fParams.front().getParamManager());
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskChainExecutor");
  }
}

bool JPetTaskChainExecutor::preprocessing(const std::vector<JPetParams>& params)
{
  if (params.empty()) {
    ERROR("No parameters provided!");
    return false;
  } else {
    return JPetTaskChainExecutorUtils::process(params.front());
  }
}

bool JPetTaskChainExecutor::process()
{
  JPetTimer timer;
  timer.startMeasurement();

  if (!preprocessing(fParams)) {
    ERROR("Error in preprocessing phase");
    return false;
  }

  timer.stopMeasurement("Preprocessing");

  JPetDataInterface nullDataObject;
  JPetParams outputParams;

  assert(fTasks.size() == fParams.size());
  auto currParamsIt = fParams.begin();

  /// We iterate over both tasks and parameters
  for (auto currentTaskIt = fTasks.begin(); currentTaskIt != fTasks.end(); currentTaskIt++) {

    auto currentTask  =  *currentTaskIt;
    auto taskName = currentTask->getName();

    assert(currParamsIt != fParams.end());
    auto currParams = *currParamsIt;
    jpet_options_tools::printOptionsToLog(currParams.getOptions(), std::string("Options for ") + taskName);
    currParamsIt++;

    timer.startMeasurement();
    INFO(Form("Starting task: %s", taskName.c_str()));
    if (!currentTask->init(currParams)) {
      ERROR("In task initialization");
      return false;
    }
    if (!currentTask->run(nullDataObject)) {
      ERROR("In task run()");
      return false;
    }
    if (!currentTask->terminate(outputParams)) {
      ERROR("In task terminate() ");
      return false;
    }

    timer.stopMeasurement("task " + taskName);
  }
  timer.printElapsedTimeToInfo();
  timer.printTotalElapsedTimeToInfo();
  return true;
}

void* JPetTaskChainExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskChainExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskChainExecutor::run()
{
  TThread* thread = new TThread(std::to_string(fInputSeqId).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

JPetTaskChainExecutor::~JPetTaskChainExecutor()
{
  for (auto& task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<commit_msg>Adapt to new JPetTimer methods<commit_after>/**
 *  @copyright Copyright 2016 The J-PET Framework 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 find a copy of the License in the LICENCE file.
 *
 *  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.
 *
 *  @file JPetTaskChainExecutor.cpp
 */

#include "JPetTaskChainExecutor.h"

#include <cassert>
#include <memory>
#include <chrono>

#include "JPetTaskChainExecutorUtils.h"
#include "../JPetLoggerInclude.h"

JPetTaskChainExecutor::JPetTaskChainExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, const OptionsPerFile& opts):
  fInputSeqId(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain)
{
  assert(taskGeneratorChain->size() == opts.size());
  /// ParamManager is generated and added to fParams
  fParams = JPetTaskChainExecutorUtils::generateParams(opts);
  assert(fParams.front().getParamManager());
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskChainExecutor");
  }
}

bool JPetTaskChainExecutor::preprocessing(const std::vector<JPetParams>& params)
{
  if (params.empty()) {
    ERROR("No parameters provided!");
    return false;
  } else {
    return JPetTaskChainExecutorUtils::process(params.front());
  }
}

bool JPetTaskChainExecutor::process()
{
  JPetTimer timer;
  timer.startMeasurement();

  if (!preprocessing(fParams)) {
    ERROR("Error in preprocessing phase");
    return false;
  }

  timer.stopMeasurement("Preprocessing");

  JPetDataInterface nullDataObject;
  JPetParams outputParams;

  assert(fTasks.size() == fParams.size());
  auto currParamsIt = fParams.begin();

  /// We iterate over both tasks and parameters
  for (auto currentTaskIt = fTasks.begin(); currentTaskIt != fTasks.end(); currentTaskIt++) {

    auto currentTask  =  *currentTaskIt;
    auto taskName = currentTask->getName();

    assert(currParamsIt != fParams.end());
    auto currParams = *currParamsIt;
    jpet_options_tools::printOptionsToLog(currParams.getOptions(), std::string("Options for ") + taskName);
    currParamsIt++;

    timer.startMeasurement();
    INFO(Form("Starting task: %s", taskName.c_str()));
    if (!currentTask->init(currParams)) {
      ERROR("In task initialization");
      return false;
    }
    if (!currentTask->run(nullDataObject)) {
      ERROR("In task run()");
      return false;
    }
    if (!currentTask->terminate(outputParams)) {
      ERROR("In task terminate() ");
      return false;
    }

    timer.stopMeasurement("task " + taskName);
  }
  INFO(timer.getElapsedTime());
  INFO(timer.getTotalElapsedTime());
  return true;
}

void* JPetTaskChainExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskChainExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskChainExecutor::run()
{
  TThread* thread = new TThread(std::to_string(fInputSeqId).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

JPetTaskChainExecutor::~JPetTaskChainExecutor()
{
  for (auto& task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<|endoftext|>


<commit_before>/******************************************************************************\
* File:          dialog.cpp
* Purpose:       Implementation of wxExDialog class
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/persist/toplevel.h>
#include <wx/extension/dialog.h>

#if wxUSE_GUI
BEGIN_EVENT_TABLE(wxExDialog, wxDialog)
  EVT_CHAR_HOOK(wxExDialog::OnKeyDown)
END_EVENT_TABLE()

wxExDialog::wxExDialog(wxWindow* parent,
  const wxString& title,
  long button_flags,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size,
  long style)
  : wxDialog(parent, id, title, pos, size, style)
  , m_ButtonFlags(button_flags)
  , m_TopSizer(new wxFlexGridSizer(1, 0, 0))
  , m_UserSizer(new wxFlexGridSizer(1, 0, 0))
{
  SetName(title);
}

wxSizerItem* wxExDialog::AddUserSizer(
  wxWindow* window,
  const wxSizerFlags& flags)
{
  wxSizerItem* item = m_UserSizer->Add(window, flags);

  if (flags.GetFlags() & wxEXPAND)
  {
    m_UserSizer->AddGrowableRow(m_UserSizer->GetChildren().GetCount() - 1);
  }

  return item;
}

wxSizerItem* wxExDialog::AddUserSizer(
  wxSizer* sizer,
  const wxSizerFlags& flags)
{
  wxSizerItem* item = m_UserSizer->Add(sizer, flags);

  if (flags.GetFlags() & wxEXPAND)
  {
    m_UserSizer->AddGrowableRow(m_UserSizer->GetChildren().GetCount() - 1);
  }

  return item;
}

void wxExDialog::LayoutSizers(bool add_separator_line)
{
  m_TopSizer->AddGrowableCol(0);
  m_UserSizer->AddGrowableCol(0);

  wxSizerFlags flag;
  flag.Expand().Center().Border();

  // The top sizer starts with a spacer, for a nice border.
  m_TopSizer->AddSpacer(wxSizerFlags::GetDefaultBorder());

  // Then place the growable user sizer.
  m_TopSizer->Add(m_UserSizer, flag);
  // So this is the user sizer, make the row growable.
  m_TopSizer->AddGrowableRow(m_TopSizer->GetChildren().GetCount() - 1);

  // Then, if buttons were specified, the button sizer.
  if (m_ButtonFlags != 0)
  {
    wxSizer* sizer = (add_separator_line ?
      CreateSeparatedButtonSizer(m_ButtonFlags):
      CreateButtonSizer(m_ButtonFlags));

    if (sizer != NULL)
    {
      m_TopSizer->Add(sizer, flag);
    }
  }

  // The top sizer ends with a spacer as well.
  m_TopSizer->AddSpacer(wxSizerFlags::GetDefaultBorder());

  SetSizerAndFit(m_TopSizer);

  wxPersistentRegisterAndRestore(this);
}

void wxExDialog::OnKeyDown(wxKeyEvent& event)
{
  // If we did not specify any buttons, then
  // use the RETURN key as OK and ESCAPE key as CANCEL.
  if (m_ButtonFlags == 0)
  {
    if (event.GetKeyCode() == WXK_RETURN)
    {
      wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
      wxPostEvent(this, event);
    }
    else if (event.GetKeyCode() == WXK_ESCAPE)
    {
      wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
      wxPostEvent(this, event);
    }
    else
    {
      event.Skip();
    }
  }
  else
  {
    event.Skip();
  }
}

#endif // wxUSE_GUI
<commit_msg>use const flag<commit_after>/******************************************************************************\
* File:          dialog.cpp
* Purpose:       Implementation of wxExDialog class
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/persist/toplevel.h>
#include <wx/extension/dialog.h>

#if wxUSE_GUI
BEGIN_EVENT_TABLE(wxExDialog, wxDialog)
  EVT_CHAR_HOOK(wxExDialog::OnKeyDown)
END_EVENT_TABLE()

wxExDialog::wxExDialog(wxWindow* parent,
  const wxString& title,
  long button_flags,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size,
  long style)
  : wxDialog(parent, id, title, pos, size, style)
  , m_ButtonFlags(button_flags)
  , m_TopSizer(new wxFlexGridSizer(1, 0, 0))
  , m_UserSizer(new wxFlexGridSizer(1, 0, 0))
{
  SetName(title);
}

wxSizerItem* wxExDialog::AddUserSizer(
  wxWindow* window,
  const wxSizerFlags& flags)
{
  wxSizerItem* item = m_UserSizer->Add(window, flags);

  if (flags.GetFlags() & wxEXPAND)
  {
    m_UserSizer->AddGrowableRow(m_UserSizer->GetChildren().GetCount() - 1);
  }

  return item;
}

wxSizerItem* wxExDialog::AddUserSizer(
  wxSizer* sizer,
  const wxSizerFlags& flags)
{
  wxSizerItem* item = m_UserSizer->Add(sizer, flags);

  if (flags.GetFlags() & wxEXPAND)
  {
    m_UserSizer->AddGrowableRow(m_UserSizer->GetChildren().GetCount() - 1);
  }

  return item;
}

void wxExDialog::LayoutSizers(bool add_separator_line)
{
  m_TopSizer->AddGrowableCol(0);
  m_UserSizer->AddGrowableCol(0);

  const wxSizerFlags flag = wxSizerFlags().Expand().Center().Border();

  // The top sizer starts with a spacer, for a nice border.
  m_TopSizer->AddSpacer(wxSizerFlags::GetDefaultBorder());

  // Then place the growable user sizer.
  m_TopSizer->Add(m_UserSizer, flag);
  // So this is the user sizer, make the row growable.
  m_TopSizer->AddGrowableRow(m_TopSizer->GetChildren().GetCount() - 1);

  // Then, if buttons were specified, the button sizer.
  if (m_ButtonFlags != 0)
  {
    wxSizer* sizer = (add_separator_line ?
      CreateSeparatedButtonSizer(m_ButtonFlags):
      CreateButtonSizer(m_ButtonFlags));

    if (sizer != NULL)
    {
      m_TopSizer->Add(sizer, flag);
    }
  }

  // The top sizer ends with a spacer as well.
  m_TopSizer->AddSpacer(wxSizerFlags::GetDefaultBorder());

  SetSizerAndFit(m_TopSizer);

  wxPersistentRegisterAndRestore(this);
}

void wxExDialog::OnKeyDown(wxKeyEvent& event)
{
  // If we did not specify any buttons, then
  // use the RETURN key as OK and ESCAPE key as CANCEL.
  if (m_ButtonFlags == 0)
  {
    if (event.GetKeyCode() == WXK_RETURN)
    {
      wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
      wxPostEvent(this, event);
    }
    else if (event.GetKeyCode() == WXK_ESCAPE)
    {
      wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
      wxPostEvent(this, event);
    }
    else
    {
      event.Skip();
    }
  }
  else
  {
    event.Skip();
  }
}

#endif // wxUSE_GUI
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    wizard(new Wizard(this)),
    world(NULL)
{
    ui->setupUi(this);

    rooms_list = new RoomsList;
    room_view = new RoomView;
    settings = new SettingsWidget;

    ui->splitter->addWidget(rooms_list);
    widget = new QWidget;
    layout = new QGridLayout;
    vspacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
    hspacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
    layout->addWidget(room_view, 0, 0);
    layout->addItem(hspacer, 0, 1);
    layout->addItem(vspacer, 1, 0, 1, 2);
    layout->setMargin(0);
    widget->setLayout(layout);
    ui->splitter->addWidget(widget);
    ui->splitter->addWidget(settings);

    ui->centralWidget->setDisabled(true);

    connect(room_view, SIGNAL(roomChanged(Room*)), room_view, SIGNAL(selected(Room*)));
    connect(room_view, SIGNAL(selected(Room*)), settings, SLOT(updateRoomSettings(Room*)));
    connect(room_view, SIGNAL(selected(Area*)), settings, SLOT(updateAreaSettings(Area*)));
    connect(ui->action_Save, SIGNAL(triggered()), this, SLOT(saveProject()));
    connect(ui->action_Open, SIGNAL(triggered()), this, SLOT(openProject()));
    connect(ui->action_New, SIGNAL(triggered()), wizard, SLOT(show()));
    connect(ui->action_Quit, SIGNAL(triggered()), this, SLOT(close()));

    connect(rooms_list, SIGNAL(selected(QModelIndex)),
            room_view, SLOT(changeActiveRoom(QModelIndex)));

    connect(wizard, SIGNAL(accepted()), this, SLOT(newProject()));

    adjustSize();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete wizard;
}

void MainWindow::saveProject()
{
    QString project_filename = QFileDialog::getSaveFileName(this, "Save project", QDir::homePath());
    QFile file(project_filename);
    if (!file.open(QIODevice::WriteOnly))
        return;

    file.write(createXml().toAscii());
    file.close();
}

void MainWindow::openProject()
{
    QDomDocument doc("RoomsProjectFile");
    QString project_filename = QFileDialog::getOpenFileName(this, "Open project",
                                                            QDir::homePath(),
                                                            "Rooms project (*.rooms)");
    QFile file(project_filename);
    if (!file.open(QIODevice::ReadOnly))
        return;
    if (!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    if (world != NULL)
        delete world;
    world = createWorld(doc);
    rooms_list->setWorld(world);
    room_view->setWorld(world);
    settings->setWorld(world);
    adjustSize();
    ui->centralWidget->setEnabled(true);
}

void MainWindow::newProject()
{
    if (world != NULL)
        delete world;
    world = new World(wizard->worldName(), wizard->worldSize());
    rooms_list->setWorld(world);
    room_view->setWorld(world);
    settings->setWorld(world);
    adjustSize();
    ui->centralWidget->setEnabled(true);
}

QString MainWindow::createXml() const
{
    QString xml;
    QDomDocument doc("RoomsProjectFile");

    QDomElement xworld = doc.createElement("world");
    xworld.setAttribute("version", "ROOMS_VANILLA");
    xworld.setAttribute("name", world->name());
    xworld.setAttribute("width", world->size().width());
    xworld.setAttribute("height", world->size().height());
    xworld.setAttribute("start", 0);
    doc.appendChild(xworld);

    //<images>
    //this block is useless for now because <img> contains only the filepath, which is
    //already included as "bg" attribute of <room>
    QDomElement ximages = doc.createElement("images");
    for (int i = 0; i < world->rooms()->count(); i++)
    {
        QDomElement ximg = doc.createElement("img");
        ximg.setAttribute("file", world->rooms()->at(i)->name());
        ximages.appendChild(ximg);
    }
    xworld.appendChild(ximages);
    //</images>

    //<rooms>
    QDomElement xrooms = doc.createElement("rooms");
    for (int i = 0; i < world->rooms()->count(); i++)
    {
        QDomElement xroom = doc.createElement("room");
        xroom.setAttribute("id", world->rooms()->at(i)->name());
        xroom.setAttribute("bg", world->rooms()->at(i)->name());
        QDomElement xareas = doc.createElement("areas");
        for (int j = 0; j < world->rooms()->at(i)->areas().count(); j++)
        {
            QDomElement xarea = doc.createElement("area");
            xarea.setAttribute("id", world->rooms()->at(i)->areas().at(j)->name());
            xarea.setAttribute("x", world->rooms()->at(i)->areas().at(j)->rect().x());
            xarea.setAttribute("y", world->rooms()->at(i)->areas().at(j)->rect().y());
            xarea.setAttribute("width", world->rooms()->at(i)->areas().at(j)->rect().width());
            xarea.setAttribute("height", world->rooms()->at(i)->areas().at(j)->rect().height());
            xareas.appendChild(xarea);
        }
        xroom.appendChild(xareas);
        xrooms.appendChild(xroom);
    }
    xworld.appendChild(xrooms);
    //</rooms>

    xml = doc.toString();
    return xml;
}

World *MainWindow::createWorld(const QDomDocument &doc)
{
    QDomElement xworld = doc.elementsByTagName("world").at(0).toElement();
    World *world = new World(xworld.attribute("name"), QSize(xworld.attribute("width").toInt(),
                                                             xworld.attribute("height").toInt()));

    //<images>
    //this block is useless for now because <img> contains only the filepath, which is
    //already included as "bg" attribute of <room>
    RoomsModel *rooms = world->rooms();
    QDomNode ximages = xworld.elementsByTagName("images").at(0);
    QDomElement ximg = ximages.firstChildElement();
    while (!ximg.isNull())
    {
        ximg = ximg.nextSiblingElement();
    }
    //</images>

    //<rooms>
    QDomNode xrooms = xworld.elementsByTagName("rooms").at(0);
    QDomElement xroom = xrooms.firstChildElement();
    while (!xroom.isNull())
    {
        rooms->appendRoom();
        Room *room = rooms->at(rooms->count()-1);
        QString id(xroom.attribute("id"));
        QString bg_file(xroom.attribute("bg"));
        QPixmap bg(bg_file);
        bg = bg.scaled(world->size());
        room->setName(id);
        room->setBackground(bg);
        //<areas>
        QDomNode xareas = xroom.firstChild();
        QDomElement xarea = xareas.firstChildElement();
        while (!xarea.isNull())
        {
            QString id(xarea.attribute("id"));
            QRect rect(xarea.attribute("x").toInt(),
                       xarea.attribute("y").toInt(),
                       xarea.attribute("width").toInt(),
                       xarea.attribute("height").toInt());
            room->addArea(rect);
            room->areas().at(room->areas().count()-1)->setName(id);
            xarea = xarea.nextSiblingElement();
        }
        //</areas>
        xroom = xroom.nextSiblingElement();
    }
    //</rooms>

    return world;
}
<commit_msg>adding support for saving background images<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    wizard(new Wizard(this)),
    world(NULL)
{
    ui->setupUi(this);

    rooms_list = new RoomsList;
    room_view = new RoomView;
    settings = new SettingsWidget;

    ui->splitter->addWidget(rooms_list);
    widget = new QWidget;
    layout = new QGridLayout;
    vspacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
    hspacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
    layout->addWidget(room_view, 0, 0);
    layout->addItem(hspacer, 0, 1);
    layout->addItem(vspacer, 1, 0, 1, 2);
    layout->setMargin(0);
    widget->setLayout(layout);
    ui->splitter->addWidget(widget);
    ui->splitter->addWidget(settings);

    ui->centralWidget->setDisabled(true);

    connect(room_view, SIGNAL(roomChanged(Room*)), room_view, SIGNAL(selected(Room*)));
    connect(room_view, SIGNAL(selected(Room*)), settings, SLOT(updateRoomSettings(Room*)));
    connect(room_view, SIGNAL(selected(Area*)), settings, SLOT(updateAreaSettings(Area*)));
    connect(ui->action_Save, SIGNAL(triggered()), this, SLOT(saveProject()));
    connect(ui->action_Open, SIGNAL(triggered()), this, SLOT(openProject()));
    connect(ui->action_New, SIGNAL(triggered()), wizard, SLOT(show()));
    connect(ui->action_Quit, SIGNAL(triggered()), this, SLOT(close()));

    connect(rooms_list, SIGNAL(selected(QModelIndex)),
            room_view, SLOT(changeActiveRoom(QModelIndex)));

    connect(wizard, SIGNAL(accepted()), this, SLOT(newProject()));

    adjustSize();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete wizard;
}

void MainWindow::saveProject()
{
    QString project_filename = QFileDialog::getSaveFileName(this, "Save project", QDir::homePath());
    QDir::setCurrent(project_filename.section("/", 0, -2));
    QFile file(project_filename);
    if (!file.open(QIODevice::WriteOnly))
        return;

    file.write(createXml().toAscii());
    file.close();

    QDir data_dir(QDir::currentPath() + "/" + world->name() + "_data");
    data_dir.mkpath(data_dir.absolutePath());

    for (int i = 0; i < world->rooms()->count(); i++)
    {
        world->rooms()->at(i)->background().save(data_dir.absolutePath() + "/" +
                                                 world->rooms()->at(i)->name() + "_bg.png");
    }
}

void MainWindow::openProject()
{
    QDomDocument doc("RoomsProjectFile");
    QString project_filename = QFileDialog::getOpenFileName(this, "Open project",
                                                            QDir::homePath(),
                                                            "Rooms project (*.rooms)");
    QFile file(project_filename);
    if (!file.open(QIODevice::ReadOnly))
        return;
    if (!doc.setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    if (world != NULL)
        delete world;
    world = createWorld(doc);
    rooms_list->setWorld(world);
    room_view->setWorld(world);
    settings->setWorld(world);
    adjustSize();
    ui->centralWidget->setEnabled(true);
}

void MainWindow::newProject()
{
    if (world != NULL)
        delete world;
    world = new World(wizard->worldName(), wizard->worldSize());
    rooms_list->setWorld(world);
    room_view->setWorld(world);
    settings->setWorld(world);
    adjustSize();
    ui->centralWidget->setEnabled(true);
}

QString MainWindow::createXml() const
{
    QString xml;
    QDomDocument doc("RoomsProjectFile");

    QDomElement xworld = doc.createElement("world");
    xworld.setAttribute("version", "ROOMS_VANILLA");
    xworld.setAttribute("name", world->name());
    xworld.setAttribute("width", world->size().width());
    xworld.setAttribute("height", world->size().height());
    xworld.setAttribute("start", 0);
    doc.appendChild(xworld);

    //<images>
    //this block is useless for now because <img> contains only the filepath, which is
    //already included as "bg" attribute of <room>
    QDomElement ximages = doc.createElement("images");
    for (int i = 0; i < world->rooms()->count(); i++)
    {
        QDomElement ximg = doc.createElement("img");
        ximg.setAttribute("file", world->rooms()->at(i)->name());
        ximages.appendChild(ximg);
    }
    xworld.appendChild(ximages);
    //</images>

    //<rooms>
    QDomElement xrooms = doc.createElement("rooms");
    for (int i = 0; i < world->rooms()->count(); i++)
    {
        QDomElement xroom = doc.createElement("room");
        xroom.setAttribute("id", world->rooms()->at(i)->name());
        xroom.setAttribute("bg", world->rooms()->at(i)->name());
        QDomElement xareas = doc.createElement("areas");
        for (int j = 0; j < world->rooms()->at(i)->areas().count(); j++)
        {
            QDomElement xarea = doc.createElement("area");
            xarea.setAttribute("id", world->rooms()->at(i)->areas().at(j)->name());
            xarea.setAttribute("x", world->rooms()->at(i)->areas().at(j)->rect().x());
            xarea.setAttribute("y", world->rooms()->at(i)->areas().at(j)->rect().y());
            xarea.setAttribute("width", world->rooms()->at(i)->areas().at(j)->rect().width());
            xarea.setAttribute("height", world->rooms()->at(i)->areas().at(j)->rect().height());
            xareas.appendChild(xarea);
        }
        xroom.appendChild(xareas);
        xrooms.appendChild(xroom);
    }
    xworld.appendChild(xrooms);
    //</rooms>

    xml = doc.toString();
    return xml;
}

World *MainWindow::createWorld(const QDomDocument &doc)
{
    QDomElement xworld = doc.elementsByTagName("world").at(0).toElement();
    World *world = new World(xworld.attribute("name"), QSize(xworld.attribute("width").toInt(),
                                                             xworld.attribute("height").toInt()));

    //<images>
    //this block is useless for now because <img> contains only the filepath, which is
    //already included as "bg" attribute of <room>
    RoomsModel *rooms = world->rooms();
    QDomNode ximages = xworld.elementsByTagName("images").at(0);
    QDomElement ximg = ximages.firstChildElement();
    while (!ximg.isNull())
    {
        ximg = ximg.nextSiblingElement();
    }
    //</images>

    //<rooms>
    QDomNode xrooms = xworld.elementsByTagName("rooms").at(0);
    QDomElement xroom = xrooms.firstChildElement();
    while (!xroom.isNull())
    {
        rooms->appendRoom();
        Room *room = rooms->at(rooms->count()-1);
        QString id(xroom.attribute("id"));
        QString bg_file(xroom.attribute("bg"));
        QPixmap bg(bg_file);
        bg = bg.scaled(world->size());
        room->setName(id);
        room->setBackground(bg);
        //<areas>
        QDomNode xareas = xroom.firstChild();
        QDomElement xarea = xareas.firstChildElement();
        while (!xarea.isNull())
        {
            QString id(xarea.attribute("id"));
            QRect rect(xarea.attribute("x").toInt(),
                       xarea.attribute("y").toInt(),
                       xarea.attribute("width").toInt(),
                       xarea.attribute("height").toInt());
            room->addArea(rect);
            room->areas().at(room->areas().count()-1)->setName(id);
            xarea = xarea.nextSiblingElement();
        }
        //</areas>
        xroom = xroom.nextSiblingElement();
    }
    //</rooms>

    return world;
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 by Pedro Mendes, Rector and Visitors of the
// University of Virginia, University of Heidelberg, and University
// of Connecticut School of Medicine.
// All rights reserved.

// Copyright (C) 2017 - 2018 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and University of
// of Connecticut School of Medicine.
// All rights reserved.

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

#include "CValidity.h"
#include "copasi/core/CObjectInterface.h"

// static
const CIssue CIssue::Success(CIssue::eSeverity::Success);

// static
const CIssue CIssue::Information(CIssue::eSeverity::Information);

// static
const CIssue CIssue::Warning(CIssue::eSeverity::Warning);

// static
const CIssue CIssue::Error(CIssue::eSeverity::Error);

// static
const CEnumAnnotation< std::string, CIssue::eSeverity > CIssue::severityNames(
{
  "success",
  "information",
  "warnings",
  "errors"
});

// static
const CEnumAnnotation< std::string, CIssue::eKind > CIssue::kindNames(
{
  "unknown issue",
  "invalid expression",
  "empty expression",
  "missing initial value",
  "calculation problem",
  "missing event assignment",
  "event already has assignment",
  "missing event trigger expression",
  "undefined unit",
  "unit conflict",
  "invalid unit",
  "undefined or unrepresentable value",
  "unfound object",
  "unfound value",
  "unfound variable",
  "Invalid structure",
  "excess arguments",
  "circular dependency",
  "invalid expression data type",
  "variable in expression",
  "unfound expression",
  "unfound function",
  "mismatched variables",
  "inconsistent value types",
  "initial expression with assignment",
  "setting fixed expression",
  "reaction kinetics not defined"
});

// static
const CEnumAnnotation< std::string, CIssue::eKind > CIssue::kindDescriptions(
{
  "Unknown issue.",
  "Invalid expression.",
  "Empty expression.",
  "Missing initial value.",
  "Problem with calculation.",
  "Missing event assignment.",
  "Event already has an assignment rule.",
  "Missing event trigger expression.",
  "Unit is undefined.",
  "Conflicting units.",
  "Invalid unit.",
  "Value is undefined or unrepresentable.",
  "Object not found.",
  "Value not found.",
  "Variable not found.",
  "Invalid structure.",
  "Too many arguments.",
  "Has circular dependency.",
  "Invalid expression data type.",
  "Expression contains a variable.",
  "CExpression not found.",
  "CFunction not found.",
  "Variables are mismatched.",
  "Inconsistent value types encountered.",
  "Initial expressions prohibited with assignment.",
  "Changing fixed expression prohibited",
  "Reaction kinetics are not defined"
});

CIssue::CIssue(const CIssue::eSeverity & severity,
               const CIssue::eKind & kind):
  mSeverity(severity),
  mKind(kind)
{}

CIssue::CIssue(const CIssue & src):
  mSeverity(src.mSeverity),
  mKind(src.mKind)
{}

CIssue::~CIssue()
{}

CIssue::operator bool() const
{
  // If this type is implicity cast to a bool, in a conditional
  // evaluation, it will evaluate to "true" that there IS NOT a
  // significant "issue", if it is not at "Error" severity.
  return (mSeverity != CIssue::eSeverity::Error);
}

CIssue & CIssue::operator &= (const CIssue & rhs)
{
  if (rhs.mSeverity > mSeverity)
    {
      mSeverity = rhs.mSeverity;
      mKind = rhs.mKind;
    }

  return *this;
}

bool CIssue::operator == (const CIssue & rhs) const
{
  if (mSeverity == rhs.mSeverity && mKind == rhs.mKind)
    return true;
  else
    return false;
}

bool CIssue::isError() const
{
  return !isSuccess();
}

bool CIssue::isSuccess() const
{
  return (mSeverity != CIssue::eSeverity::Error);
}

CValidity & CValidity::operator |= (const CValidity & rhs)
{
  size_t Count = mErrors.count() + mWarnings.count() + mInformation.count();

  if (this != &rhs)
    {
      mErrors |= rhs.mErrors;
      mWarnings |= rhs.mWarnings;
      mInformation |= rhs.mInformation;
    }

  if (mpObjectInterface != NULL &&
      Count < mErrors.count() + mWarnings.count() + mInformation.count())
    {
      mpObjectInterface->validityChanged(*this);
    }

  return *this;
}

const CIssue & CValidity::getFirstWorstIssue() const
{
  return mFirstWorstIssue;
}

CValidity & CValidity::operator = (const CValidity & rhs)
{
  if (this == &rhs)
    return *this;

  bool Changed = false;

  if (mErrors != rhs.mErrors)
    {
      mErrors = rhs.mErrors;
      Changed = true;
    }

  if (mWarnings != rhs.mWarnings)
    {
      mWarnings = rhs.mWarnings;
      Changed = true;
    }

  if (mInformation != rhs.mInformation)
    {
      mInformation = rhs.mInformation;
      Changed = true;
    }

  if (mpObjectInterface != NULL && Changed)
    {
      mpObjectInterface->validityChanged(*this);
    }

  return *this;
}

const CIssue::eSeverity & CIssue::getSeverity() const
{
  return mSeverity;
}

const CIssue::eKind & CIssue::getKind() const
{
  return mKind;
}

CValidity::CValidity(CObjectInterface * pObjectInterface):
  mErrors(),
  mWarnings(),
  mInformation(),
  mpObjectInterface(pObjectInterface),
  mFirstWorstIssue()
{}

CValidity::CValidity(const CValidity & src, CObjectInterface * pObjectInterface):
  mErrors(src.mErrors),
  mWarnings(src.mWarnings),
  mInformation(src.mInformation),
  mpObjectInterface(pObjectInterface),
  mFirstWorstIssue(src.mFirstWorstIssue)
{}

CValidity::~CValidity()
{}

void CValidity::clear()
{
  // Only need to reset, if anything is set (i.e. not already clear)
  if (0 < mErrors.count() + mWarnings.count() + mInformation.count())
    {

      mFirstWorstIssue = CIssue::Success;

      mErrors.reset();
      mWarnings.reset();
      mInformation.reset();

      // Given something changed, notify the parent
      // to do it's thing, if it exists.
      if (mpObjectInterface != NULL)
        {
          mpObjectInterface->validityChanged(*this);
        }
    }
}

bool CValidity::empty() const
{
  return mErrors.count() + mWarnings.count() + mInformation.count() == 0;
}

void CValidity::add(const CIssue & issue)
{
  mFirstWorstIssue &= issue;

  size_t Count = 0;
  bool Changed = false;

  switch (issue.getSeverity())
    {
      case CIssue::eSeverity::Error:
        Count = mErrors.count();
        mErrors |= issue.getKind();
        Changed = Count < mErrors.count();
        break;

      case CIssue::eSeverity::Warning:
        Count = mWarnings.count();
        mWarnings |= issue.getKind();
        Changed = Count < mWarnings.count();
        break;

      case CIssue::eSeverity::Information:
        Count = mInformation.count();
        mInformation |= issue.getKind();
        Changed = Count < mInformation.count();
        break;

      case CIssue::eSeverity::__SIZE:
        break;
    }

  if (Changed &&
      mpObjectInterface != NULL)
    {
      mpObjectInterface->validityChanged(*this);
    }
}

void CValidity::remove(const CIssue & issue)
{
  if (mFirstWorstIssue == issue)
    mFirstWorstIssue = CIssue::Success;

  size_t Count = 0;
  bool Changed = false;

  switch (issue.getSeverity())
    {
      case CIssue::eSeverity::Error:
        Count = mErrors.count();
        mErrors &= ~Kind(issue.getKind());
        Changed = Count > mErrors.count();
        break;

      case CIssue::eSeverity::Warning:
        Count = mWarnings.count();
        mWarnings &= ~Kind(issue.getKind());
        Changed = Count > mWarnings.count();
        break;

      case CIssue::eSeverity::Information:
        Count = mInformation.count();
        mInformation &= ~Kind(issue.getKind());
        Changed = Count > mInformation.count();
        break;

      case CIssue::eSeverity::__SIZE:
        break;
    }

  if (Changed &&
      mpObjectInterface != NULL)
    {
      mpObjectInterface->validityChanged(*this);
    }
}

void CValidity::remove(const CValidity::Severity & severity,
                       const CValidity::Kind & kind)
{
  size_t Count = mErrors.count() + mWarnings.count() + mInformation.count();

  if (severity.isSet(CIssue::eSeverity::Error))
    mErrors &= ~kind;

  if (severity.isSet(CIssue::eSeverity::Warning))
    mWarnings &= ~kind;

  if (severity.isSet(CIssue::eSeverity::Information))
    mInformation &= ~kind;

  if (mpObjectInterface != NULL &&
      Count > mErrors.count() + mWarnings.count() + mInformation.count())
    {
      mpObjectInterface->validityChanged(*this);
    }
}

CIssue::eSeverity CValidity::getHighestSeverity(const CValidity::Severity & filterSeverity,
    const CValidity::Kind & filterKind) const
{
  if (filterSeverity.isSet(CIssue::eSeverity::Error) &&
      (mErrors & filterKind)) return CIssue::eSeverity::Error;

  if (filterSeverity.isSet(CIssue::eSeverity::Warning) &&
      (mWarnings & filterKind)) return CIssue::eSeverity::Warning;

  if (filterSeverity.isSet(CIssue::eSeverity::Information) &&
      (mInformation & filterKind)) return CIssue::eSeverity::Information;

  return CIssue::eSeverity::Success;
}

const CValidity::Kind & CValidity::get(const CIssue::eSeverity & severity) const
{
  static const Kind OK;

  switch (severity)
    {
      case CIssue::eSeverity::Error:
        return mErrors;

      case CIssue::eSeverity::Warning:
        return mWarnings;

      case CIssue::eSeverity::Information:
        return mInformation;

      default:
        return OK;
    }
}

const std::string CValidity::getIssueMessages(const Severity & severityFilter,
    const Kind & kindFilter) const
{
  std::string severityString = "";
  std::vector< std::string > descriptions;
  std::string messages = "";
  std::vector< std::string >::const_iterator it, end;

  if (severityFilter.isSet(CIssue::eSeverity::Error))
    {
      severityString = "Error: ";
      descriptions = mErrors.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  if (severityFilter.isSet(CIssue::eSeverity::Warning))
    {
      severityString = "Warning: ";
      descriptions = mWarnings.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  if (severityFilter.isSet(CIssue::eSeverity::Information))
    {
      severityString = "Information: ";
      descriptions = mInformation.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  // Remove last newline
  if (!messages.empty())
    messages = messages.substr(0, messages.size() - 1);

  return messages;
}
<commit_msg>Bug 2917: mFirstWorstIssue was not updated when combining validities.<commit_after>// Copyright (C) 2019 - 2020 by Pedro Mendes, Rector and Visitors of the
// University of Virginia, University of Heidelberg, and University
// of Connecticut School of Medicine.
// All rights reserved.

// Copyright (C) 2017 - 2018 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and University of
// of Connecticut School of Medicine.
// All rights reserved.

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

#include "CValidity.h"
#include "copasi/core/CObjectInterface.h"

// static
const CIssue CIssue::Success(CIssue::eSeverity::Success);

// static
const CIssue CIssue::Information(CIssue::eSeverity::Information);

// static
const CIssue CIssue::Warning(CIssue::eSeverity::Warning);

// static
const CIssue CIssue::Error(CIssue::eSeverity::Error);

// static
const CEnumAnnotation< std::string, CIssue::eSeverity > CIssue::severityNames(
{
  "success",
  "information",
  "warnings",
  "errors"
});

// static
const CEnumAnnotation< std::string, CIssue::eKind > CIssue::kindNames(
{
  "unknown issue",
  "invalid expression",
  "empty expression",
  "missing initial value",
  "calculation problem",
  "missing event assignment",
  "event already has assignment",
  "missing event trigger expression",
  "undefined unit",
  "unit conflict",
  "invalid unit",
  "undefined or unrepresentable value",
  "unfound object",
  "unfound value",
  "unfound variable",
  "Invalid structure",
  "excess arguments",
  "circular dependency",
  "invalid expression data type",
  "variable in expression",
  "unfound expression",
  "unfound function",
  "mismatched variables",
  "inconsistent value types",
  "initial expression with assignment",
  "setting fixed expression",
  "reaction kinetics not defined"
});

// static
const CEnumAnnotation< std::string, CIssue::eKind > CIssue::kindDescriptions(
{
  "Unknown issue.",
  "Invalid expression.",
  "Empty expression.",
  "Missing initial value.",
  "Problem with calculation.",
  "Missing event assignment.",
  "Event already has an assignment rule.",
  "Missing event trigger expression.",
  "Unit is undefined.",
  "Conflicting units.",
  "Invalid unit.",
  "Value is undefined or unrepresentable.",
  "Object not found.",
  "Value not found.",
  "Variable not found.",
  "Invalid structure.",
  "Too many arguments.",
  "Has circular dependency.",
  "Invalid expression data type.",
  "Expression contains a variable.",
  "CExpression not found.",
  "CFunction not found.",
  "Variables are mismatched.",
  "Inconsistent value types encountered.",
  "Initial expressions prohibited with assignment.",
  "Changing fixed expression prohibited",
  "Reaction kinetics are not defined"
});

CIssue::CIssue(const CIssue::eSeverity & severity,
               const CIssue::eKind & kind):
  mSeverity(severity),
  mKind(kind)
{}

CIssue::CIssue(const CIssue & src):
  mSeverity(src.mSeverity),
  mKind(src.mKind)
{}

CIssue::~CIssue()
{}

CIssue::operator bool() const
{
  // If this type is implicity cast to a bool, in a conditional
  // evaluation, it will evaluate to "true" that there IS NOT a
  // significant "issue", if it is not at "Error" severity.
  return (mSeverity != CIssue::eSeverity::Error);
}

CIssue & CIssue::operator &= (const CIssue & rhs)
{
  if (rhs.mSeverity > mSeverity)
    {
      mSeverity = rhs.mSeverity;
      mKind = rhs.mKind;
    }

  return *this;
}

bool CIssue::operator == (const CIssue & rhs) const
{
  if (mSeverity == rhs.mSeverity && mKind == rhs.mKind)
    return true;
  else
    return false;
}

bool CIssue::isError() const
{
  return !isSuccess();
}

bool CIssue::isSuccess() const
{
  return (mSeverity != CIssue::eSeverity::Error);
}

CValidity & CValidity::operator |= (const CValidity & rhs)
{
  size_t Count = mErrors.count() + mWarnings.count() + mInformation.count();

  if (this != &rhs)
    {
      mErrors |= rhs.mErrors;
      mWarnings |= rhs.mWarnings;
      mInformation |= rhs.mInformation;

      mFirstWorstIssue &= rhs.mFirstWorstIssue;
    }

  if (mpObjectInterface != NULL &&
      Count < mErrors.count() + mWarnings.count() + mInformation.count())
    {
      mpObjectInterface->validityChanged(*this);
    }

  return *this;
}

const CIssue & CValidity::getFirstWorstIssue() const
{
  return mFirstWorstIssue;
}

CValidity & CValidity::operator = (const CValidity & rhs)
{
  if (this == &rhs)
    return *this;

  bool Changed = false;

  if (mErrors != rhs.mErrors)
    {
      mErrors = rhs.mErrors;
      Changed = true;
    }

  if (mWarnings != rhs.mWarnings)
    {
      mWarnings = rhs.mWarnings;
      Changed = true;
    }

  if (mInformation != rhs.mInformation)
    {
      mInformation = rhs.mInformation;
      Changed = true;
    }

  if (mpObjectInterface != NULL && Changed)
    {
      mpObjectInterface->validityChanged(*this);
    }

  return *this;
}

const CIssue::eSeverity & CIssue::getSeverity() const
{
  return mSeverity;
}

const CIssue::eKind & CIssue::getKind() const
{
  return mKind;
}

CValidity::CValidity(CObjectInterface * pObjectInterface):
  mErrors(),
  mWarnings(),
  mInformation(),
  mpObjectInterface(pObjectInterface),
  mFirstWorstIssue()
{}

CValidity::CValidity(const CValidity & src, CObjectInterface * pObjectInterface):
  mErrors(src.mErrors),
  mWarnings(src.mWarnings),
  mInformation(src.mInformation),
  mpObjectInterface(pObjectInterface),
  mFirstWorstIssue(src.mFirstWorstIssue)
{}

CValidity::~CValidity()
{}

void CValidity::clear()
{
  // Only need to reset, if anything is set (i.e. not already clear)
  if (0 < mErrors.count() + mWarnings.count() + mInformation.count())
    {

      mFirstWorstIssue = CIssue::Success;

      mErrors.reset();
      mWarnings.reset();
      mInformation.reset();

      // Given something changed, notify the parent
      // to do it's thing, if it exists.
      if (mpObjectInterface != NULL)
        {
          mpObjectInterface->validityChanged(*this);
        }
    }
}

bool CValidity::empty() const
{
  return mErrors.count() + mWarnings.count() + mInformation.count() == 0;
}

void CValidity::add(const CIssue & issue)
{
  mFirstWorstIssue &= issue;

  size_t Count = 0;
  bool Changed = false;

  switch (issue.getSeverity())
    {
      case CIssue::eSeverity::Error:
        Count = mErrors.count();
        mErrors |= issue.getKind();
        Changed = Count < mErrors.count();
        break;

      case CIssue::eSeverity::Warning:
        Count = mWarnings.count();
        mWarnings |= issue.getKind();
        Changed = Count < mWarnings.count();
        break;

      case CIssue::eSeverity::Information:
        Count = mInformation.count();
        mInformation |= issue.getKind();
        Changed = Count < mInformation.count();
        break;

      case CIssue::eSeverity::__SIZE:
        break;
    }

  if (Changed &&
      mpObjectInterface != NULL)
    {
      mpObjectInterface->validityChanged(*this);
    }
}

void CValidity::remove(const CIssue & issue)
{
  if (mFirstWorstIssue == issue)
    mFirstWorstIssue = CIssue::Success;

  size_t Count = 0;
  bool Changed = false;

  switch (issue.getSeverity())
    {
      case CIssue::eSeverity::Error:
        Count = mErrors.count();
        mErrors &= ~Kind(issue.getKind());
        Changed = Count > mErrors.count();
        break;

      case CIssue::eSeverity::Warning:
        Count = mWarnings.count();
        mWarnings &= ~Kind(issue.getKind());
        Changed = Count > mWarnings.count();
        break;

      case CIssue::eSeverity::Information:
        Count = mInformation.count();
        mInformation &= ~Kind(issue.getKind());
        Changed = Count > mInformation.count();
        break;

      case CIssue::eSeverity::__SIZE:
        break;
    }

  if (Changed &&
      mpObjectInterface != NULL)
    {
      mpObjectInterface->validityChanged(*this);
    }
}

void CValidity::remove(const CValidity::Severity & severity,
                       const CValidity::Kind & kind)
{
  size_t Count = mErrors.count() + mWarnings.count() + mInformation.count();

  if (severity.isSet(CIssue::eSeverity::Error))
    mErrors &= ~kind;

  if (severity.isSet(CIssue::eSeverity::Warning))
    mWarnings &= ~kind;

  if (severity.isSet(CIssue::eSeverity::Information))
    mInformation &= ~kind;

  if (mpObjectInterface != NULL &&
      Count > mErrors.count() + mWarnings.count() + mInformation.count())
    {
      mpObjectInterface->validityChanged(*this);
    }
}

CIssue::eSeverity CValidity::getHighestSeverity(const CValidity::Severity & filterSeverity,
    const CValidity::Kind & filterKind) const
{
  if (filterSeverity.isSet(CIssue::eSeverity::Error) &&
      (mErrors & filterKind)) return CIssue::eSeverity::Error;

  if (filterSeverity.isSet(CIssue::eSeverity::Warning) &&
      (mWarnings & filterKind)) return CIssue::eSeverity::Warning;

  if (filterSeverity.isSet(CIssue::eSeverity::Information) &&
      (mInformation & filterKind)) return CIssue::eSeverity::Information;

  return CIssue::eSeverity::Success;
}

const CValidity::Kind & CValidity::get(const CIssue::eSeverity & severity) const
{
  static const Kind OK;

  switch (severity)
    {
      case CIssue::eSeverity::Error:
        return mErrors;

      case CIssue::eSeverity::Warning:
        return mWarnings;

      case CIssue::eSeverity::Information:
        return mInformation;

      default:
        return OK;
    }
}

const std::string CValidity::getIssueMessages(const Severity & severityFilter,
    const Kind & kindFilter) const
{
  std::string severityString = "";
  std::vector< std::string > descriptions;
  std::string messages = "";
  std::vector< std::string >::const_iterator it, end;

  if (severityFilter.isSet(CIssue::eSeverity::Error))
    {
      severityString = "Error: ";
      descriptions = mErrors.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  if (severityFilter.isSet(CIssue::eSeverity::Warning))
    {
      severityString = "Warning: ";
      descriptions = mWarnings.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  if (severityFilter.isSet(CIssue::eSeverity::Information))
    {
      severityString = "Information: ";
      descriptions = mInformation.getAnnotations(CIssue::kindDescriptions, kindFilter);

      it = descriptions.begin();
      end = descriptions.end();

      for (; it != end; ++it)
        {
          messages += severityString + *it + "\n";
        }
    }

  // Remove last newline
  if (!messages.empty())
    messages = messages.substr(0, messages.size() - 1);

  return messages;
}
<|endoftext|>


<commit_before>// @(#)root/cont:$Id$
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class TProcessID
\ingroup Base

A TProcessID identifies a ROOT job in a unique way in time and space.
The TProcessID title consists of a TUUID object which provides a globally
unique identifier (for more see TUUID.h).

A TProcessID is automatically created by the TROOT constructor.
When a TFile contains referenced objects (see TRef), the TProcessID
object is written to the file.
If a file has been written in multiple sessions (same machine or not),
a TProcessID is written for each session.
These objects are used by the class TRef to uniquely identified
any TObject pointed by a TRef.

When a referenced object is read from a file (its bit kIsReferenced is set),
this object is entered into the objects table of the corresponding TProcessID.
Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
accessible via TProcessID::fgPIDs (for all files).
When this object is deleted, it is removed from the table via the cleanup
mechanism invoked by the TObject destructor.

Each TProcessID has a table (TObjArray *fObjects) that keeps track
of all referenced objects. If a referenced object has a fUniqueID set,
a pointer to this unique object may be found via fObjects->At(fUniqueID).
In the same way, when a TRef::GetObject is called, GetObject uses
its own fUniqueID to find the pointer to the referenced object.
See TProcessID::GetObjectWithID and PutObjectWithID.

When a referenced object is deleted, its slot in fObjects is set to null.
//
See also TProcessUUID: a specialized TProcessID to manage the single list
of TUUIDs.
*/

#include "TProcessID.h"
#include "TROOT.h"
#include "TObjArray.h"
#include "TExMap.h"
#include "TVirtualMutex.h"
#include "TError.h"

TObjArray  *TProcessID::fgPIDs   = 0; //pointer to the list of TProcessID
TProcessID *TProcessID::fgPID    = 0; //pointer to the TProcessID of the current session
std::atomic_uint TProcessID::fgNumber(0); //Current referenced object instance count
TExMap     *TProcessID::fgObjPIDs= 0; //Table (pointer,pids)
ClassImp(TProcessID);

static std::atomic<TProcessID *> gIsValidCache;
using PIDCacheContent_t = std::pair<Int_t, TProcessID*>;
static std::atomic<PIDCacheContent_t *> gGetProcessWithUIDCache;

////////////////////////////////////////////////////////////////////////////////
/// Return hash value for this object.

static inline ULong_t Void_Hash(const void *ptr)
{
   return TString::Hash(&ptr, sizeof(void*));
}

////////////////////////////////////////////////////////////////////////////////
/// Default constructor.

TProcessID::TProcessID()
{
   // MSVC doesn't support fSpinLock=ATOMIC_FLAG_INIT; in the class definition
   // and Apple LLVM version 7.3.0 (clang-703.0.31) warns about:
   // fLock(ATOMIC_FLAG_INIT)
   // ^~~~~~~~~~~~~~~~
   //    c++/v1/atomic:1779:26: note: expanded from macro 'ATOMIC_FLAG_INIT'
   //  #define ATOMIC_FLAG_INIT {false}
   // So reset the flag instead.
   std::atomic_flag_clear( &fLock );

   fCount = 0;
   fObjects = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Destructor.

TProcessID::~TProcessID()
{
   delete fObjects;
   fObjects = 0;

   TProcessID *This = this; // We need a referencable value for the 1st argument
   gIsValidCache.compare_exchange_strong(This, nullptr);

   auto current = gGetProcessWithUIDCache.load();
   if (current && current->second == this) {
      gGetProcessWithUIDCache.compare_exchange_strong(current, nullptr);
      delete current;
   }

   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
   fgPIDs->Remove(this);
}

////////////////////////////////////////////////////////////////////////////////
/// Static function to add a new TProcessID to the list of PIDs.

TProcessID *TProcessID::AddProcessID()
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   if (fgPIDs && fgPIDs->GetEntriesFast() >= 65534) {
      if (fgPIDs->GetEntriesFast() == 65534) {
         ::Warning("TProcessID::AddProcessID","Maximum number of TProcessID (65535) is almost reached (one left).  TRef will stop being functional when the limit is reached.");
      } else {
         ::Fatal("TProcessID::AddProcessID","Maximum number of TProcessID (65535) has been reached.  TRef are not longer functional.");
      }
   }

   TProcessID *pid = new TProcessID();

   if (!fgPIDs) {
      fgPID  = pid;
      fgPIDs = new TObjArray(10);
      gROOT->GetListOfCleanups()->Add(fgPIDs);
   }
   UShort_t apid = fgPIDs->GetEntriesFast();
   pid->IncrementCount();

   fgPIDs->Add(pid);
   // if (apid == 0) for(int incr=0; incr < 65533; ++incr) fgPIDs->Add(0); // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   char name[20];
   snprintf(name,20,"ProcessID%d",apid);
   pid->SetName(name);
   pid->SetUniqueID((UInt_t)apid);
   TUUID u;
   //apid = fgPIDs->GetEntriesFast();
   pid->SetTitle(u.AsString());
   return pid;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning the ID assigned to obj
/// If the object is not yet referenced, its kIsReferenced bit is set
/// and its fUniqueID set to the current number of referenced objects so far.

UInt_t TProcessID::AssignID(TObject *obj)
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == fgPID->GetObjectWithID(uid)) return uid;
   if (obj->TestBit(kIsReferenced)) {
      fgPID->PutObjectWithID(obj,uid);
      return uid;
   }
   if (fgNumber >= 16777215) {
      // This process id is 'full', we need to use a new one.
      fgPID = AddProcessID();
      fgNumber = 0;
      for(Int_t i = 0; i < fgPIDs->GetLast()+1; ++i) {
         TProcessID *pid = (TProcessID*)fgPIDs->At(i);
         if (pid && pid->fObjects && pid->fObjects->GetEntries() == 0) {
            pid->Clear();
         }
      }
   }
   fgNumber++;
   obj->SetBit(kIsReferenced);
   uid = fgNumber;
   // if (fgNumber<10) fgNumber = 16777213; // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   if ( fgPID->GetUniqueID() < 255 ) {
      obj->SetUniqueID( (uid & 0xffffff) + (fgPID->GetUniqueID()<<24) );
   } else {
      obj->SetUniqueID( (uid & 0xffffff) + 0xff000000 /* 255 << 24 */ );
   }
   fgPID->PutObjectWithID(obj,uid);
   return uid;
}

////////////////////////////////////////////////////////////////////////////////
/// Initialize fObjects.

void TProcessID::CheckInit()
{
   if (!fObjects) {
       while (fLock.test_and_set(std::memory_order_acquire));  // acquire lock
       if (!fObjects) fObjects = new TObjArray(100);
       fLock.clear(std::memory_order_release);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function (called by TROOT destructor) to delete all TProcessIDs

void TProcessID::Cleanup()
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   fgPIDs->Delete();
   gROOT->GetListOfCleanups()->Remove(fgPIDs);
   delete fgPIDs;
   fgPIDs = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// delete the TObjArray pointing to referenced objects
/// this function is called by TFile::Close("R")

void TProcessID::Clear(Option_t *)
{
   if (GetUniqueID()>254 && fObjects && fgObjPIDs) {
      // We might have many references registered in the map
      for(Int_t i = 0; i < fObjects->GetSize(); ++i) {
         TObject *obj = fObjects->UncheckedAt(i);
         if (obj) {
            ULong64_t hash = Void_Hash(obj);
            fgObjPIDs->Remove(hash,(Long64_t)obj);
            (*fObjects)[i] = 0;
         }
      }
   }
   delete fObjects; fObjects = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// The reference fCount is used to delete the TProcessID
/// in the TFile destructor when fCount = 0

Int_t TProcessID::DecrementCount()
{
   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID number pid in fgPIDs

TProcessID *TProcessID::GetProcessID(UShort_t pid)
{
   return (TProcessID*)fgPIDs->At(pid);
}

////////////////////////////////////////////////////////////////////////////////
/// Return the (static) number of process IDs.

UInt_t TProcessID::GetNProcessIDs()
{
   return fgPIDs ? fgPIDs->GetLast()+1 : 0;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID with its pid
/// encoded in the highest byte of uid

TProcessID *TProcessID::GetProcessWithUID(UInt_t uid, const void *obj)
{

   Int_t pid = (uid>>24)&0xff;
   if (pid==0xff) {
      // Look up the pid in the table (pointer,pid)
      if (fgObjPIDs==0) return 0;
      ULong_t hash = Void_Hash(obj);

      R__READ_LOCKGUARD(ROOT::gCoreMutex);
      pid = fgObjPIDs->GetValue(hash,(Long_t)obj);
      return (TProcessID*)fgPIDs->At(pid);
   } else {
      auto current = gGetProcessWithUIDCache.load();
      if (current && current->first == pid)
         return current->second;

      R__READ_LOCKGUARD(ROOT::gCoreMutex);
      auto res = (TProcessID*)fgPIDs->At(pid);

      auto next = new PIDCacheContent_t(pid, res);
      auto old = gGetProcessWithUIDCache.exchange(next);
      delete old;

      return res;
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID with its pid
/// encoded in the highest byte of obj->GetUniqueID()

TProcessID *TProcessID::GetProcessWithUID(const TObject *obj)
{
   return GetProcessWithUID(obj->GetUniqueID(),obj);
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning the pointer to the session TProcessID

TProcessID *TProcessID::GetSessionProcessID()
{
   return fgPID;
}

////////////////////////////////////////////////////////////////////////////////
/// Increase the reference count to this object.

Int_t TProcessID::IncrementCount()
{
   CheckInit();
   ++fCount;
   return fCount;
}

////////////////////////////////////////////////////////////////////////////////
/// Return the current referenced object count
/// fgNumber is incremented every time a new object is referenced

UInt_t TProcessID::GetObjectCount()
{
   return fgNumber;
}

////////////////////////////////////////////////////////////////////////////////
/// returns the TObject with unique identifier uid in the table of objects

TObject *TProcessID::GetObjectWithID(UInt_t uidd)
{
   Int_t uid = uidd & 0xffffff;  //take only the 24 lower bits

   if (fObjects==0 || uid >= fObjects->GetSize()) return 0;
   return fObjects->UncheckedAt(uid);
}

////////////////////////////////////////////////////////////////////////////////
/// static: returns pointer to current TProcessID

TProcessID *TProcessID::GetPID()
{
   return fgPID;
}

////////////////////////////////////////////////////////////////////////////////
/// static: returns array of TProcessIDs

TObjArray *TProcessID::GetPIDs()
{
   return fgPIDs;
}

////////////////////////////////////////////////////////////////////////////////
/// static function. return kTRUE if pid is a valid TProcessID

Bool_t TProcessID::IsValid(TProcessID *pid)
{
   if (gIsValidCache == pid)
      return kTRUE;

   R__READ_LOCKGUARD(ROOT::gCoreMutex);

   if (fgPIDs==0) return kFALSE;
   if (fgPIDs->IndexOf(pid) >= 0) {
      gIsValidCache = pid;
      return kTRUE;
   }
    if (pid == (TProcessID*)gROOT->GetUUIDs()) {
      gIsValidCache = pid;
      return kTRUE;
   }
   return kFALSE;
}

////////////////////////////////////////////////////////////////////////////////
/// stores the object at the uid th slot in the table of objects
/// The object uniqued is set as well as its kMustCleanup bit

void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
{
   R__LOCKGUARD_IMT(gROOTMutex); // Lock for parallel TTree I/O

   if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;

   if (!fObjects) fObjects = new TObjArray(100);
   fObjects->AddAtAndExpand(obj,uid);

   obj->SetBit(kMustCleanup);
   if ( (obj->GetUniqueID()&0xff000000)==0xff000000 ) {
      // We have more than 255 pids we need to store this
      // pointer in the table(pointer,pid) since there is no
      // more space in fUniqueID
      if (fgObjPIDs==0) fgObjPIDs = new TExMap;
      ULong_t hash = Void_Hash(obj);

      // We use operator() rather than Add() because
      // if the address has already been registered, we want to
      // update it's uniqueID (this can easily happen when the
      // referenced object have been stored in a TClonesArray.
      (*fgObjPIDs)(hash, (Long_t)obj) = GetUniqueID();
   }
}

////////////////////////////////////////////////////////////////////////////////
/// called by the object destructor
/// remove reference to obj from the current table if it is referenced

void TProcessID::RecursiveRemove(TObject *obj)
{
   if (!fObjects) return;
   if (!obj->TestBit(kIsReferenced)) return;
   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == GetObjectWithID(uid)) {
      R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
      if (fgObjPIDs) {
         ULong64_t hash = Void_Hash(obj);
         fgObjPIDs->Remove(hash,(Long64_t)obj);
      }
      (*fObjects)[uid] = 0; // Avoid recalculation of fLast (compared to ->RemoveAt(uid))
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function to set the current referenced object count
/// fgNumber is incremented every time a new object is referenced

void TProcessID::SetObjectCount(UInt_t number)
{
   fgNumber = number;
}
<commit_msg>TProcessID::RecursiveRemove: prevent spurrious error msg.<commit_after>// @(#)root/cont:$Id$
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class TProcessID
\ingroup Base

A TProcessID identifies a ROOT job in a unique way in time and space.
The TProcessID title consists of a TUUID object which provides a globally
unique identifier (for more see TUUID.h).

A TProcessID is automatically created by the TROOT constructor.
When a TFile contains referenced objects (see TRef), the TProcessID
object is written to the file.
If a file has been written in multiple sessions (same machine or not),
a TProcessID is written for each session.
These objects are used by the class TRef to uniquely identified
any TObject pointed by a TRef.

When a referenced object is read from a file (its bit kIsReferenced is set),
this object is entered into the objects table of the corresponding TProcessID.
Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
accessible via TProcessID::fgPIDs (for all files).
When this object is deleted, it is removed from the table via the cleanup
mechanism invoked by the TObject destructor.

Each TProcessID has a table (TObjArray *fObjects) that keeps track
of all referenced objects. If a referenced object has a fUniqueID set,
a pointer to this unique object may be found via fObjects->At(fUniqueID).
In the same way, when a TRef::GetObject is called, GetObject uses
its own fUniqueID to find the pointer to the referenced object.
See TProcessID::GetObjectWithID and PutObjectWithID.

When a referenced object is deleted, its slot in fObjects is set to null.
//
See also TProcessUUID: a specialized TProcessID to manage the single list
of TUUIDs.
*/

#include "TProcessID.h"
#include "TROOT.h"
#include "TObjArray.h"
#include "TExMap.h"
#include "TVirtualMutex.h"
#include "TError.h"

TObjArray  *TProcessID::fgPIDs   = 0; //pointer to the list of TProcessID
TProcessID *TProcessID::fgPID    = 0; //pointer to the TProcessID of the current session
std::atomic_uint TProcessID::fgNumber(0); //Current referenced object instance count
TExMap     *TProcessID::fgObjPIDs= 0; //Table (pointer,pids)
ClassImp(TProcessID);

static std::atomic<TProcessID *> gIsValidCache;
using PIDCacheContent_t = std::pair<Int_t, TProcessID*>;
static std::atomic<PIDCacheContent_t *> gGetProcessWithUIDCache;

////////////////////////////////////////////////////////////////////////////////
/// Return hash value for this object.

static inline ULong_t Void_Hash(const void *ptr)
{
   return TString::Hash(&ptr, sizeof(void*));
}

////////////////////////////////////////////////////////////////////////////////
/// Default constructor.

TProcessID::TProcessID()
{
   // MSVC doesn't support fSpinLock=ATOMIC_FLAG_INIT; in the class definition
   // and Apple LLVM version 7.3.0 (clang-703.0.31) warns about:
   // fLock(ATOMIC_FLAG_INIT)
   // ^~~~~~~~~~~~~~~~
   //    c++/v1/atomic:1779:26: note: expanded from macro 'ATOMIC_FLAG_INIT'
   //  #define ATOMIC_FLAG_INIT {false}
   // So reset the flag instead.
   std::atomic_flag_clear( &fLock );

   fCount = 0;
   fObjects = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Destructor.

TProcessID::~TProcessID()
{
   delete fObjects;
   fObjects = 0;

   TProcessID *This = this; // We need a referencable value for the 1st argument
   gIsValidCache.compare_exchange_strong(This, nullptr);

   auto current = gGetProcessWithUIDCache.load();
   if (current && current->second == this) {
      gGetProcessWithUIDCache.compare_exchange_strong(current, nullptr);
      delete current;
   }

   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
   fgPIDs->Remove(this);
}

////////////////////////////////////////////////////////////////////////////////
/// Static function to add a new TProcessID to the list of PIDs.

TProcessID *TProcessID::AddProcessID()
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   if (fgPIDs && fgPIDs->GetEntriesFast() >= 65534) {
      if (fgPIDs->GetEntriesFast() == 65534) {
         ::Warning("TProcessID::AddProcessID","Maximum number of TProcessID (65535) is almost reached (one left).  TRef will stop being functional when the limit is reached.");
      } else {
         ::Fatal("TProcessID::AddProcessID","Maximum number of TProcessID (65535) has been reached.  TRef are not longer functional.");
      }
   }

   TProcessID *pid = new TProcessID();

   if (!fgPIDs) {
      fgPID  = pid;
      fgPIDs = new TObjArray(10);
      gROOT->GetListOfCleanups()->Add(fgPIDs);
   }
   UShort_t apid = fgPIDs->GetEntriesFast();
   pid->IncrementCount();

   fgPIDs->Add(pid);
   // if (apid == 0) for(int incr=0; incr < 65533; ++incr) fgPIDs->Add(0); // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   char name[20];
   snprintf(name,20,"ProcessID%d",apid);
   pid->SetName(name);
   pid->SetUniqueID((UInt_t)apid);
   TUUID u;
   //apid = fgPIDs->GetEntriesFast();
   pid->SetTitle(u.AsString());
   return pid;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning the ID assigned to obj
/// If the object is not yet referenced, its kIsReferenced bit is set
/// and its fUniqueID set to the current number of referenced objects so far.

UInt_t TProcessID::AssignID(TObject *obj)
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == fgPID->GetObjectWithID(uid)) return uid;
   if (obj->TestBit(kIsReferenced)) {
      fgPID->PutObjectWithID(obj,uid);
      return uid;
   }
   if (fgNumber >= 16777215) {
      // This process id is 'full', we need to use a new one.
      fgPID = AddProcessID();
      fgNumber = 0;
      for(Int_t i = 0; i < fgPIDs->GetLast()+1; ++i) {
         TProcessID *pid = (TProcessID*)fgPIDs->At(i);
         if (pid && pid->fObjects && pid->fObjects->GetEntries() == 0) {
            pid->Clear();
         }
      }
   }
   fgNumber++;
   obj->SetBit(kIsReferenced);
   uid = fgNumber;
   // if (fgNumber<10) fgNumber = 16777213; // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   if ( fgPID->GetUniqueID() < 255 ) {
      obj->SetUniqueID( (uid & 0xffffff) + (fgPID->GetUniqueID()<<24) );
   } else {
      obj->SetUniqueID( (uid & 0xffffff) + 0xff000000 /* 255 << 24 */ );
   }
   fgPID->PutObjectWithID(obj,uid);
   return uid;
}

////////////////////////////////////////////////////////////////////////////////
/// Initialize fObjects.

void TProcessID::CheckInit()
{
   if (!fObjects) {
       while (fLock.test_and_set(std::memory_order_acquire));  // acquire lock
       if (!fObjects) fObjects = new TObjArray(100);
       fLock.clear(std::memory_order_release);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function (called by TROOT destructor) to delete all TProcessIDs

void TProcessID::Cleanup()
{
   R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

   fgPIDs->Delete();
   gROOT->GetListOfCleanups()->Remove(fgPIDs);
   delete fgPIDs;
   fgPIDs = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// delete the TObjArray pointing to referenced objects
/// this function is called by TFile::Close("R")

void TProcessID::Clear(Option_t *)
{
   if (GetUniqueID()>254 && fObjects && fgObjPIDs) {
      // We might have many references registered in the map
      for(Int_t i = 0; i < fObjects->GetSize(); ++i) {
         TObject *obj = fObjects->UncheckedAt(i);
         if (obj) {
            ULong64_t hash = Void_Hash(obj);
            fgObjPIDs->Remove(hash,(Long64_t)obj);
            (*fObjects)[i] = 0;
         }
      }
   }
   delete fObjects; fObjects = 0;
}

////////////////////////////////////////////////////////////////////////////////
/// The reference fCount is used to delete the TProcessID
/// in the TFile destructor when fCount = 0

Int_t TProcessID::DecrementCount()
{
   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID number pid in fgPIDs

TProcessID *TProcessID::GetProcessID(UShort_t pid)
{
   return (TProcessID*)fgPIDs->At(pid);
}

////////////////////////////////////////////////////////////////////////////////
/// Return the (static) number of process IDs.

UInt_t TProcessID::GetNProcessIDs()
{
   return fgPIDs ? fgPIDs->GetLast()+1 : 0;
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID with its pid
/// encoded in the highest byte of uid

TProcessID *TProcessID::GetProcessWithUID(UInt_t uid, const void *obj)
{

   Int_t pid = (uid>>24)&0xff;
   if (pid==0xff) {
      // Look up the pid in the table (pointer,pid)
      if (fgObjPIDs==0) return 0;
      ULong_t hash = Void_Hash(obj);

      R__READ_LOCKGUARD(ROOT::gCoreMutex);
      pid = fgObjPIDs->GetValue(hash,(Long_t)obj);
      return (TProcessID*)fgPIDs->At(pid);
   } else {
      auto current = gGetProcessWithUIDCache.load();
      if (current && current->first == pid)
         return current->second;

      R__READ_LOCKGUARD(ROOT::gCoreMutex);
      auto res = (TProcessID*)fgPIDs->At(pid);

      auto next = new PIDCacheContent_t(pid, res);
      auto old = gGetProcessWithUIDCache.exchange(next);
      delete old;

      return res;
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning a pointer to TProcessID with its pid
/// encoded in the highest byte of obj->GetUniqueID()

TProcessID *TProcessID::GetProcessWithUID(const TObject *obj)
{
   return GetProcessWithUID(obj->GetUniqueID(),obj);
}

////////////////////////////////////////////////////////////////////////////////
/// static function returning the pointer to the session TProcessID

TProcessID *TProcessID::GetSessionProcessID()
{
   return fgPID;
}

////////////////////////////////////////////////////////////////////////////////
/// Increase the reference count to this object.

Int_t TProcessID::IncrementCount()
{
   CheckInit();
   ++fCount;
   return fCount;
}

////////////////////////////////////////////////////////////////////////////////
/// Return the current referenced object count
/// fgNumber is incremented every time a new object is referenced

UInt_t TProcessID::GetObjectCount()
{
   return fgNumber;
}

////////////////////////////////////////////////////////////////////////////////
/// returns the TObject with unique identifier uid in the table of objects

TObject *TProcessID::GetObjectWithID(UInt_t uidd)
{
   Int_t uid = uidd & 0xffffff;  //take only the 24 lower bits

   if (fObjects==0 || uid >= fObjects->GetSize()) return 0;
   return fObjects->UncheckedAt(uid);
}

////////////////////////////////////////////////////////////////////////////////
/// static: returns pointer to current TProcessID

TProcessID *TProcessID::GetPID()
{
   return fgPID;
}

////////////////////////////////////////////////////////////////////////////////
/// static: returns array of TProcessIDs

TObjArray *TProcessID::GetPIDs()
{
   return fgPIDs;
}

////////////////////////////////////////////////////////////////////////////////
/// static function. return kTRUE if pid is a valid TProcessID

Bool_t TProcessID::IsValid(TProcessID *pid)
{
   if (gIsValidCache == pid)
      return kTRUE;

   R__READ_LOCKGUARD(ROOT::gCoreMutex);

   if (fgPIDs==0) return kFALSE;
   if (fgPIDs->IndexOf(pid) >= 0) {
      gIsValidCache = pid;
      return kTRUE;
   }
    if (pid == (TProcessID*)gROOT->GetUUIDs()) {
      gIsValidCache = pid;
      return kTRUE;
   }
   return kFALSE;
}

////////////////////////////////////////////////////////////////////////////////
/// stores the object at the uid th slot in the table of objects
/// The object uniqued is set as well as its kMustCleanup bit

void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
{
   R__LOCKGUARD_IMT(gROOTMutex); // Lock for parallel TTree I/O

   if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;

   if (!fObjects) fObjects = new TObjArray(100);
   fObjects->AddAtAndExpand(obj,uid);

   obj->SetBit(kMustCleanup);
   if ( (obj->GetUniqueID()&0xff000000)==0xff000000 ) {
      // We have more than 255 pids we need to store this
      // pointer in the table(pointer,pid) since there is no
      // more space in fUniqueID
      if (fgObjPIDs==0) fgObjPIDs = new TExMap;
      ULong_t hash = Void_Hash(obj);

      // We use operator() rather than Add() because
      // if the address has already been registered, we want to
      // update it's uniqueID (this can easily happen when the
      // referenced object have been stored in a TClonesArray.
      (*fgObjPIDs)(hash, (Long_t)obj) = GetUniqueID();
   }
}

////////////////////////////////////////////////////////////////////////////////
/// called by the object destructor
/// remove reference to obj from the current table if it is referenced

void TProcessID::RecursiveRemove(TObject *obj)
{
   if (!fObjects) return;
   if (!obj->TestBit(kIsReferenced)) return;
   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == GetObjectWithID(uid)) {
      R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
      if (fgObjPIDs && ((obj->GetUniqueID()&0xff000000)==0xff000000)) {
         ULong64_t hash = Void_Hash(obj);
         fgObjPIDs->Remove(hash,(Long64_t)obj);
      }
      (*fObjects)[uid] = 0; // Avoid recalculation of fLast (compared to ->RemoveAt(uid))
   }
}

////////////////////////////////////////////////////////////////////////////////
/// static function to set the current referenced object count
/// fgNumber is incremented every time a new object is referenced

void TProcessID::SetObjectCount(UInt_t number)
{
   fgNumber = number;
}
<|endoftext|>