OpenShot Audio Library | OpenShotAudio  0.6.0
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 
26 namespace juce
27 {
28 
29 namespace 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 //==============================================================================
42  : commonToAllUsers (false),
43  ignoreCaseOfKeyNames (false),
44  doNotSave (false),
45  millisecondsBeforeSaving (3000),
46  storageFormat (PropertiesFile::storeAsXML),
47  processLock (nullptr)
48 {
49 }
50 
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
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 //==============================================================================
116  : PropertySet (o.ignoreCaseOfKeyNames),
117  file (f), options (o)
118 {
119  reload();
120 }
121 
123  : PropertySet (o.ignoreCaseOfKeyNames),
124  file (o.getDefaultFile()), options (o)
125 {
126  reload();
127 }
128 
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 
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 
151 {
152  const ScopedLock sl (getLock());
153  return (! needsWriting) || save();
154 }
155 
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 
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 
186 bool 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 
207 bool 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 
238 bool 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 
260 bool 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 
279 bool 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 
323 bool 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 
342 void PropertiesFile::timerCallback()
343 {
344  saveIfNeeded();
345 }
346 
348 {
350  needsWriting = true;
351 
352  if (options.millisecondsBeforeSaving > 0)
354  else if (options.millisecondsBeforeSaving == 0)
355  saveIfNeeded();
356 }
357 
358 } // namespace juce
constexpr static 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:420
@ userApplicationDataDirectory
Definition: juce_File.h:895
@ commonApplicationDataDirectory
Definition: juce_File.h:907
File getParentDirectory() const
Definition: juce_File.cpp:358
File withFileExtension(StringRef newExtension) const
Definition: juce_File.cpp:707
static String createLegalFileName(const String &fileNameToFix)
Definition: juce_File.cpp:843
bool exists() const
Result createDirectory() const
Definition: juce_File.cpp:519
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:332
void startTimer(int intervalInMilliseconds) noexcept
Definition: juce_Timer.cpp:309
TextFormat withoutHeader() const