OpenShot Audio Library | OpenShotAudio  0.3.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..
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 
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
static JUCE_CONSTEXPR uint16 makeInt(uint8 leastSig, uint8 mostSig) noexcept
bool isDirectory() const
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
File getChildFile(StringRef relativeOrAbsolutePath) const
Definition: juce_File.cpp:414
@ userApplicationDataDirectory
Definition: juce_File.h:858
@ commonApplicationDataDirectory
Definition: juce_File.h:870
File getParentDirectory() const
Definition: juce_File.cpp:360
File withFileExtension(StringRef newExtension) const
Definition: juce_File.cpp:704
static String createLegalFileName(const String &fileNameToFix)
Definition: juce_File.cpp:838
bool exists() const
Result createDirectory() const
Definition: juce_File.cpp:513
PropertiesFile(const Options &options)
void setNeedsToBeSaved(bool needsToBeSaved)
void propertyChanged() override
const CriticalSection & getLock() const noexcept
StringPairArray & getAllProperties() noexcept
void set(const String &key, const String &value)
void stopTimer() noexcept
Definition: juce_Timer.cpp:348
void startTimer(int intervalInMilliseconds) noexcept
Definition: juce_Timer.cpp:323
TextFormat withoutHeader() const