OpenShot Library | libopenshot-audio  0.1.9
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  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 
221 bool 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 
252 bool 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 
274 bool 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 
293 bool 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 
337 bool 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 
356 void 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
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
Definition: juce_Timer.cpp:323
Parses a text-based XML document and creates an XmlElement object from it.
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:340
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:493
Used to build a tree of elements representing an XML document.
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:394
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:818
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:684
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.
bool writeToFile(const File &destinationFile, StringRef dtdToUse, StringRef encodingType="UTF-8", int lineWrapLength=60) const
Writes the element to a file as an XML document.
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...
static XmlElement * parse(const File &file)
A handy static method that parses a file.
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.
XmlElement * getDocumentElement(bool onlyReadOuterDocumentElement=false)
Creates an XmlElement object to represent the main document node.
bool startsWith(StringRef text) const noexcept
Tests whether the string begins with another string.