OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
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
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
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 if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag))
190 {
191 forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag)
192 {
193 auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
194
195 if (name.isNotEmpty())
196 getAllProperties().set (name,
197 e->getFirstChildElement() != nullptr
198 ? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader())
199 : e->getStringAttribute (PropertyFileConstants::valueAttribute));
200 }
201
202 return true;
203 }
204
205 return false;
206}
207
208bool PropertiesFile::saveAsXml()
209{
210 XmlElement doc (PropertyFileConstants::fileTag);
211 auto& props = getAllProperties();
212
213 for (int i = 0; i < props.size(); ++i)
214 {
215 auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
216 e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
217
218 // if the value seems to contain xml, store it as such..
219 if (auto childElement = parseXML (props.getAllValues() [i]))
220 e->addChildElement (childElement.release());
221 else
222 e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
223 }
224
225 ProcessScopedLock pl (createProcessLock());
226
227 if (pl != nullptr && ! pl->isLocked())
228 return false; // locking failure..
229
230 if (doc.writeTo (file, {}))
231 {
232 needsWriting = false;
233 return true;
234 }
235
236 return false;
237}
238
239bool PropertiesFile::loadAsBinary()
240{
241 FileInputStream fileStream (file);
242
243 if (fileStream.openedOk())
244 {
245 auto magicNumber = fileStream.readInt();
246
247 if (magicNumber == PropertyFileConstants::magicNumberCompressed)
248 {
249 SubregionStream subStream (&fileStream, 4, -1, false);
250 GZIPDecompressorInputStream gzip (subStream);
251 return loadAsBinary (gzip);
252 }
253
254 if (magicNumber == PropertyFileConstants::magicNumber)
255 return loadAsBinary (fileStream);
256 }
257
258 return false;
259}
260
261bool PropertiesFile::loadAsBinary (InputStream& input)
262{
263 BufferedInputStream in (input, 2048);
264
265 int numValues = in.readInt();
266
267 while (--numValues >= 0 && ! in.isExhausted())
268 {
269 auto key = in.readString();
270 auto value = in.readString();
271 jassert (key.isNotEmpty());
272
273 if (key.isNotEmpty())
274 getAllProperties().set (key, value);
275 }
276
277 return true;
278}
279
280bool PropertiesFile::saveAsBinary()
281{
282 ProcessScopedLock pl (createProcessLock());
283
284 if (pl != nullptr && ! pl->isLocked())
285 return false; // locking failure..
286
287 TemporaryFile tempFile (file);
288
289 {
290 FileOutputStream out (tempFile.getFile());
291
292 if (! out.openedOk())
293 return false;
294
295 if (options.storageFormat == storeAsCompressedBinary)
296 {
297 out.writeInt (PropertyFileConstants::magicNumberCompressed);
298 out.flush();
299
300 GZIPCompressorOutputStream zipped (out, 9);
301
302 if (! writeToStream (zipped))
303 return false;
304 }
305 else
306 {
307 // have you set up the storage option flags correctly?
308 jassert (options.storageFormat == storeAsBinary);
309
310 out.writeInt (PropertyFileConstants::magicNumber);
311
312 if (! writeToStream (out))
313 return false;
314 }
315 }
316
317 if (! tempFile.overwriteTargetFileWithTemporary())
318 return false;
319
320 needsWriting = false;
321 return true;
322}
323
324bool PropertiesFile::writeToStream (OutputStream& out)
325{
326 auto& props = getAllProperties();
327 auto& keys = props.getAllKeys();
328 auto& values = props.getAllValues();
329 auto numProperties = props.size();
330
331 if (! out.writeInt (numProperties))
332 return false;
333
334 for (int i = 0; i < numProperties; ++i)
335 {
336 if (! out.writeString (keys[i])) return false;
337 if (! out.writeString (values[i])) return false;
338 }
339
340 return true;
341}
342
343void PropertiesFile::timerCallback()
344{
345 saveIfNeeded();
346}
347
349{
351 needsWriting = true;
352
353 if (options.millisecondsBeforeSaving > 0)
355 else if (options.millisecondsBeforeSaving == 0)
356 saveIfNeeded();
357}
358
359} // namespace juce
static JUCE_CONSTEXPR uint16 makeInt(uint8 leastSig, uint8 mostSig) noexcept
bool isDirectory() const
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
@ userApplicationDataDirectory
Definition juce_File.h:858
@ commonApplicationDataDirectory
Definition juce_File.h:870
File getParentDirectory() const
static String createLegalFileName(const String &fileNameToFix)
bool exists() const
Result createDirectory() const
PropertiesFile(const Options &options)
void setNeedsToBeSaved(bool needsToBeSaved)
StringPairArray & getAllProperties() noexcept
const CriticalSection & getLock() const noexcept
void set(const String &key, const String &value)
void stopTimer() noexcept
void startTimer(int intervalInMilliseconds) noexcept