Loading...
Searching...
No Matches
juce_PropertiesFile.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace PropertyFileConstants
{
constexpr static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P');
constexpr static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P');
constexpr static const char* const fileTag = "PROPERTIES";
constexpr static const char* const valueTag = "VALUE";
constexpr static const char* const nameAttribute = "name";
constexpr static const char* const valueAttribute = "val";
}
//==============================================================================
PropertiesFile::Options::Options()
: commonToAllUsers (false),
ignoreCaseOfKeyNames (false),
doNotSave (false),
millisecondsBeforeSaving (3000),
storageFormat (PropertiesFile::storeAsXML),
processLock (nullptr)
{
}
File PropertiesFile::Options::getDefaultFile() const
{
// mustn't have illegal characters in this name..
jassert (applicationName == File::createLegalFileName (applicationName));
#if JUCE_MAC || JUCE_IOS
File dir (commonToAllUsers ? "/Library/"
: "~/Library/");
if (osxLibrarySubFolder != "Preferences"
&& ! osxLibrarySubFolder.startsWith ("Application Support")
&& ! osxLibrarySubFolder.startsWith ("Containers"))
{
/* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
have changed their advice, and now stipulate that settings should go in "Library/Application Support",
or Library/Containers/[app_bundle_id] for a sandboxed app.
Because older apps would be broken by a silent change in this class's behaviour, you must now
explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
In newer apps, you should always set this to "Application Support"
or "Application Support/YourSubFolderName".
If your app needs to load settings files that were created by older versions of juce and
you want to maintain backwards-compatibility, then you can set this to "Preferences".
But.. for better Apple-compliance, the recommended approach would be to write some code that
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
and then uses the new path.
*/
jassertfalse;
dir = dir.getChildFile ("Application Support");
}
else
{
dir = dir.getChildFile (osxLibrarySubFolder);
}
if (folderName.isNotEmpty())
dir = dir.getChildFile (folderName);
#elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
auto dir = File (commonToAllUsers ? "/var" : "~")
.getChildFile (folderName.isNotEmpty() ? folderName
: ("." + applicationName));
#elif JUCE_WINDOWS
auto dir = File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
: File::userApplicationDataDirectory);
if (dir == File())
return {};
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
: applicationName);
#endif
return (filenameSuffix.startsWithChar (L'.')
? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
: dir.getChildFile (applicationName + "." + filenameSuffix));
}
//==============================================================================
PropertiesFile::PropertiesFile (const File& f, const Options& o)
: PropertySet (o.ignoreCaseOfKeyNames),
file (f), options (o)
{
reload();
}
PropertiesFile::PropertiesFile (const Options& o)
: PropertySet (o.ignoreCaseOfKeyNames),
file (o.getDefaultFile()), options (o)
{
reload();
}
bool PropertiesFile::reload()
{
ProcessScopedLock pl (createProcessLock());
if (pl != nullptr && ! pl->isLocked())
return false; // locking failure..
loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
return loadedOk;
}
PropertiesFile::~PropertiesFile()
{
saveIfNeeded();
}
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
{
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
}
bool PropertiesFile::saveIfNeeded()
{
const ScopedLock sl (getLock());
return (! needsWriting) || save();
}
bool PropertiesFile::needsToBeSaved() const
{
const ScopedLock sl (getLock());
return needsWriting;
}
void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
{
const ScopedLock sl (getLock());
needsWriting = needsToBeSaved_;
}
bool PropertiesFile::save()
{
const ScopedLock sl (getLock());
stopTimer();
if (options.doNotSave
|| file == File()
|| file.isDirectory()
|| ! file.getParentDirectory().createDirectory())
return false;
if (options.storageFormat == storeAsXML)
return saveAsXml();
return saveAsBinary();
}
bool PropertiesFile::loadAsXml()
{
if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag))
{
for (auto* e : doc->getChildWithTagNameIterator (PropertyFileConstants::valueTag))
{
auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
if (name.isNotEmpty())
getAllProperties().set (name,
e->getFirstChildElement() != nullptr
? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader())
: e->getStringAttribute (PropertyFileConstants::valueAttribute));
}
return true;
}
return false;
}
bool PropertiesFile::saveAsXml()
{
XmlElement doc (PropertyFileConstants::fileTag);
auto& props = getAllProperties();
for (int i = 0; i < props.size(); ++i)
{
auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
// if the value seems to contain xml, store it as such..
if (auto childElement = parseXML (props.getAllValues() [i]))
e->addChildElement (childElement.release());
else
e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
}
ProcessScopedLock pl (createProcessLock());
if (pl != nullptr && ! pl->isLocked())
return false; // locking failure..
if (doc.writeTo (file, {}))
{
needsWriting = false;
return true;
}
return false;
}
bool PropertiesFile::loadAsBinary()
{
FileInputStream fileStream (file);
if (fileStream.openedOk())
{
auto magicNumber = fileStream.readInt();
if (magicNumber == PropertyFileConstants::magicNumberCompressed)
{
SubregionStream subStream (&fileStream, 4, -1, false);
GZIPDecompressorInputStream gzip (subStream);
return loadAsBinary (gzip);
}
if (magicNumber == PropertyFileConstants::magicNumber)
return loadAsBinary (fileStream);
}
return false;
}
bool PropertiesFile::loadAsBinary (InputStream& input)
{
BufferedInputStream in (input, 2048);
int numValues = in.readInt();
while (--numValues >= 0 && ! in.isExhausted())
{
auto key = in.readString();
auto value = in.readString();
jassert (key.isNotEmpty());
if (key.isNotEmpty())
getAllProperties().set (key, value);
}
return true;
}
bool PropertiesFile::saveAsBinary()
{
ProcessScopedLock pl (createProcessLock());
if (pl != nullptr && ! pl->isLocked())
return false; // locking failure..
TemporaryFile tempFile (file);
{
FileOutputStream out (tempFile.getFile());
if (! out.openedOk())
return false;
if (options.storageFormat == storeAsCompressedBinary)
{
out.writeInt (PropertyFileConstants::magicNumberCompressed);
out.flush();
GZIPCompressorOutputStream zipped (out, 9);
if (! writeToStream (zipped))
return false;
}
else
{
// have you set up the storage option flags correctly?
jassert (options.storageFormat == storeAsBinary);
out.writeInt (PropertyFileConstants::magicNumber);
if (! writeToStream (out))
return false;
}
}
if (! tempFile.overwriteTargetFileWithTemporary())
return false;
needsWriting = false;
return true;
}
bool PropertiesFile::writeToStream (OutputStream& out)
{
auto& props = getAllProperties();
auto& keys = props.getAllKeys();
auto& values = props.getAllValues();
auto numProperties = props.size();
if (! out.writeInt (numProperties))
return false;
for (int i = 0; i < numProperties; ++i)
{
if (! out.writeString (keys[i])) return false;
if (! out.writeString (values[i])) return false;
}
return true;
}
void PropertiesFile::timerCallback()
{
saveIfNeeded();
}
void PropertiesFile::propertyChanged()
{
sendChangeMessage();
needsWriting = true;
if (options.millisecondsBeforeSaving > 0)
startTimer (options.millisecondsBeforeSaving);
else if (options.millisecondsBeforeSaving == 0)
saveIfNeeded();
}
} // namespace juce
juce::ByteOrder::makeInt
static constexpr uint16 makeInt(uint8 leastSig, uint8 mostSig) noexcept
Definition juce_ByteOrder.h:181
juce::ChangeBroadcaster::sendChangeMessage
void sendChangeMessage()
Definition juce_ChangeBroadcaster.cpp:65
juce::File
Definition juce_File.h:45
juce::File::isDirectory
bool isDirectory() const
juce::File::getSpecialLocation
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
juce::File::getChildFile
File getChildFile(StringRef relativeOrAbsolutePath) const
Definition juce_File.cpp:420
juce::File::userApplicationDataDirectory
@ userApplicationDataDirectory
Definition juce_File.h:895
juce::File::commonApplicationDataDirectory
@ commonApplicationDataDirectory
Definition juce_File.h:907
juce::File::withFileExtension
File withFileExtension(StringRef newExtension) const
Definition juce_File.cpp:707
juce::File::createLegalFileName
static String createLegalFileName(const String &fileNameToFix)
Definition juce_File.cpp:843
juce::InputStream
Definition juce_InputStream.h:37
juce::InterProcessLock::ScopedLockType
Definition juce_InterProcessLock.h:72
juce::OutputStream
Definition juce_OutputStream.h:38
juce::PropertiesFile::~PropertiesFile
~PropertiesFile() override
Definition juce_PropertiesFile.cpp:140
juce::PropertiesFile::PropertiesFile
PropertiesFile(const Options &options)
Definition juce_PropertiesFile.cpp:122
juce::PropertiesFile::setNeedsToBeSaved
void setNeedsToBeSaved(bool needsToBeSaved)
Definition juce_PropertiesFile.cpp:162
juce::PropertiesFile::reload
bool reload()
Definition juce_PropertiesFile.cpp:129
juce::PropertiesFile::needsToBeSaved
bool needsToBeSaved() const
Definition juce_PropertiesFile.cpp:156
juce::PropertiesFile::saveIfNeeded
bool saveIfNeeded()
Definition juce_PropertiesFile.cpp:150
juce::PropertiesFile::save
bool save()
Definition juce_PropertiesFile.cpp:168
juce::PropertiesFile::propertyChanged
void propertyChanged() override
Definition juce_PropertiesFile.cpp:347
juce::PropertySet::getAllProperties
StringPairArray & getAllProperties() noexcept
Definition juce_PropertySet.h:154
juce::PropertySet::getLock
const CriticalSection & getLock() const noexcept
Definition juce_PropertySet.h:157
juce::PropertySet::PropertySet
PropertySet(bool ignoreCaseOfKeyNames=false)
Definition juce_PropertySet.cpp:26
juce::StringPairArray::set
void set(const String &key, const String &value)
Definition juce_StringPairArray.cpp:101
juce::Timer::stopTimer
void stopTimer() noexcept
Definition juce_Timer.cpp:332
juce::Timer::startTimer
void startTimer(int intervalInMilliseconds) noexcept
Definition juce_Timer.cpp:309
juce::Timer::timerCallback
virtual void timerCallback()=0
juce::PropertiesFile::Options
Definition juce_PropertiesFile.h:61
juce::PropertiesFile::Options::Options
Options()
Definition juce_PropertiesFile.cpp:41
juce::PropertiesFile::Options::storageFormat
StorageFormat storageFormat
Definition juce_PropertiesFile.h:134
juce::PropertiesFile::Options::getDefaultFile
File getDefaultFile() const
Definition juce_PropertiesFile.cpp:51
juce::PropertiesFile::Options::processLock
InterProcessLock * processLock
Definition juce_PropertiesFile.h:143
juce::PropertiesFile::Options::osxLibrarySubFolder
String osxLibrarySubFolder
Definition juce_PropertiesFile.h:104
juce::PropertiesFile::Options::filenameSuffix
String filenameSuffix
Definition juce_PropertiesFile.h:79
juce::PropertiesFile::Options::folderName
String folderName
Definition juce_PropertiesFile.h:84
juce::PropertiesFile::Options::applicationName
String applicationName
Definition juce_PropertiesFile.h:69
juce::PropertiesFile::Options::commonToAllUsers
bool commonToAllUsers
Definition juce_PropertiesFile.h:109
juce::PropertiesFile::Options::doNotSave
bool doNotSave
Definition juce_PropertiesFile.h:118
juce::PropertiesFile::Options::ignoreCaseOfKeyNames
bool ignoreCaseOfKeyNames
Definition juce_PropertiesFile.h:115
juce::PropertiesFile::Options::millisecondsBeforeSaving
int millisecondsBeforeSaving
Definition juce_PropertiesFile.h:128
juce::XmlElement::TextFormat
Definition juce_XmlElement.h:136
juce::XmlElement::TextFormat::withoutHeader
TextFormat withoutHeader() const
Definition juce_XmlElement.cpp:345
juce::XmlElement::TextFormat::singleLine
TextFormat singleLine() const
Definition juce_XmlElement.cpp:338