OpenShot Library | OpenShotAudio  0.2.2
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 
27 namespace juce
28 {
29 
30 namespace 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..
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  {
88  }
89 
90  if (folderName.isNotEmpty())
91  dir = dir.getChildFile (folderName);
92 
93  #elif JUCE_LINUX || JUCE_ANDROID
94  auto dir = File (commonToAllUsers ? "/var" : "~")
96  : ("." + applicationName));
97 
98  #elif JUCE_WINDOWS
101 
102  if (dir == File())
103  return {};
104 
106  : applicationName);
107  #endif
108 
109  return (filenameSuffix.startsWithChar (L'.')
112 }
113 
114 
115 //==============================================================================
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 
146 InterProcessLock::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 
163 void 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()
178  || ! file.getParentDirectory().createDirectory())
179  return false;
180 
181  if (options.storageFormat == storeAsXML)
182  return saveAsXml();
183 
184  return saveAsBinary();
185 }
186 
187 bool 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 
208 bool 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 
239 bool 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 
261 bool 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 
280 bool 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 
324 bool 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 
343 void 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
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
Definition: juce_Timer.cpp:323
StringPairArray & getAllProperties() noexcept
Returns the keys/value pair array containing all the properties.
String filenameSuffix
The suffix to use for your properties file.
String osxLibrarySubFolder
If you&#39;re using properties files on a Mac, you must set this value - failure to do so will cause a ru...
String applicationName
The name of your application - this is used to help generate the path and filename at which the prope...
Manages a temporary file, which will be deleted when this object is deleted.
void stopTimer() noexcept
Stops the timer.
Definition: juce_Timer.cpp:348
Structure describing properties file options.
String folderName
The name of a subfolder in which you&#39;d like your properties file to live.
Wraps another input stream, and reads from a specific part of it.
bool overwriteTargetFileWithTemporary() const
Tries to move the temporary file to overwrite the target file that was specified in the constructor...
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
static JUCE_CONSTEXPR uint16 makeInt(uint8 leastSig, uint8 mostSig) noexcept
Constructs a 16-bit integer from its constituent bytes, in order of significance. ...
const File & getFile() const noexcept
Returns the temporary file.
~PropertiesFile() override
Destructor.
A set of named property values, which can be strings, integers, floating point, etc.
File getParentDirectory() const
Returns the directory that contains this file or directory.
Definition: juce_File.cpp:360
The base class for streams that read data.
An equivalent of the userApplicationDataDirectory folder that is shared by all users of the computer...
Definition: juce_File.h:874
Result createDirectory() const
Creates a new directory for this filename.
Definition: juce_File.cpp:513
Used to build a tree of elements representing an XML document.
void writeTo(OutputStream &output, const TextFormat &format={}) const
Writes the document to a stream as UTF-8.
InterProcessLock * processLock
An optional InterprocessLock object that will be used to prevent multiple threads or processes from w...
StorageFormat storageFormat
Specifies whether the file should be written as XML, binary, etc.
A stream which uses zlib to compress the data written into it.
File getChildFile(StringRef relativeOrAbsolutePath) const
Returns a file that represents a relative (or absolute) sub-path of the current one.
Definition: juce_File.cpp:414
bool exists() const
Checks whether the file actually exists.
Automatically locks and unlocks an InterProcessLock object.
PropertiesFile(const Options &options)
Creates a PropertiesFile object.
static String createLegalFileName(const String &fileNameToFix)
Returns a version of a filename with any illegal characters removed.
Definition: juce_File.cpp:838
bool save()
This will force a write-to-disk of the current values, regardless of whether anything has changed sin...
bool doNotSave
If set to true, this prevents the file from being written to disk.
XmlElement * createNewChildElement(StringRef tagName)
Creates a new element with the given name and returns it, after adding it as a child element...
int millisecondsBeforeSaving
If this is zero or greater, then after a value is changed, the object will wait for this amount of ti...
Wraps another input stream, and reads from it using an intermediate buffer.
bool saveIfNeeded()
This will flush all the values to disk if they&#39;ve changed since the last time they were saved...
File withFileExtension(StringRef newExtension) const
Returns a version of this file with a different file extension.
Definition: juce_File.cpp:704
void propertyChanged() override
bool openedOk() const noexcept
Returns true if the stream opened without problems.
Wrapper on a file that stores a list of key/value data pairs.
virtual int readInt()
Reads four bytes from the stream as a little-endian 32-bit value.
bool reload()
Attempts to reload the settings from the file.
void setAttribute(const Identifier &attributeName, const String &newValue)
Adds a named attribute to the element.
void setNeedsToBeSaved(bool needsToBeSaved)
Explicitly sets the flag to indicate whether the file needs saving or not.
String readString() override
Reads a zero-terminated UTF-8 string from the stream.
bool commonToAllUsers
If true, the file will be created in a location that&#39;s shared between users.
Represents a local file or directory.
Definition: juce_File.h:44
bool ignoreCaseOfKeyNames
If true, this means that property names are matched in a case-insensitive manner. ...
The base class for streams that write data to some kind of destination.
bool needsToBeSaved() const
Returns true if the properties have been altered since the last time they were saved.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners. ...
void set(const String &key, const String &value)
Adds or amends a key/value pair.
An input stream that reads from a local file.
bool startsWithChar(juce_wchar character) const noexcept
Tests whether the string begins with a particular character.
File getDefaultFile() const
This can be called to suggest a file that should be used, based on the values in this structure...
An output stream that writes into a local file.
The folder in which applications store their persistent user-specific settings.
Definition: juce_File.h:862
virtual bool writeInt(int value)
Writes a 32-bit integer to the stream in a little-endian byte order.
Options()
Creates an empty Options structure.
bool isExhausted() override
Returns true if the stream has no more data to read.
bool isDirectory() const
Checks whether the file is a directory that exists.
virtual bool writeString(const String &text)
Stores a string in the stream in a binary format.
This stream will decompress a source-stream using zlib.
Automatically locks and unlocks a mutex object.
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...
const CriticalSection & getLock() const noexcept
Returns the lock used when reading or writing to this set.
A struct containing options for formatting the text when representing an XML element as a string...
bool startsWith(StringRef text) const noexcept
Tests whether the string begins with another string.