diff --git a/modules/juce_core/containers/juce_PropertySet.h b/modules/juce_core/containers/juce_PropertySet.h index b3c4d581f4..1939442a4d 100644 --- a/modules/juce_core/containers/juce_PropertySet.h +++ b/modules/juce_core/containers/juce_PropertySet.h @@ -46,18 +46,15 @@ class JUCE_API PropertySet public: //============================================================================== /** Creates an empty PropertySet. - - @param ignoreCaseOfKeyNames if true, the names of properties are compared in a - case-insensitive way + @param ignoreCaseOfKeyNames if true, the names of properties are compared in a + case-insensitive way */ PropertySet (bool ignoreCaseOfKeyNames = false); - /** Creates a copy of another PropertySet. - */ + /** Creates a copy of another PropertySet. */ PropertySet (const PropertySet& other); - /** Copies another PropertySet over this one. - */ + /** Copies another PropertySet over this one. */ PropertySet& operator= (const PropertySet& other); /** Destructor. */ @@ -152,7 +149,6 @@ public: //============================================================================== /** Deletes a property. - @param keyName the name of the property to delete. (This mustn't be an empty string) */ void removeValue (const String& keyName); @@ -172,17 +168,13 @@ public: //============================================================================== /** Returns an XML element which encapsulates all the items in this property set. - The string parameter is the tag name that should be used for the node. - @see restoreFromXml */ XmlElement* createXml (const String& nodeName) const; /** Reloads a set of properties that were previously stored as XML. - The node passed in must have been created by the createXml() method. - @see createXml */ void restoreFromXml (const XmlElement& xml); @@ -208,12 +200,10 @@ public: PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; } protected: - //============================================================================== /** Subclasses can override this to be told when one of the properies has been changed. */ virtual void propertyChanged(); private: - //============================================================================== StringPairArray properties; PropertySet* fallbackProperties; CriticalSection lock; diff --git a/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index e51eaf978f..ef77829b83 100644 --- a/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -49,7 +49,7 @@ File PropertiesFile::Options::getDefaultFile() const // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); - #if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS File dir (commonToAllUsers ? "/Library/" : "~/Library/"); @@ -81,12 +81,12 @@ File PropertiesFile::Options::getDefaultFile() const if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); - #elif JUCE_LINUX || JUCE_ANDROID + #elif JUCE_LINUX || JUCE_ANDROID const File dir ((commonToAllUsers ? "/var/" : "~/") + (folderName.isNotEmpty() ? folderName : ("." + applicationName))); - #elif JUCE_WINDOWS + #elif JUCE_WINDOWS File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); @@ -95,7 +95,7 @@ File PropertiesFile::Options::getDefaultFile() const dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); - #endif + #endif return dir.getChildFile (applicationName) .withFileExtension (filenameSuffix); @@ -121,90 +121,17 @@ PropertiesFile::PropertiesFile (const Options& options_) void PropertiesFile::initialise() { - // You need to correctly specify just one storage format for the file ProcessScopedLock pl (createProcessLock()); if (pl != nullptr && ! pl->isLocked()) return; // locking failure.. - ScopedPointer fileStream (file.createInputStream()); - - if (fileStream != nullptr) - { - int magicNumber = fileStream->readInt(); - - if (magicNumber == PropertyFileConstants::magicNumberCompressed) - { - fileStream = new GZIPDecompressorInputStream (new SubregionStream (fileStream.release(), 4, -1, true), true); - magicNumber = PropertyFileConstants::magicNumber; - } - - if (magicNumber == PropertyFileConstants::magicNumber) - { - loadedOk = true; - BufferedInputStream in (fileStream.release(), 2048, true); - - int numValues = in.readInt(); - - while (--numValues >= 0 && ! in.isExhausted()) - { - const String key (in.readString()); - const String value (in.readString()); - - jassert (key.isNotEmpty()); - if (key.isNotEmpty()) - getAllProperties().set (key, value); - } - } - else - { - // Not a binary props file - let's see if it's XML.. - fileStream = nullptr; - - XmlDocument parser (file); - ScopedPointer doc (parser.getDocumentElement (true)); - - if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) - { - doc = parser.getDocumentElement(); - - if (doc != nullptr) - { - loadedOk = true; - - forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) - { - const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute)); - - if (name.isNotEmpty()) - { - getAllProperties().set (name, - e->getFirstChildElement() != nullptr - ? e->getFirstChildElement()->createDocument (String::empty, true) - : e->getStringAttribute (PropertyFileConstants::valueAttribute)); - } - } - } - else - { - // must be a pretty broken XML file we're trying to parse here, - // or a sign that this object needs an InterProcessLock, - // or just a failure reading the file. This last reason is why - // we don't jassertfalse here. - } - } - } - } - else - { - loadedOk = ! file.exists(); - } + loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml(); } PropertiesFile::~PropertiesFile() { - if (! saveIfNeeded()) - jassertfalse; + saveIfNeeded(); } InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const @@ -242,79 +169,167 @@ bool PropertiesFile::save() return false; if (options.storageFormat == storeAsXML) + return saveAsXml(); + else + return saveAsBinary(); +} + +bool PropertiesFile::loadAsXml() +{ + XmlDocument parser (file); + ScopedPointer doc (parser.getDocumentElement (true)); + + if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) { - XmlElement doc (PropertyFileConstants::fileTag); + doc = parser.getDocumentElement(); - for (int i = 0; i < getAllProperties().size(); ++i) + if (doc != nullptr) { - XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag); - e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]); + forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) + { + const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute)); - // if the value seems to contain xml, store it as such.. - XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]); + if (name.isNotEmpty()) + { + getAllProperties().set (name, + e->getFirstChildElement() != nullptr + ? e->getFirstChildElement()->createDocument (String::empty, true) + : e->getStringAttribute (PropertyFileConstants::valueAttribute)); + } + } - if (childElement != nullptr) - e->addChildElement (childElement); - else - e->setAttribute (PropertyFileConstants::valueAttribute, - getAllProperties().getAllValues() [i]); + return true; + } + else + { + // must be a pretty broken XML file we're trying to parse here, + // or a sign that this object needs an InterProcessLock, + // or just a failure reading the file. This last reason is why + // we don't jassertfalse here. } + } + + return false; +} + +bool PropertiesFile::saveAsXml() +{ + XmlElement doc (PropertyFileConstants::fileTag); + + for (int i = 0; i < getAllProperties().size(); ++i) + { + XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag); + e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]); + + // if the value seems to contain xml, store it as such.. + XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]); - ProcessScopedLock pl (createProcessLock()); + if (childElement != nullptr) + e->addChildElement (childElement); + else + e->setAttribute (PropertyFileConstants::valueAttribute, + getAllProperties().getAllValues() [i]); + } + + ProcessScopedLock pl (createProcessLock()); + + if (pl != nullptr && ! pl->isLocked()) + return false; // locking failure.. + + if (doc.writeToFile (file, String::empty)) + { + needsWriting = false; + return true; + } - if (pl != nullptr && ! pl->isLocked()) - return false; // locking failure.. + return false; +} - if (doc.writeToFile (file, String::empty)) +bool PropertiesFile::loadAsBinary() +{ + FileInputStream fileStream (file); + + if (fileStream.openedOk()) + { + const int magicNumber = fileStream.readInt(); + + if (magicNumber == PropertyFileConstants::magicNumberCompressed) { - needsWriting = false; - return true; + SubregionStream subStream (&fileStream, 4, -1, false); + GZIPDecompressorInputStream gzip (subStream); + return loadAsBinary (gzip); + } + else if (magicNumber == PropertyFileConstants::magicNumber) + { + return loadAsBinary (fileStream); } } - else + + return false; +} + +bool PropertiesFile::loadAsBinary (InputStream& input) +{ + BufferedInputStream in (input, 2048); + + int numValues = in.readInt(); + + while (--numValues >= 0 && ! in.isExhausted()) { - ProcessScopedLock pl (createProcessLock()); + const String key (in.readString()); + const String value (in.readString()); + + jassert (key.isNotEmpty()); + if (key.isNotEmpty()) + getAllProperties().set (key, value); + } + + return true; +} + +bool PropertiesFile::saveAsBinary() +{ + ProcessScopedLock pl (createProcessLock()); - if (pl != nullptr && ! pl->isLocked()) - return false; // locking failure.. + if (pl != nullptr && ! pl->isLocked()) + return false; // locking failure.. - TemporaryFile tempFile (file); - ScopedPointer out (tempFile.getFile().createOutputStream()); + TemporaryFile tempFile (file); + ScopedPointer out (tempFile.getFile().createOutputStream()); - if (out != nullptr) + if (out != nullptr) + { + if (options.storageFormat == storeAsCompressedBinary) { - if (options.storageFormat == storeAsCompressedBinary) - { - out->writeInt (PropertyFileConstants::magicNumberCompressed); - out->flush(); + out->writeInt (PropertyFileConstants::magicNumberCompressed); + out->flush(); - out = new GZIPCompressorOutputStream (out.release(), 9, true); - } - else - { - // have you set up the storage option flags correctly? - jassert (options.storageFormat == storeAsBinary); + out = new GZIPCompressorOutputStream (out.release(), 9, true); + } + else + { + // have you set up the storage option flags correctly? + jassert (options.storageFormat == storeAsBinary); - out->writeInt (PropertyFileConstants::magicNumber); - } + out->writeInt (PropertyFileConstants::magicNumber); + } - const int numProperties = getAllProperties().size(); + const int numProperties = getAllProperties().size(); - out->writeInt (numProperties); + out->writeInt (numProperties); - for (int i = 0; i < numProperties; ++i) - { - out->writeString (getAllProperties().getAllKeys() [i]); - out->writeString (getAllProperties().getAllValues() [i]); - } + for (int i = 0; i < numProperties; ++i) + { + out->writeString (getAllProperties().getAllKeys() [i]); + out->writeString (getAllProperties().getAllValues() [i]); + } - out = nullptr; + out = nullptr; - if (tempFile.overwriteTargetFileWithTemporary()) - { - needsWriting = false; - return true; - } + if (tempFile.overwriteTargetFileWithTemporary()) + { + needsWriting = false; + return true; } } diff --git a/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index 8df6ecde4b..d501d9950a 100644 --- a/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -212,7 +212,7 @@ public: //============================================================================== /** Returns the file that's being used. */ - File getFile() const { return file; } + const File& getFile() const noexcept { return file; } protected: @@ -230,6 +230,11 @@ private: void timerCallback(); void initialise(); + bool saveAsXml(); + bool saveAsBinary(); + bool loadAsXml(); + bool loadAsBinary(); + bool loadAsBinary (InputStream&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile); };