JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_PropertiesFile.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29namespace PropertyFileConstants
30{
31 constexpr static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P');
32 constexpr static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P');
33
34 constexpr static const char* const fileTag = "PROPERTIES";
35 constexpr static const char* const valueTag = "VALUE";
36 constexpr static const char* const nameAttribute = "name";
37 constexpr static const char* const valueAttribute = "val";
38}
39
40//==============================================================================
42 : commonToAllUsers (false),
43 ignoreCaseOfKeyNames (false),
44 doNotSave (false),
45 millisecondsBeforeSaving (3000),
46 storageFormat (PropertiesFile::storeAsXML),
47 processLock (nullptr)
48{
49}
50
52{
53 // mustn't have illegal characters in this name..
54 jassert (applicationName == File::createLegalFileName (applicationName));
55
56 #if JUCE_MAC || JUCE_IOS
57 File dir (commonToAllUsers ? "/Library/"
58 : "~/Library/");
59
60 if (osxLibrarySubFolder != "Preferences"
61 && ! osxLibrarySubFolder.startsWith ("Application Support")
62 && ! osxLibrarySubFolder.startsWith ("Containers"))
63 {
64 /* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
65 have changed their advice, and now stipulate that settings should go in "Library/Application Support",
66 or Library/Containers/[app_bundle_id] for a sandboxed app.
67
68 Because older apps would be broken by a silent change in this class's behaviour, you must now
69 explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
70
71 In newer apps, you should always set this to "Application Support"
72 or "Application Support/YourSubFolderName".
73
74 If your app needs to load settings files that were created by older versions of juce and
75 you want to maintain backwards-compatibility, then you can set this to "Preferences".
76 But.. for better Apple-compliance, the recommended approach would be to write some code that
77 finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
78 and then uses the new path.
79 */
81
82 dir = dir.getChildFile ("Application Support");
83 }
84 else
85 {
86 dir = dir.getChildFile (osxLibrarySubFolder);
87 }
88
89 if (folderName.isNotEmpty())
90 dir = dir.getChildFile (folderName);
91
92 #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
93 auto dir = File (commonToAllUsers ? "/var" : "~")
94 .getChildFile (folderName.isNotEmpty() ? folderName
95 : ("." + applicationName));
96
97 #elif JUCE_WINDOWS
100
101 if (dir == File())
102 return {};
103
104 dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
105 : applicationName);
106 #endif
107
108 return (filenameSuffix.startsWithChar (L'.')
109 ? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
110 : dir.getChildFile (applicationName + "." + filenameSuffix));
111}
112
113
114//==============================================================================
116 : PropertySet (o.ignoreCaseOfKeyNames),
117 file (f), options (o)
118{
119 reload();
120}
121
123 : PropertySet (o.ignoreCaseOfKeyNames),
124 file (o.getDefaultFile()), options (o)
125{
126 reload();
127}
128
130{
131 ProcessScopedLock pl (createProcessLock());
132
133 if (pl != nullptr && ! pl->isLocked())
134 return false; // locking failure..
135
136 loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
137 return loadedOk;
138}
139
144
145InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
146{
147 return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
148}
149
151{
152 const ScopedLock sl (getLock());
153 return (! needsWriting) || save();
154}
155
157{
158 const ScopedLock sl (getLock());
159 return needsWriting;
160}
161
163{
164 const ScopedLock sl (getLock());
165 needsWriting = needsToBeSaved_;
166}
167
169{
170 const ScopedLock sl (getLock());
171
172 stopTimer();
173
174 if (options.doNotSave
175 || file == File()
176 || file.isDirectory()
178 return false;
179
180 if (options.storageFormat == storeAsXML)
181 return saveAsXml();
182
183 return saveAsBinary();
184}
185
186bool PropertiesFile::loadAsXml()
187{
188 if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag))
189 {
190 for (auto* e : doc->getChildWithTagNameIterator (PropertyFileConstants::valueTag))
191 {
192 auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
193
194 if (name.isNotEmpty())
195 getAllProperties().set (name,
196 e->getFirstChildElement() != nullptr
197 ? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader())
198 : e->getStringAttribute (PropertyFileConstants::valueAttribute));
199 }
200
201 return true;
202 }
203
204 return false;
205}
206
207bool PropertiesFile::saveAsXml()
208{
209 XmlElement doc (PropertyFileConstants::fileTag);
210 auto& props = getAllProperties();
211
212 for (int i = 0; i < props.size(); ++i)
213 {
214 auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
215 e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
216
217 // if the value seems to contain xml, store it as such..
218 if (auto childElement = parseXML (props.getAllValues() [i]))
219 e->addChildElement (childElement.release());
220 else
221 e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
222 }
223
224 ProcessScopedLock pl (createProcessLock());
225
226 if (pl != nullptr && ! pl->isLocked())
227 return false; // locking failure..
228
229 if (doc.writeTo (file, {}))
230 {
231 needsWriting = false;
232 return true;
233 }
234
235 return false;
236}
237
238bool PropertiesFile::loadAsBinary()
239{
240 FileInputStream fileStream (file);
241
242 if (fileStream.openedOk())
243 {
244 auto magicNumber = fileStream.readInt();
245
246 if (magicNumber == PropertyFileConstants::magicNumberCompressed)
247 {
248 SubregionStream subStream (&fileStream, 4, -1, false);
249 GZIPDecompressorInputStream gzip (subStream);
250 return loadAsBinary (gzip);
251 }
252
253 if (magicNumber == PropertyFileConstants::magicNumber)
254 return loadAsBinary (fileStream);
255 }
256
257 return false;
258}
259
260bool PropertiesFile::loadAsBinary (InputStream& input)
261{
262 BufferedInputStream in (input, 2048);
263
264 int numValues = in.readInt();
265
266 while (--numValues >= 0 && ! in.isExhausted())
267 {
268 auto key = in.readString();
269 auto value = in.readString();
270 jassert (key.isNotEmpty());
271
272 if (key.isNotEmpty())
273 getAllProperties().set (key, value);
274 }
275
276 return true;
277}
278
279bool PropertiesFile::saveAsBinary()
280{
281 ProcessScopedLock pl (createProcessLock());
282
283 if (pl != nullptr && ! pl->isLocked())
284 return false; // locking failure..
285
286 TemporaryFile tempFile (file);
287
288 {
289 FileOutputStream out (tempFile.getFile());
290
291 if (! out.openedOk())
292 return false;
293
294 if (options.storageFormat == storeAsCompressedBinary)
295 {
296 out.writeInt (PropertyFileConstants::magicNumberCompressed);
297 out.flush();
298
299 GZIPCompressorOutputStream zipped (out, 9);
300
301 if (! writeToStream (zipped))
302 return false;
303 }
304 else
305 {
306 // have you set up the storage option flags correctly?
307 jassert (options.storageFormat == storeAsBinary);
308
309 out.writeInt (PropertyFileConstants::magicNumber);
310
311 if (! writeToStream (out))
312 return false;
313 }
314 }
315
316 if (! tempFile.overwriteTargetFileWithTemporary())
317 return false;
318
319 needsWriting = false;
320 return true;
321}
322
323bool PropertiesFile::writeToStream (OutputStream& out)
324{
325 auto& props = getAllProperties();
326 auto& keys = props.getAllKeys();
327 auto& values = props.getAllValues();
328 auto numProperties = props.size();
329
330 if (! out.writeInt (numProperties))
331 return false;
332
333 for (int i = 0; i < numProperties; ++i)
334 {
335 if (! out.writeString (keys[i])) return false;
336 if (! out.writeString (values[i])) return false;
337 }
338
339 return true;
340}
341
342void PropertiesFile::timerCallback()
343{
344 saveIfNeeded();
345}
346
348{
350 needsWriting = true;
351
352 if (options.millisecondsBeforeSaving > 0)
354 else if (options.millisecondsBeforeSaving == 0)
355 saveIfNeeded();
356}
357
358} // namespace juce
static constexpr uint16 makeInt(uint8 leastSig, uint8 mostSig) noexcept
Constructs a 16-bit integer from its constituent bytes, in order of significance.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
Represents a local file or directory.
Definition juce_File.h:45
bool isDirectory() const
Checks whether the file is a directory that exists.
File getChildFile(StringRef relativeOrAbsolutePath) const
Returns a file that represents a relative (or absolute) sub-path of the current one.
@ userApplicationDataDirectory
The folder in which applications store their persistent user-specific settings.
Definition juce_File.h:895
@ commonApplicationDataDirectory
An equivalent of the userApplicationDataDirectory folder that is shared by all users of the computer,...
Definition juce_File.h:907
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
Finds the location of a special type of file or directory, such as a home folder or documents folder.
File getParentDirectory() const
Returns the directory that contains this file or directory.
File withFileExtension(StringRef newExtension) const
Returns a version of this file with a different file extension.
static String createLegalFileName(const String &fileNameToFix)
Returns a version of a filename with any illegal characters removed.
bool exists() const
Checks whether the file actually exists.
Result createDirectory() const
Creates a new directory for this filename.
Automatically locks and unlocks a mutex object.
Automatically locks and unlocks an InterProcessLock object.
Wrapper on a file that stores a list of key/value data pairs.
~PropertiesFile() override
Destructor.
PropertiesFile(const Options &options)
Creates a PropertiesFile object.
void setNeedsToBeSaved(bool needsToBeSaved)
Explicitly sets the flag to indicate whether the file needs saving or not.
bool reload()
Attempts to reload the settings from the file.
bool needsToBeSaved() const
Returns true if the properties have been altered since the last time they were saved.
bool saveIfNeeded()
This will flush all the values to disk if they've changed since the last time they were saved.
bool save()
This will force a write-to-disk of the current values, regardless of whether anything has changed sin...
void propertyChanged() override
Subclasses can override this to be told when one of the properties has been changed.
A set of named property values, which can be strings, integers, floating point, etc.
StringPairArray & getAllProperties() noexcept
Returns the keys/value pair array containing all the properties.
const CriticalSection & getLock() const noexcept
Returns the lock used when reading or writing to this set.
void set(const String &key, const String &value)
Adds or amends a key/value pair.
void stopTimer() noexcept
Stops the timer.
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
typedef int
JUCE Namespace.
std::unique_ptr< XmlElement > parseXMLIfTagMatches(const String &textToParse, StringRef requiredTag)
Does an inexpensive check to see whether the top-level element has the given tag name,...
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
Attempts to parse some XML text, returning a new XmlElement if it was valid.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
Structure describing properties file options.
Options()
Creates an empty Options structure.
StorageFormat storageFormat
Specifies whether the file should be written as XML, binary, etc.
File getDefaultFile() const
This can be called to suggest a file that should be used, based on the values in this structure.
InterProcessLock * processLock
An optional InterprocessLock object that will be used to prevent multiple threads or processes from w...
bool doNotSave
If set to true, this prevents the file from being written to disk.
int millisecondsBeforeSaving
If this is zero or greater, then after a value is changed, the object will wait for this amount of ti...
A struct containing options for formatting the text when representing an XML element as a string.
TextFormat withoutHeader() const
returns a copy of this format with the addDefaultHeader flag set to false.
TextFormat singleLine() const
returns a copy of this format with newLineChars set to nullptr.