root/src/add-ons/translators/shared/TranslatorSettings.cpp
/*****************************************************************************/
// TranslatorSettings
// Written by Michael Wilber, Haiku Translation Kit Team
//
// TranslatorSettings.cpp
//
// This class manages (saves/loads/locks/unlocks) the settings
// for a Translator.
//
//
// Copyright (c) 2004 Haiku Project
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/

#include <string.h>
#include <File.h>
#include <FindDirectory.h>
#include <TranslatorFormats.h>
        // for B_TRANSLATOR_EXT_*
#include "TranslatorSettings.h"

// ---------------------------------------------------------------
// Constructor
//
// Sets the default settings, location for the settings file
// and sets the reference count to 1
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
TranslatorSettings::TranslatorSettings(const char *settingsFile,
        const TranSetting *defaults, int32 defCount)
        : fLock("TranslatorSettings Lock")
{
        if (find_directory(B_USER_SETTINGS_DIRECTORY, &fSettingsPath))
                fSettingsPath.SetTo("/tmp");
        fSettingsPath.Append(settingsFile);

        fRefCount = 1;

        if (defCount > 0) {
                fDefaults = defaults;
                fDefCount = defCount;
        } else {
                fDefaults = NULL;
                fDefCount = 0;
        }

        // Add Default Settings
        // (Used when loading from the settings file or from
        // a BMessage fails)
        const TranSetting *defs = fDefaults;
        for (int32 i = 0; i < fDefCount; i++) {
                switch (defs[i].dataType) {
                        case TRAN_SETTING_BOOL:
                                fSettingsMsg.AddBool(defs[i].name,
                                        static_cast<bool>(defs[i].defaultVal));
                                break;

                        case TRAN_SETTING_INT32:
                                fSettingsMsg.AddInt32(defs[i].name, defs[i].defaultVal);
                                break;

                        default:
                                // ASSERT here? Erase the bogus setting entry instead?
                                break;
                }
        }
}

// ---------------------------------------------------------------
// Acquire
//
// Returns a pointer to the TranslatorSettings and increments
// the reference count.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: pointer to this TranslatorSettings object
// ---------------------------------------------------------------
TranslatorSettings *
TranslatorSettings::Acquire()
{
        TranslatorSettings *psettings = NULL;

        fLock.Lock();
        fRefCount++;
        psettings = this;
        fLock.Unlock();

        return psettings;
}

// ---------------------------------------------------------------
// Release
//
// Decrements the reference count and deletes the
// TranslatorSettings if the reference count is zero.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: pointer to this TranslatorSettings object if
// the reference count is greater than zero, returns NULL
// if the reference count is zero and the TranslatorSettings
// object has been deleted
// ---------------------------------------------------------------
TranslatorSettings *
TranslatorSettings::Release()
{
        TranslatorSettings *psettings = NULL;

        fLock.Lock();
        fRefCount--;
        if (fRefCount > 0) {
                psettings = this;
                fLock.Unlock();
        } else
                delete this;
                        // delete this object and
                        // release locks

        return psettings;
}

// ---------------------------------------------------------------
// Destructor
//
// Does nothing!
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
TranslatorSettings::~TranslatorSettings()
{
}

// ---------------------------------------------------------------
// LoadSettings
//
// Loads the settings by reading them from the default
// settings file.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: B_OK if there were no errors or an error code from
// BFile::SetTo() or BMessage::Unflatten() if there were errors
// ---------------------------------------------------------------
status_t
TranslatorSettings::LoadSettings()
{
        status_t result;

        fLock.Lock();

        // Don't try to open the settings file if there are
        // no settings that need to be loaded
        if (fDefCount > 0) {
                BFile settingsFile;
                result = settingsFile.SetTo(fSettingsPath.Path(), B_READ_ONLY);
                if (result == B_OK) {
                        BMessage msg;
                        result = msg.Unflatten(&settingsFile);
                        if (result == B_OK)
                                result = LoadSettings(&msg);
                }
        } else
                result = B_OK;

        fLock.Unlock();

        return result;
}

// ---------------------------------------------------------------
// LoadSettings
//
// Loads the settings from a BMessage passed to the function.
//
// Preconditions:
//
// Parameters:  pmsg    pointer to BMessage that contains the
//                                              settings
//
// Postconditions:
//
// Returns: B_BAD_VALUE if pmsg is NULL or invalid options
// have been found, B_OK if there were no
// errors or an error code from BMessage::FindBool() or
// BMessage::ReplaceBool() if there were other errors
// ---------------------------------------------------------------
status_t
TranslatorSettings::LoadSettings(BMessage *pmsg)
{
        if (pmsg == NULL)
                return B_BAD_VALUE;

        fLock.Lock();
        const TranSetting *defaults = fDefaults;
        for (int32 i = 0; i < fDefCount; i++) {
                switch (defaults[i].dataType) {
                        case TRAN_SETTING_BOOL:
                        {
                                bool value;
                                if (pmsg->FindBool(defaults[i].name, &value) != B_OK) {
                                        if (fSettingsMsg.HasBool(defaults[i].name))
                                                break;
                                        else
                                                value = static_cast<bool>(defaults[i].defaultVal);
                                }

                                fSettingsMsg.ReplaceBool(defaults[i].name, value);
                                break;
                        }

                        case TRAN_SETTING_INT32:
                        {
                                int32 value;
                                if (pmsg->FindInt32(defaults[i].name, &value) != B_OK) {
                                        if (fSettingsMsg.HasInt32(defaults[i].name))
                                                break;
                                        else
                                                value = defaults[i].defaultVal;
                                }

                                fSettingsMsg.ReplaceInt32(defaults[i].name, value);
                                break;
                        }

                        default:
                                // TODO: ASSERT here? Erase the bogus setting entry instead?
                                break;
                }
        }

        fLock.Unlock();
        return B_OK;
}

// ---------------------------------------------------------------
// SaveSettings
//
// Saves the settings as a flattened BMessage to the default
// settings file
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: B_OK if no errors or an error code from BFile::SetTo()
// or BMessage::Flatten() if there were errors
// ---------------------------------------------------------------
status_t
TranslatorSettings::SaveSettings()
{
        status_t result;

        fLock.Lock();

        // Only write out settings file if there are
        // actual settings stored by this object
        if (fDefCount > 0) {
                BFile settingsFile;
                result = settingsFile.SetTo(fSettingsPath.Path(),
                        B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
                if (result == B_OK)
                        result = fSettingsMsg.Flatten(&settingsFile);
        } else
                result = B_OK;

        fLock.Unlock();

        return result;
}

// ---------------------------------------------------------------
// GetConfigurationMessage
//
// Saves the current settings to the BMessage passed to the
// function
//
// Preconditions:
//
// Parameters:  pmsg    pointer to BMessage where the settings
//                                              will be stored
//
// Postconditions:
//
// Returns: B_OK if there were no errors or an error code from
// BMessage::RemoveName() or BMessage::AddBool() if there were
// errors
// ---------------------------------------------------------------
status_t
TranslatorSettings::GetConfigurationMessage(BMessage *pmsg)
{
        status_t result = B_BAD_VALUE;

        if (pmsg) {
                int32 i;
                for (i = 0; i < fDefCount; i++) {
                        result = pmsg->RemoveName(fDefaults[i].name);
                        if (result != B_OK && result != B_NAME_NOT_FOUND)
                                break;
                }
                if (i == fDefCount) {
                        fLock.Lock();
                        result = B_OK;

                        const TranSetting *defs = fDefaults;
                        for (i = 0; i < fDefCount && result >= B_OK; i++) {
                                switch (defs[i].dataType) {
                                        case TRAN_SETTING_BOOL:
                                                result = pmsg->AddBool(defs[i].name,
                                                        SetGetBool(defs[i].name));
                                                break;

                                        case TRAN_SETTING_INT32:
                                                result = pmsg->AddInt32(defs[i].name,
                                                        SetGetInt32(defs[i].name));
                                                break;

                                        default:
                                                // ASSERT here? Erase the bogus setting entry instead?
                                                break;
                                }
                        }

                        fLock.Unlock();
                }
        }

        return result;
}

// ---------------------------------------------------------------
// FindTranSetting
//
// Returns a pointer to the TranSetting with the given name
//
//
// Preconditions:
//
// Parameters:  name    name of the TranSetting to find
//
//
// Postconditions:
//
// Returns: NULL if the TranSetting cannot be found, or a pointer
//          to the desired TranSetting if it is found
// ---------------------------------------------------------------
const TranSetting *
TranslatorSettings::FindTranSetting(const char *name)
{
        for (int32 i = 0; i < fDefCount; i++) {
                if (!strcmp(fDefaults[i].name, name))
                        return fDefaults + i;
        }
        return NULL;
}

// ---------------------------------------------------------------
// SetGetBool
//
// Sets the state of the bool setting identified by the given name
//
//
// Preconditions:
//
// Parameters:  name    identifies the setting to set or get
//
//                              pbool   the new value for the bool, or, if null,
//                                              it indicates that the caller wants to Get
//                                              rather than Set
//
// Postconditions:
//
// Returns: the prior value of the setting
// ---------------------------------------------------------------
bool
TranslatorSettings::SetGetBool(const char *name, bool *pbool)
{
        bool bprevValue;

        fLock.Lock();

        const TranSetting *def = FindTranSetting(name);
        if (def) {
                fSettingsMsg.FindBool(def->name, &bprevValue);
                if (pbool)
                        fSettingsMsg.ReplaceBool(def->name, *pbool);
        } else
                bprevValue = false;

        fLock.Unlock();

        return bprevValue;
}

// ---------------------------------------------------------------
// SetGetInt32
//
// Sets the state of the int32 setting identified by the given name
//
//
// Preconditions:
//
// Parameters:  name    identifies the setting to set or get
//
//                              pint32  the new value for the setting, or, if null,
//                                              it indicates that the caller wants to Get
//                                              rather than Set
//
// Postconditions:
//
// Returns: the prior value of the setting
// ---------------------------------------------------------------
int32
TranslatorSettings::SetGetInt32(const char *name, int32 *pint32)
{
        int32 prevValue;

        fLock.Lock();

        const TranSetting *def = FindTranSetting(name);
        if (def) {
                fSettingsMsg.FindInt32(def->name, &prevValue);
                if (pint32)
                        fSettingsMsg.ReplaceInt32(def->name, *pint32);
        } else
                prevValue = 0;

        fLock.Unlock();

        return prevValue;
}