OpenShot Audio Library | OpenShotAudio: juce_PropertiesFile.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_PropertiesFile.cpp
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//==============================================================================
41PropertiesFile::Options::Options()
42 : commonToAllUsers (false),
43 ignoreCaseOfKeyNames (false),
44 doNotSave (false),
45 millisecondsBeforeSaving (3000),
46 storageFormat (PropertiesFile::storeAsXML),
47 processLock (nullptr)
48{
49}
50
51 File PropertiesFile::Options::getDefaultFile() const
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 */
80 jassertfalse;
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
98 auto dir = File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
99 : File::userApplicationDataDirectory);
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//==============================================================================
115 PropertiesFile::PropertiesFile (const File& f, const Options& o)
116 : PropertySet (o.ignoreCaseOfKeyNames),
117 file (f), options (o)
118{
119 reload();
120}
121
122PropertiesFile::PropertiesFile (const Options& o)
123 : PropertySet (o.ignoreCaseOfKeyNames),
124 file (o.getDefaultFile()), options (o)
125{
126 reload();
127}
128
129 bool PropertiesFile::reload()
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
140PropertiesFile::~PropertiesFile()
141{
142 saveIfNeeded();
143}
144
145 InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
146{
147 return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
148}
149
150 bool PropertiesFile::saveIfNeeded()
151{
152 const ScopedLock sl (getLock());
153 return (! needsWriting) || save();
154}
155
156 bool PropertiesFile::needsToBeSaved() const
157{
158 const ScopedLock sl (getLock());
159 return needsWriting;
160}
161
162 void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
163{
164 const ScopedLock sl (getLock());
165 needsWriting = needsToBeSaved_;
166}
167
168 bool PropertiesFile::save()
169{
170 const ScopedLock sl (getLock());
171
172 stopTimer();
173
174 if (options.doNotSave
175 || file == File()
176 || file.isDirectory()
177 || ! file.getParentDirectory().createDirectory())
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
347 void PropertiesFile::propertyChanged()
348{
349 sendChangeMessage();
350 needsWriting = true;
351
352 if (options.millisecondsBeforeSaving > 0)
353 startTimer (options.millisecondsBeforeSaving);
354 else if (options.millisecondsBeforeSaving == 0)
355 saveIfNeeded();
356}
357
358} // 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::getParentDirectory
File getParentDirectory() const
Definition juce_File.cpp:358
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::File::exists
bool exists() const
juce::File::createDirectory
Result createDirectory() const
Definition juce_File.cpp:519
juce::GenericScopedLock
Definition juce_ScopedLock.h:55
juce::InterProcessLock::ScopedLockType
Definition juce_InterProcessLock.h:72
juce::PropertiesFile
Definition juce_PropertiesFile.h:48
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
Definition juce_PropertySet.h:39
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::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::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::doNotSave
bool doNotSave
Definition juce_PropertiesFile.h:118
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