OpenShot Library | libopenshot-audio 0.2.0
juce_PropertiesFile.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
11 Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12 27th April 2017).
13
14 End User License Agreement: www.juce.com/juce-5-licence
15 Privacy Policy: www.juce.com/juce-5-privacy-policy
16
17 Or: You may also use this code under the terms of the GPL v3 (see
18 www.gnu.org/licenses).
19
20 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22 DISCLAIMED.
23
24 ==============================================================================
25*/
26
27namespace juce
28{
29
30namespace PropertyFileConstants
31{
32 JUCE_CONSTEXPR static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P');
33 JUCE_CONSTEXPR static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P');
34
35 JUCE_CONSTEXPR static const char* const fileTag = "PROPERTIES";
36 JUCE_CONSTEXPR static const char* const valueTag = "VALUE";
37 JUCE_CONSTEXPR static const char* const nameAttribute = "name";
38 JUCE_CONSTEXPR static const char* const valueAttribute = "val";
39}
40
41//==============================================================================
43 : commonToAllUsers (false),
44 ignoreCaseOfKeyNames (false),
45 doNotSave (false),
46 millisecondsBeforeSaving (3000),
47 storageFormat (PropertiesFile::storeAsXML),
48 processLock (nullptr)
49{
50}
51
53{
54 // mustn't have illegal characters in this name..
55 jassert (applicationName == File::createLegalFileName (applicationName));
56
57 #if JUCE_MAC || JUCE_IOS
58 File dir (commonToAllUsers ? "/Library/"
59 : "~/Library/");
60
61 if (osxLibrarySubFolder != "Preferences"
62 && ! osxLibrarySubFolder.startsWith ("Application Support")
63 && ! osxLibrarySubFolder.startsWith ("Containers"))
64 {
65 /* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
66 have changed their advice, and now stipulate that settings should go in "Library/Application Support",
67 or Library/Containers/[app_bundle_id] for a sandboxed app.
68
69 Because older apps would be broken by a silent change in this class's behaviour, you must now
70 explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
71
72 In newer apps, you should always set this to "Application Support"
73 or "Application Support/YourSubFolderName".
74
75 If your app needs to load settings files that were created by older versions of juce and
76 you want to maintain backwards-compatibility, then you can set this to "Preferences".
77 But.. for better Apple-compliance, the recommended approach would be to write some code that
78 finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
79 and then uses the new path.
80 */
81 jassertfalse;
82
83 dir = dir.getChildFile ("Application Support");
84 }
85 else
86 {
87 dir = dir.getChildFile (osxLibrarySubFolder);
88 }
89
90 if (folderName.isNotEmpty())
91 dir = dir.getChildFile (folderName);
92
93 #elif JUCE_LINUX || JUCE_ANDROID
94 auto dir = File (commonToAllUsers ? "/var" : "~")
95 .getChildFile (folderName.isNotEmpty() ? folderName
96 : ("." + applicationName));
97
98 #elif JUCE_WINDOWS
101
102 if (dir == File())
103 return {};
104
105 dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
106 : applicationName);
107 #endif
108
109 return (filenameSuffix.startsWithChar (L'.')
110 ? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
111 : dir.getChildFile (applicationName + "." + filenameSuffix));
112}
113
114
115//==============================================================================
117 : PropertySet (o.ignoreCaseOfKeyNames),
118 file (f), options (o)
119{
120 reload();
121}
122
124 : PropertySet (o.ignoreCaseOfKeyNames),
125 file (o.getDefaultFile()), options (o)
126{
127 reload();
128}
129
131{
132 ProcessScopedLock pl (createProcessLock());
133
134 if (pl != nullptr && ! pl->isLocked())
135 return false; // locking failure..
136
137 loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
138 return loadedOk;
139}
140
142{
143 saveIfNeeded();
144}
145
146InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
147{
148 return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
149}
150
152{
153 const ScopedLock sl (getLock());
154 return (! needsWriting) || save();
155}
156
158{
159 const ScopedLock sl (getLock());
160 return needsWriting;
161}
162
163void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
164{
165 const ScopedLock sl (getLock());
166 needsWriting = needsToBeSaved_;
167}
168
170{
171 const ScopedLock sl (getLock());
172
173 stopTimer();
174
175 if (options.doNotSave
176 || file == File()
177 || file.isDirectory()
179 return false;
180
181 if (options.storageFormat == storeAsXML)
182 return saveAsXml();
183
184 return saveAsBinary();
185}
186
187bool PropertiesFile::loadAsXml()
188{
189 XmlDocument parser (file);
190 std::unique_ptr<XmlElement> doc (parser.getDocumentElement (true));
191
192 if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag))
193 {
194 doc.reset (parser.getDocumentElement());
195
196 if (doc != nullptr)
197 {
198 forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag)
199 {
200 auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
201
202 if (name.isNotEmpty())
203 getAllProperties().set (name,
204 e->getFirstChildElement() != nullptr
205 ? e->getFirstChildElement()->createDocument ("", true)
206 : e->getStringAttribute (PropertyFileConstants::valueAttribute));
207 }
208
209 return true;
210 }
211
212 // must be a pretty broken XML file we're trying to parse here,
213 // or a sign that this object needs an InterProcessLock,
214 // or just a failure reading the file. This last reason is why
215 // we don't jassertfalse here.
216 }
217
218 return false;
219}
220
221bool PropertiesFile::saveAsXml()
222{
223 XmlElement doc (PropertyFileConstants::fileTag);
224 auto& props = getAllProperties();
225
226 for (int i = 0; i < props.size(); ++i)
227 {
228 auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
229 e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
230
231 // if the value seems to contain xml, store it as such..
232 if (auto* childElement = XmlDocument::parse (props.getAllValues() [i]))
233 e->addChildElement (childElement);
234 else
235 e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
236 }
237
238 ProcessScopedLock pl (createProcessLock());
239
240 if (pl != nullptr && ! pl->isLocked())
241 return false; // locking failure..
242
243 if (doc.writeToFile (file, {}))
244 {
245 needsWriting = false;
246 return true;
247 }
248
249 return false;
250}
251
252bool PropertiesFile::loadAsBinary()
253{
254 FileInputStream fileStream (file);
255
256 if (fileStream.openedOk())
257 {
258 auto magicNumber = fileStream.readInt();
259
260 if (magicNumber == PropertyFileConstants::magicNumberCompressed)
261 {
262 SubregionStream subStream (&fileStream, 4, -1, false);
263 GZIPDecompressorInputStream gzip (subStream);
264 return loadAsBinary (gzip);
265 }
266
267 if (magicNumber == PropertyFileConstants::magicNumber)
268 return loadAsBinary (fileStream);
269 }
270
271 return false;
272}
273
274bool PropertiesFile::loadAsBinary (InputStream& input)
275{
276 BufferedInputStream in (input, 2048);
277
278 int numValues = in.readInt();
279
280 while (--numValues >= 0 && ! in.isExhausted())
281 {
282 auto key = in.readString();
283 auto value = in.readString();
284 jassert (key.isNotEmpty());
285
286 if (key.isNotEmpty())
287 getAllProperties().set (key, value);
288 }
289
290 return true;
291}
292
293bool PropertiesFile::saveAsBinary()
294{
295 ProcessScopedLock pl (createProcessLock());
296
297 if (pl != nullptr && ! pl->isLocked())
298 return false; // locking failure..
299
300 TemporaryFile tempFile (file);
301
302 {
303 FileOutputStream out (tempFile.getFile());
304
305 if (! out.openedOk())
306 return false;
307
308 if (options.storageFormat == storeAsCompressedBinary)
309 {
310 out.writeInt (PropertyFileConstants::magicNumberCompressed);
311 out.flush();
312
313 GZIPCompressorOutputStream zipped (out, 9);
314
315 if (! writeToStream (zipped))
316 return false;
317 }
318 else
319 {
320 // have you set up the storage option flags correctly?
321 jassert (options.storageFormat == storeAsBinary);
322
323 out.writeInt (PropertyFileConstants::magicNumber);
324
325 if (! writeToStream (out))
326 return false;
327 }
328 }
329
330 if (! tempFile.overwriteTargetFileWithTemporary())
331 return false;
332
333 needsWriting = false;
334 return true;
335}
336
337bool PropertiesFile::writeToStream (OutputStream& out)
338{
339 auto& props = getAllProperties();
340 auto& keys = props.getAllKeys();
341 auto& values = props.getAllValues();
342 auto numProperties = props.size();
343
344 if (! out.writeInt (numProperties))
345 return false;
346
347 for (int i = 0; i < numProperties; ++i)
348 {
349 if (! out.writeString (keys[i])) return false;
350 if (! out.writeString (values[i])) return false;
351 }
352
353 return true;
354}
355
356void PropertiesFile::timerCallback()
357{
358 saveIfNeeded();
359}
360
362{
364 needsWriting = true;
365
366 if (options.millisecondsBeforeSaving > 0)
368 else if (options.millisecondsBeforeSaving == 0)
369 saveIfNeeded();
370}
371
372} // namespace juce
static JUCE_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.
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 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:862
@ commonApplicationDataDirectory
An equivalent of the userApplicationDataDirectory folder that is shared by all users of the computer,...
Definition juce_File.h:874
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...
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.
Parses a text-based XML document and creates an XmlElement object from it.
static XmlElement * parse(const File &file)
A handy static method that parses a file.
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...