text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/** A handy macro to make it easy to iterate all the child elements in an XmlElement.

    The parentXmlElement should be a reference to the parent XML, and the childElementVariableName
    will be the name of a pointer to each child element.

    E.g. @code
    XmlElement* myParentXml = createSomeKindOfXmlDocument();

    forEachXmlChildElement (*myParentXml, child)
    {
        if (child->hasTagName ("FOO"))
            doSomethingWithXmlElement (child);
    }

    @endcode

    @see forEachXmlChildElementWithTagName
*/
#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \
\
    for (juce::XmlElement* childElementVariableName = (parentXmlElement).getFirstChildElement(); \
         childElementVariableName != nullptr; \
         childElementVariableName = childElementVariableName->getNextElement())

/** A macro that makes it easy to iterate all the child elements of an XmlElement
    which have a specified tag.

    This does the same job as the forEachXmlChildElement macro, but only for those
    elements that have a particular tag name.

    The parentXmlElement should be a reference to the parent XML, and the childElementVariableName
    will be the name of a pointer to each child element. The requiredTagName is the
    tag name to match.

    E.g. @code
    XmlElement* myParentXml = createSomeKindOfXmlDocument();

    forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG")
    {
        // the child object is now guaranteed to be a <MYTAG> element..
        doSomethingWithMYTAGElement (child);
    }

    @endcode

    @see forEachXmlChildElement
*/
#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \
\
    for (juce::XmlElement* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \
         childElementVariableName != nullptr; \
         childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName))

//==============================================================================
/** Used to build a tree of elements representing an XML document.

    An XML document can be parsed into a tree of XmlElements, each of which
    represents an XML tag structure, and which may itself contain other
    nested elements.

    An XmlElement can also be converted back into a text document, and has
    lots of useful methods for manipulating its attributes and sub-elements,
    so XmlElements can actually be used as a handy general-purpose data
    structure.

    Here's an example of parsing some elements: @code
    // check we're looking at the right kind of document..
    if (myElement->hasTagName ("ANIMALS"))
    {
        // now we'll iterate its sub-elements looking for 'giraffe' elements..
        forEachXmlChildElement (*myElement, e)
        {
            if (e->hasTagName ("GIRAFFE"))
            {
                // found a giraffe, so use some of its attributes..

                String giraffeName  = e->getStringAttribute ("name");
                int giraffeAge      = e->getIntAttribute ("age");
                bool isFriendly     = e->getBoolAttribute ("friendly");
            }
        }
    }
    @endcode

    And here's an example of how to create an XML document from scratch: @code
    // create an outer node called "ANIMALS"
    XmlElement animalsList ("ANIMALS");

    for (int i = 0; i < numAnimals; ++i)
    {
        // create an inner element..
        XmlElement* giraffe = new XmlElement ("GIRAFFE");

        giraffe->setAttribute ("name", "nigel");
        giraffe->setAttribute ("age", 10);
        giraffe->setAttribute ("friendly", true);

        // ..and add our new element to the parent node
        animalsList.addChildElement (giraffe);
    }

    // now we can turn the whole thing into a text document..
    String myXmlDoc = animalsList.createDocument (String());
    @endcode

    @see XmlDocument
*/
class JUCE_API  XmlElement
{
public:
    //==============================================================================
    /** Creates an XmlElement with this tag name. */
    explicit XmlElement (const String& tagName);

    /** Creates an XmlElement with this tag name. */
    explicit XmlElement (const char* tagName);

    /** Creates an XmlElement with this tag name. */
    explicit XmlElement (const Identifier& tagName);

    /** Creates an XmlElement with this tag name. */
    explicit XmlElement (StringRef tagName);

    /** Creates an XmlElement with this tag name. */
    XmlElement (String::CharPointerType tagNameBegin, String::CharPointerType tagNameEnd);

    /** Creates a (deep) copy of another element. */
    XmlElement (const XmlElement&);

    /** Creates a (deep) copy of another element. */
    XmlElement& operator= (const XmlElement&);

    /** Move assignment operator */
    XmlElement& operator= (XmlElement&&) noexcept;

    /** Move constructor */
    XmlElement (XmlElement&&) noexcept;

    /** Deleting an XmlElement will also delete all of its child elements. */
    ~XmlElement() noexcept;

    //==============================================================================
    /** Compares two XmlElements to see if they contain the same text and attributes.

        The elements are only considered equivalent if they contain the same attributes
        with the same values, and have the same sub-nodes.

        @param other                    the other element to compare to
        @param ignoreOrderOfAttributes  if true, this means that two elements with the
                                        same attributes in a different order will be
                                        considered the same; if false, the attributes must
                                        be in the same order as well
    */
    bool isEquivalentTo (const XmlElement* other,
                         bool ignoreOrderOfAttributes) const noexcept;

    //==============================================================================
    /** Returns an XML text document that represents this element.

        The string returned can be parsed to recreate the same XmlElement that
        was used to create it.

        @param dtdToUse         the DTD to add to the document
        @param allOnOneLine     if true, this means that the document will not contain any
                                linefeeds, so it'll be smaller but not very easy to read.
        @param includeXmlHeader whether to add the "<?xml version..etc" line at the start of the
                                document
        @param encodingType     the character encoding format string to put into the xml
                                header
        @param lineWrapLength   the line length that will be used before items get placed on
                                a new line. This isn't an absolute maximum length, it just
                                determines how lists of attributes get broken up
        @see writeToStream, writeToFile
    */
    String createDocument (StringRef dtdToUse,
                           bool allOnOneLine = false,
                           bool includeXmlHeader = true,
                           StringRef encodingType = "UTF-8",
                           int lineWrapLength = 60) const;

    /** Writes the document to a stream as UTF-8.

        @param output           the stream to write to
        @param dtdToUse         the DTD to add to the document
        @param allOnOneLine     if true, this means that the document will not contain any
                                linefeeds, so it'll be smaller but not very easy to read.
        @param includeXmlHeader whether to add the "<?xml version..etc" line at the start of the
                                document
        @param encodingType     the character encoding format string to put into the xml
                                header
        @param lineWrapLength   the line length that will be used before items get placed on
                                a new line. This isn't an absolute maximum length, it just
                                determines how lists of attributes get broken up
        @see writeToFile, createDocument
    */
    void writeToStream (OutputStream& output,
                        StringRef dtdToUse,
                        bool allOnOneLine = false,
                        bool includeXmlHeader = true,
                        StringRef encodingType = "UTF-8",
                        int lineWrapLength = 60) const;

    /** Writes the element to a file as an XML document.

        To improve safety in case something goes wrong while writing the file, this
        will actually write the document to a new temporary file in the same
        directory as the destination file, and if this succeeds, it will rename this
        new file as the destination file (overwriting any existing file that was there).

        @param destinationFile  the file to write to. If this already exists, it will be
                                overwritten.
        @param dtdToUse         the DTD to add to the document
        @param encodingType     the character encoding format string to put into the xml
                                header
        @param lineWrapLength   the line length that will be used before items get placed on
                                a new line. This isn't an absolute maximum length, it just
                                determines how lists of attributes get broken up
        @returns    true if the file is written successfully; false if something goes wrong
                    in the process
        @see createDocument
    */
    bool writeToFile (const File& destinationFile,
                      StringRef dtdToUse,
                      StringRef encodingType = "UTF-8",
                      int lineWrapLength = 60) const;

    //==============================================================================
    /** Returns this element's tag type name.
        E.g. for an element such as \<MOOSE legs="4" antlers="2">, this would return "MOOSE".
        @see hasTagName
    */
    const String& getTagName() const noexcept            { return tagName; }

    /** Returns the namespace portion of the tag-name, or an empty string if none is specified. */
    String getNamespace() const;

    /** Returns the part of the tag-name that follows any namespace declaration. */
    String getTagNameWithoutNamespace() const;

    /** Tests whether this element has a particular tag name.
        @param possibleTagName  the tag name you're comparing it with
        @see getTagName
    */
    bool hasTagName (StringRef possibleTagName) const noexcept;

    /** Tests whether this element has a particular tag name, ignoring any XML namespace prefix.
        So a test for e.g. "xyz" will return true for "xyz" and also "foo:xyz", "bar::xyz", etc.
        @see getTagName
    */
    bool hasTagNameIgnoringNamespace (StringRef possibleTagName) const;

    //==============================================================================
    /** Returns the number of XML attributes this element contains.

        E.g. for an element such as \<MOOSE legs="4" antlers="2">, this would
        return 2.
    */
    int getNumAttributes() const noexcept;

    /** Returns the name of one of the elements attributes.

        E.g. for an element such as \<MOOSE legs="4" antlers="2">, then
        getAttributeName(1) would return "antlers".

        @see getAttributeValue, getStringAttribute
    */
    const String& getAttributeName (int attributeIndex) const noexcept;

    /** Returns the value of one of the elements attributes.

        E.g. for an element such as \<MOOSE legs="4" antlers="2">, then
        getAttributeName(1) would return "2".

        @see getAttributeName, getStringAttribute
    */
    const String& getAttributeValue (int attributeIndex) const noexcept;

    //==============================================================================
    // Attribute-handling methods..

    /** Checks whether the element contains an attribute with a certain name. */
    bool hasAttribute (StringRef attributeName) const noexcept;

    /** Returns the value of a named attribute.
        @param attributeName        the name of the attribute to look up
    */
    const String& getStringAttribute (StringRef attributeName) const noexcept;

    /** Returns the value of a named attribute.
        @param attributeName        the name of the attribute to look up
        @param defaultReturnValue   a value to return if the element doesn't have an attribute
                                    with this name
    */
    String getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const;

    /** Compares the value of a named attribute with a value passed-in.

        @param attributeName            the name of the attribute to look up
        @param stringToCompareAgainst   the value to compare it with
        @param ignoreCase               whether the comparison should be case-insensitive
        @returns    true if the value of the attribute is the same as the string passed-in;
                    false if it's different (or if no such attribute exists)
    */
    bool compareAttribute (StringRef attributeName,
                           StringRef stringToCompareAgainst,
                           bool ignoreCase = false) const noexcept;

    /** Returns the value of a named attribute as an integer.

        This will try to find the attribute and convert it to an integer (using
        the String::getIntValue() method).

        @param attributeName        the name of the attribute to look up
        @param defaultReturnValue   a value to return if the element doesn't have an attribute
                                    with this name
        @see setAttribute
    */
    int getIntAttribute (StringRef attributeName, int defaultReturnValue = 0) const;

    /** Returns the value of a named attribute as floating-point.

        This will try to find the attribute and convert it to a double (using
        the String::getDoubleValue() method).

        @param attributeName        the name of the attribute to look up
        @param defaultReturnValue   a value to return if the element doesn't have an attribute
                                    with this name
        @see setAttribute
    */
    double getDoubleAttribute (StringRef attributeName, double defaultReturnValue = 0.0) const;

    /** Returns the value of a named attribute as a boolean.

        This will try to find the attribute and interpret it as a boolean. To do this,
        it'll return true if the value is "1", "true", "y", etc, or false for other
        values.

        @param attributeName        the name of the attribute to look up
        @param defaultReturnValue   a value to return if the element doesn't have an attribute
                                    with this name
    */
    bool getBoolAttribute (StringRef attributeName, bool defaultReturnValue = false) const;

    /** Adds a named attribute to the element.

        If the element already contains an attribute with this name, it's value will
        be updated to the new value. If there's no such attribute yet, a new one will
        be added.

        Note that there are other setAttribute() methods that take integers,
        doubles, etc. to make it easy to store numbers.

        @param attributeName        the name of the attribute to set
        @param newValue             the value to set it to
        @see removeAttribute
    */
    void setAttribute (const Identifier& attributeName, const String& newValue);

    /** Adds a named attribute to the element, setting it to an integer value.

        If the element already contains an attribute with this name, it's value will
        be updated to the new value. If there's no such attribute yet, a new one will
        be added.

        Note that there are other setAttribute() methods that take integers,
        doubles, etc. to make it easy to store numbers.

        @param attributeName        the name of the attribute to set
        @param newValue             the value to set it to
    */
    void setAttribute (const Identifier& attributeName, int newValue);

    /** Adds a named attribute to the element, setting it to a floating-point value.

        If the element already contains an attribute with this name, it's value will
        be updated to the new value. If there's no such attribute yet, a new one will
        be added.

        Note that there are other setAttribute() methods that take integers,
        doubles, etc. to make it easy to store numbers.

        @param attributeName        the name of the attribute to set
        @param newValue             the value to set it to
    */
    void setAttribute (const Identifier& attributeName, double newValue);

    /** Removes a named attribute from the element.

        @param attributeName    the name of the attribute to remove
        @see removeAllAttributes
    */
    void removeAttribute (const Identifier& attributeName) noexcept;

    /** Removes all attributes from this element. */
    void removeAllAttributes() noexcept;

    //==============================================================================
    // Child element methods..

    /** Returns the first of this element's sub-elements.
        see getNextElement() for an example of how to iterate the sub-elements.
        @see forEachXmlChildElement
    */
    XmlElement* getFirstChildElement() const noexcept       { return firstChildElement; }

    /** Returns the next of this element's siblings.

        This can be used for iterating an element's sub-elements, e.g.
        @code
        XmlElement* child = myXmlDocument->getFirstChildElement();

        while (child != nullptr)
        {
            ...do stuff with this child..

            child = child->getNextElement();
        }
        @endcode

        Note that when iterating the child elements, some of them might be
        text elements as well as XML tags - use isTextElement() to work this
        out.

        Also, it's much easier and neater to use this method indirectly via the
        forEachXmlChildElement macro.

        @returns    the sibling element that follows this one, or a nullptr if
                    this is the last element in its parent

        @see getNextElement, isTextElement, forEachXmlChildElement
    */
    inline XmlElement* getNextElement() const noexcept          { return nextListItem; }

    /** Returns the next of this element's siblings which has the specified tag
        name.

        This is like getNextElement(), but will scan through the list until it
        finds an element with the given tag name.

        @see getNextElement, forEachXmlChildElementWithTagName
    */
    XmlElement* getNextElementWithTagName (StringRef requiredTagName) const;

    /** Returns the number of sub-elements in this element.
        @see getChildElement
    */
    int getNumChildElements() const noexcept;

    /** Returns the sub-element at a certain index.

        It's not very efficient to iterate the sub-elements by index - see
        getNextElement() for an example of how best to iterate.

        @returns the n'th child of this element, or nullptr if the index is out-of-range
        @see getNextElement, isTextElement, getChildByName
    */
    XmlElement* getChildElement (int index) const noexcept;

    /** Returns the first sub-element with a given tag-name.

        @param tagNameToLookFor     the tag name of the element you want to find
        @returns the first element with this tag name, or nullptr if none is found
        @see getNextElement, isTextElement, getChildElement, getChildByAttribute
    */
    XmlElement* getChildByName (StringRef tagNameToLookFor) const noexcept;

    /** Returns the first sub-element which has an attribute that matches the given value.

        @param attributeName     the name of the attribute to check
        @param attributeValue    the target value of the attribute
        @returns the first element with this attribute value, or nullptr if none is found
        @see getChildByName
    */
    XmlElement* getChildByAttribute (StringRef attributeName,
                                     StringRef attributeValue) const noexcept;

    //==============================================================================
    /** Appends an element to this element's list of children.

        Child elements are deleted automatically when their parent is deleted, so
        make sure the object that you pass in will not be deleted by anything else,
        and make sure it's not already the child of another element.

        Note that due to the XmlElement using a singly-linked-list, prependChildElement()
        is an O(1) operation, but addChildElement() is an O(N) operation - so if
        you're adding large number of elements, you may prefer to do so in reverse order!

        @see getFirstChildElement, getNextElement, getNumChildElements,
             getChildElement, removeChildElement
    */
    void addChildElement (XmlElement* newChildElement) noexcept;

    /** Inserts an element into this element's list of children.

        Child elements are deleted automatically when their parent is deleted, so
        make sure the object that you pass in will not be deleted by anything else,
        and make sure it's not already the child of another element.

        @param newChildElement  the element to add
        @param indexToInsertAt  the index at which to insert the new element - if this is
                                below zero, it will be added to the end of the list
        @see addChildElement, insertChildElement
    */
    void insertChildElement (XmlElement* newChildElement,
                             int indexToInsertAt) noexcept;

    /** Inserts an element at the beginning of this element's list of children.

        Child elements are deleted automatically when their parent is deleted, so
        make sure the object that you pass in will not be deleted by anything else,
        and make sure it's not already the child of another element.

        Note that due to the XmlElement using a singly-linked-list, prependChildElement()
        is an O(1) operation, but addChildElement() is an O(N) operation - so if
        you're adding large number of elements, you may prefer to do so in reverse order!

        @see addChildElement, insertChildElement
    */
    void prependChildElement (XmlElement* newChildElement) noexcept;

    /** Creates a new element with the given name and returns it, after adding it
        as a child element.

        This is a handy method that means that instead of writing this:
        @code
        XmlElement* newElement = new XmlElement ("foobar");
        myParentElement->addChildElement (newElement);
        @endcode

        ..you could just write this:
        @code
        XmlElement* newElement = myParentElement->createNewChildElement ("foobar");
        @endcode
    */
    XmlElement* createNewChildElement (StringRef tagName);

    /** Replaces one of this element's children with another node.

        If the current element passed-in isn't actually a child of this element,
        this will return false and the new one won't be added. Otherwise, the
        existing element will be deleted, replaced with the new one, and it
        will return true.
    */
    bool replaceChildElement (XmlElement* currentChildElement,
                              XmlElement* newChildNode) noexcept;

    /** Removes a child element.

        @param childToRemove            the child to look for and remove
        @param shouldDeleteTheChild     if true, the child will be deleted, if false it'll
                                        just remove it
    */
    void removeChildElement (XmlElement* childToRemove,
                             bool shouldDeleteTheChild) noexcept;

    /** Deletes all the child elements in the element.
        @see removeChildElement, deleteAllChildElementsWithTagName
    */
    void deleteAllChildElements() noexcept;

    /** Deletes all the child elements with a given tag name.
        @see removeChildElement
    */
    void deleteAllChildElementsWithTagName (StringRef tagName) noexcept;

    /** Returns true if the given element is a child of this one. */
    bool containsChildElement (const XmlElement* possibleChild) const noexcept;

    /** Recursively searches all sub-elements of this one, looking for an element
        which is the direct parent of the specified element.

        Because elements don't store a pointer to their parent, if you have one
        and need to find its parent, the only way to do so is to exhaustively
        search the whole tree for it.

        If the given child is found somewhere in this element's hierarchy, then
        this method will return its parent. If not, it will return nullptr.
    */
    XmlElement* findParentElementOf (const XmlElement* childToSearchFor) noexcept;

    //==============================================================================
    /** Sorts the child elements using a comparator.

        This will use a comparator object to sort the elements into order. The object
        passed must have a method of the form:
        @code
        int compareElements (const XmlElement* first, const XmlElement* second);
        @endcode

        ..and this method must return:
          - a value of < 0 if the first comes before the second
          - a value of 0 if the two objects are equivalent
          - a value of > 0 if the second comes before the first

        To improve performance, the compareElements() method can be declared as static or const.

        @param comparator   the comparator to use for comparing elements.
        @param retainOrderOfEquivalentItems     if this is true, then items which the comparator
                            says are equivalent will be kept in the order in which they
                            currently appear in the array. This is slower to perform, but
                            may be important in some cases. If it's false, a faster algorithm
                            is used, but equivalent elements may be rearranged.
    */
    template <class ElementComparator>
    void sortChildElements (ElementComparator& comparator,
                            bool retainOrderOfEquivalentItems = false)
    {
        auto num = getNumChildElements();

        if (num > 1)
        {
            HeapBlock<XmlElement*> elems (num);
            getChildElementsAsArray (elems);
            sortArray (comparator, (XmlElement**) elems, 0, num - 1, retainOrderOfEquivalentItems);
            reorderChildElements (elems, num);
        }
    }

    //==============================================================================
    /** Returns true if this element is a section of text.

        Elements can either be an XML tag element or a section of text, so this
        is used to find out what kind of element this one is.

        @see getAllText, addTextElement, deleteAllTextElements
    */
    bool isTextElement() const noexcept;

    /** Returns the text for a text element.

        Note that if you have an element like this:

        @code<xyz>hello</xyz>@endcode

        then calling getText on the "xyz" element won't return "hello", because that is
        actually stored in a special text sub-element inside the xyz element. To get the
        "hello" string, you could either call getText on the (unnamed) sub-element, or
        use getAllSubText() to do this automatically.

        Note that leading and trailing whitespace will be included in the string - to remove
        if, just call String::trim() on the result.

        @see isTextElement, getAllSubText, getChildElementAllSubText
    */
    const String& getText() const noexcept;

    /** Sets the text in a text element.

        Note that this is only a valid call if this element is a text element. If it's
        not, then no action will be performed. If you're trying to add text inside a normal
        element, you probably want to use addTextElement() instead.
    */
    void setText (const String& newText);

    /** Returns all the text from this element's child nodes.

        This iterates all the child elements and when it finds text elements,
        it concatenates their text into a big string which it returns.

        E.g. @code<xyz>hello <x>there</x> world</xyz>@endcode
        if you called getAllSubText on the "xyz" element, it'd return "hello there world".

        Note that leading and trailing whitespace will be included in the string - to remove
        if, just call String::trim() on the result.

        @see isTextElement, getChildElementAllSubText, getText, addTextElement
    */
    String getAllSubText() const;

    /** Returns all the sub-text of a named child element.

        If there is a child element with the given tag name, this will return
        all of its sub-text (by calling getAllSubText() on it). If there is
        no such child element, this will return the default string passed-in.

        @see getAllSubText
    */
    String getChildElementAllSubText (StringRef childTagName,
                                      const String& defaultReturnValue) const;

    /** Appends a section of text to this element.
        @see isTextElement, getText, getAllSubText
    */
    void addTextElement (const String& text);

    /** Removes all the text elements from this element.
        @see isTextElement, getText, getAllSubText, addTextElement
    */
    void deleteAllTextElements() noexcept;

    /** Creates a text element that can be added to a parent element. */
    static XmlElement* createTextElement (const String& text);

    /** Checks if a given string is a valid XML name */
    static bool isValidXmlName (StringRef possibleName) noexcept;

    //==============================================================================
private:
    struct XmlAttributeNode
    {
        XmlAttributeNode (const XmlAttributeNode&) noexcept;
        XmlAttributeNode (const Identifier&, const String&) noexcept;
        XmlAttributeNode (String::CharPointerType, String::CharPointerType);

        LinkedListPointer<XmlAttributeNode> nextListItem;
        Identifier name;
        String value;

    private:
        XmlAttributeNode& operator= (const XmlAttributeNode&) JUCE_DELETED_FUNCTION;
    };

    friend class XmlDocument;
    friend class LinkedListPointer<XmlAttributeNode>;
    friend class LinkedListPointer<XmlElement>;
    friend class LinkedListPointer<XmlElement>::Appender;
    friend class NamedValueSet;

    LinkedListPointer<XmlElement> nextListItem;
    LinkedListPointer<XmlElement> firstChildElement;
    LinkedListPointer<XmlAttributeNode> attributes;
    String tagName;

    XmlElement (int) noexcept;
    void copyChildrenAndAttributesFrom (const XmlElement&);
    void writeElementAsText (OutputStream&, int indentationLevel, int lineWrapLength) const;
    void getChildElementsAsArray (XmlElement**) const noexcept;
    void reorderChildElements (XmlElement**, int) noexcept;
    XmlAttributeNode* getAttribute (StringRef) const noexcept;

    // Sigh.. L"" or _T("") string literals are problematic in general, and really inappropriate
    // for XML tags. Use a UTF-8 encoded literal instead, or if you're really determined to use
    // UTF-16, cast it to a String and use the other constructor.
    XmlElement (const wchar_t*) JUCE_DELETED_FUNCTION;

    JUCE_LEAK_DETECTOR (XmlElement)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

DirectoryIterator::DirectoryIterator (const File& directory, bool recursive,
                                      const String& pattern, const int type)
  : wildCards (parseWildcards (pattern)),
    fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern),
    wildCard (pattern),
    path (File::addTrailingSeparator (directory.getFullPathName())),
    index (-1),
    totalNumFiles (-1),
    whatToLookFor (type),
    isRecursive (recursive),
    hasBeenAdvanced (false)
{
    // you have to specify the type of files you're looking for!
    jassert ((type & (File::findFiles | File::findDirectories)) != 0);
    jassert (type > 0 && type <= 7);
}

DirectoryIterator::~DirectoryIterator()
{
}

StringArray DirectoryIterator::parseWildcards (const String& pattern)
{
    StringArray s;
    s.addTokens (pattern, ";,", "\"'");
    s.trim();
    s.removeEmptyStrings();
    return s;
}

bool DirectoryIterator::fileMatches (const StringArray& wildCards, const String& filename)
{
    for (int i = 0; i < wildCards.size(); ++i)
        if (filename.matchesWildcard (wildCards[i], ! File::areFileNamesCaseSensitive()))
            return true;

    return false;
}

bool DirectoryIterator::next()
{
    return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
}

bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize,
                              Time* const modTime, Time* const creationTime, bool* const isReadOnly)
{
    for (;;)
    {
        hasBeenAdvanced = true;

        if (subIterator != nullptr)
        {
            if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly))
                return true;

            subIterator = nullptr;
        }

        String filename;
        bool isDirectory, isHidden = false, shouldContinue = false;

        while (fileFinder.next (filename, &isDirectory,
                                (isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr,
                                fileSize, modTime, creationTime, isReadOnly))
        {
            ++index;

            if (! filename.containsOnly ("."))
            {
                bool matches = false;

                if (isDirectory)
                {
                    if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden))
                        subIterator = new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename),
                                                             true, wildCard, whatToLookFor);

                    matches = (whatToLookFor & File::findDirectories) != 0;
                }
                else
                {
                    matches = (whatToLookFor & File::findFiles) != 0;
                }

                // if we're not relying on the OS iterator to do the wildcard match, do it now..
                if (matches && (isRecursive || wildCards.size() > 1))
                    matches = fileMatches (wildCards, filename);

                if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0)
                    matches = ! isHidden;

                if (matches)
                {
                    currentFile = File::createFileWithoutCheckingPath (path + filename);
                    if (isHiddenResult != nullptr)     *isHiddenResult = isHidden;
                    if (isDirResult != nullptr)        *isDirResult = isDirectory;

                    return true;
                }

                if (subIterator != nullptr)
                {
                    shouldContinue = true;
                    break;
                }
            }
        }

        if (! shouldContinue)
            return false;
    }
}

const File& DirectoryIterator::getFile() const
{
    if (subIterator != nullptr && subIterator->hasBeenAdvanced)
        return subIterator->getFile();

    // You need to call DirectoryIterator::next() before asking it for the file that it found!
    jassert (hasBeenAdvanced);

    return currentFile;
}

float DirectoryIterator::getEstimatedProgress() const
{
    if (totalNumFiles < 0)
        totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories);

    if (totalNumFiles <= 0)
        return 0.0f;

    const float detailedIndex = (subIterator != nullptr) ? index + subIterator->getEstimatedProgress()
                                                         : (float) index;

    return jlimit (0.0f, 1.0f, detailedIndex / totalNumFiles);
}

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Represents a set of folders that make up a search path.

    @see File
*/
class JUCE_API  FileSearchPath
{
public:
    //==============================================================================
    /** Creates an empty search path. */
    FileSearchPath();

    /** Creates a search path from a string of pathnames.

        The path can be semicolon- or comma-separated, e.g.
        "/foo/bar;/foo/moose;/fish/moose"

        The separate folders are tokenised and added to the search path.
    */
    FileSearchPath (const String& path);

    /** Creates a copy of another search path. */
    FileSearchPath (const FileSearchPath&);

    /** Copies another search path. */
    FileSearchPath& operator= (const FileSearchPath&);

    /** Destructor. */
    ~FileSearchPath();

    /** Uses a string containing a list of pathnames to re-initialise this list.

        This search path is cleared and the semicolon- or comma-separated folders
        in this string are added instead. e.g. "/foo/bar;/foo/moose;/fish/moose"
    */
    FileSearchPath& operator= (const String& path);

    //==============================================================================
    /** Returns the number of folders in this search path.
        @see operator[]
    */
    int getNumPaths() const;

    /** Returns one of the folders in this search path.
        The file returned isn't guaranteed to actually be a valid directory.
        @see getNumPaths
    */
    File operator[] (int index) const;

    /** Returns the search path as a semicolon-separated list of directories. */
    String toString() const;

    //==============================================================================
    /** Adds a new directory to the search path.

        The new directory is added to the end of the list if the insertIndex parameter is
        less than zero, otherwise it is inserted at the given index.
    */
    void add (const File& directoryToAdd,
              int insertIndex = -1);

    /** Adds a new directory to the search path if it's not already in there.

        @return true if the directory has been added, false otherwise.
    */
    bool addIfNotAlreadyThere (const File& directoryToAdd);

    /** Removes a directory from the search path. */
    void remove (int indexToRemove);

    /** Merges another search path into this one.
        This will remove any duplicate directories.
    */
    void addPath (const FileSearchPath&);

    /** Removes any directories that are actually subdirectories of one of the other directories in the search path.

        If the search is intended to be recursive, there's no point having nested folders in the search
        path, because they'll just get searched twice and you'll get duplicate results.

        e.g. if the path is "c:\abc\de;c:\abc", this method will simplify it to "c:\abc"
    */
    void removeRedundantPaths();

    /** Removes any directories that don't actually exist. */
    void removeNonExistentPaths();

    //==============================================================================
    /** Searches the path for a wildcard.

        This will search all the directories in the search path in order, adding any
        matching files to the results array.

        @param results                  an array to append the results to
        @param whatToLookFor            a value from the File::TypesOfFileToFind enum, specifying whether to
                                        return files, directories, or both.
        @param searchRecursively        whether to recursively search the subdirectories too
        @param wildCardPattern          a pattern to match against the filenames
        @returns the number of files added to the array
        @see File::findChildFiles
    */
    int findChildFiles (Array<File>& results,
                        int whatToLookFor,
                        bool searchRecursively,
                        const String& wildCardPattern = "*") const;

    //==============================================================================
    /** Finds out whether a file is inside one of the path's directories.

        This will return true if the specified file is a child of one of the
        directories specified by this path. Note that this doesn't actually do any
        searching or check that the files exist - it just looks at the pathnames
        to work out whether the file would be inside a directory.

        @param fileToCheck      the file to look for
        @param checkRecursively if true, then this will return true if the file is inside a
                                subfolder of one of the path's directories (at any depth). If false
                                it will only return true if the file is actually a direct child
                                of one of the directories.
        @see File::isAChildOf

    */
    bool isFileInPath (const File& fileToCheck,
                       bool checkRecursively) const;

private:
    //==============================================================================
    StringArray directories;

    void init (const String&);

    JUCE_LEAK_DETECTOR (FileSearchPath)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

static void parseWildcard (const String& pattern, StringArray& result)
{
    result.addTokens (pattern.toLowerCase(), ";,", "\"'");

    result.trim();
    result.removeEmptyStrings();

    // special case for *.*, because people use it to mean "any file", but it
    // would actually ignore files with no extension.
    for (int i = result.size(); --i >= 0;)
        if (result[i] == "*.*")
            result.set (i, "*");
}

static bool matchWildcard (const File& file, const StringArray& wildcards)
{
    const String filename (file.getFileName());

    for (int i = wildcards.size(); --i >= 0;)
        if (filename.matchesWildcard (wildcards[i], true))
            return true;

    return false;
}

WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
                                        const String& directoryWildcardPatterns,
                                        const String& desc)
    : FileFilter (desc.isEmpty() ? fileWildcardPatterns
                                 : (desc + " (" + fileWildcardPatterns + ")"))
{
    parseWildcard (fileWildcardPatterns, fileWildcards);
    parseWildcard (directoryWildcardPatterns, directoryWildcards);
}

WildcardFileFilter::~WildcardFileFilter()
{
}

bool WildcardFileFilter::isFileSuitable (const File& file) const
{
    return matchWildcard (file, fileWildcards);
}

bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
    return matchWildcard (file, directoryWildcards);
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    An output stream that writes into a local file.

    @see OutputStream, FileInputStream, File::createOutputStream
*/
class JUCE_API  FileOutputStream  : public OutputStream
{
public:
    //==============================================================================
    /** Creates a FileOutputStream.

        If the file doesn't exist, it will first be created. If the file can't be
        created or opened (for example, because the parent directory of the file
        does not exist), the failedToOpen() method will return true.

        If the file already exists when opened, the stream's write-position will
        be set to the end of the file. To overwrite an existing file,
        use File::deleteFile() before opening the stream, or use setPosition(0)
        after it's opened (although this won't truncate the file).

        Destroying a FileOutputStream object does not force the operating system
        to write the buffered data to disk immediately. If this is required you
        should call flush() before triggering the destructor.

        @see TemporaryFile
    */
    FileOutputStream (const File& fileToWriteTo,
                      size_t bufferSizeToUse = 16384);

    /** Destructor. */
    ~FileOutputStream();

    //==============================================================================
    /** Returns the file that this stream is writing to.
    */
    const File& getFile() const                         { return file; }

    /** Returns the status of the file stream.
        The result will be ok if the file opened successfully. If an error occurs while
        opening or writing to the file, this will contain an error message.
    */
    const Result& getStatus() const noexcept            { return status; }

    /** Returns true if the stream couldn't be opened for some reason.
        @see getResult()
    */
    bool failedToOpen() const noexcept                  { return status.failed(); }

    /** Returns true if the stream opened without problems.
        @see getResult()
    */
    bool openedOk() const noexcept                      { return status.wasOk(); }

    /** Attempts to truncate the file to the current write position.
        To truncate a file to a specific size, first use setPosition() to seek to the
        appropriate location, and then call this method.
    */
    Result truncate();

    //==============================================================================
    void flush() override;
    int64 getPosition() override;
    bool setPosition (int64) override;
    bool write (const void*, size_t) override;
    bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override;

private:
    //==============================================================================
    File file;
    void* fileHandle;
    Result status;
    int64 currentPosition;
    size_t bufferSize, bytesInBuffer;
    HeapBlock<char> buffer;

    void openHandle();
    void closeHandle();
    void flushInternal();
    bool flushBuffer();
    int64 setPositionInternal (int64);
    ssize_t writeInternal (const void*, size_t);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Manages a temporary file, which will be deleted when this object is deleted.

    This object is intended to be used as a stack based object, using its scope
    to make sure the temporary file isn't left lying around.

    For example:

    @code
    {
        File myTargetFile ("~/myfile.txt");

        // this will choose a file called something like "~/myfile_temp239348.txt"
        // which definitely doesn't exist at the time the constructor is called.
        TemporaryFile temp (myTargetFile);

        // create a stream to the temporary file, and write some data to it...
        ScopedPointer<FileOutputStream> out (temp.getFile().createOutputStream());

        if (out != nullptr)
        {
            out->write ( ...etc )
            out = nullptr; // (deletes the stream)

            // ..now we've finished writing, this will rename the temp file to
            // make it replace the target file we specified above.
            bool succeeded = temp.overwriteTargetFileWithTemporary();
        }

        // ..and even if something went wrong and our overwrite failed,
        // as the TemporaryFile object goes out of scope here, it'll make sure
        // that the temp file gets deleted.
    }
    @endcode

    @see File, FileOutputStream
*/
class JUCE_API  TemporaryFile
{
public:
    //==============================================================================
    enum OptionFlags
    {
        useHiddenFile = 1,          /**< Indicates that the temporary file should be hidden -
                                         i.e. its name should start with a dot. */
        putNumbersInBrackets = 2    /**< Indicates that when numbers are appended to make sure
                                         the file is unique, they should go in brackets rather
                                         than just being appended (see File::getNonexistentSibling() )*/
    };

    //==============================================================================
    /** Creates a randomly-named temporary file in the default temp directory.

        @param suffix       a file suffix to use for the file
        @param optionFlags  a combination of the values listed in the OptionFlags enum
        The file will not be created until you write to it. And remember that when
        this object is deleted, the file will also be deleted!
    */
    TemporaryFile (const String& suffix = String(),
                   int optionFlags = 0);

    /** Creates a temporary file in the same directory as a specified file.

        This is useful if you have a file that you want to overwrite, but don't
        want to harm the original file if the write operation fails. You can
        use this to create a temporary file next to the target file, then
        write to the temporary file, and finally use overwriteTargetFileWithTemporary()
        to replace the target file with the one you've just written.

        This class won't create any files until you actually write to them. And remember
        that when this object is deleted, the temporary file will also be deleted!

        @param targetFile   the file that you intend to overwrite - the temporary
                            file will be created in the same directory as this
        @param optionFlags  a combination of the values listed in the OptionFlags enum
    */
    TemporaryFile (const File& targetFile,
                   int optionFlags = 0);

    /** Creates a temporary file using an explicit filename.
        The other constructors are a better choice than this one, unless for some reason
        you need to explicitly specify the temporary file you want to use.

        @param targetFile    the file that you intend to overwrite
        @param temporaryFile the temporary file to be used
    */
    TemporaryFile (const File& targetFile,
                   const File& temporaryFile);

    /** Destructor.

        When this object is deleted it will make sure that its temporary file is
        also deleted! If the operation fails, it'll throw an assertion in debug
        mode.
    */
    ~TemporaryFile();

    //==============================================================================
    /** Returns the temporary file. */
    const File& getFile() const noexcept                { return temporaryFile; }

    /** Returns the target file that was specified in the constructor. */
    const File& getTargetFile() const noexcept          { return targetFile; }

    /** Tries to move the temporary file to overwrite the target file that was
        specified in the constructor.

        If you used the constructor that specified a target file, this will attempt
        to replace that file with the temporary one.

        Before calling this, make sure:
        - that you've actually written to the temporary file
        - that you've closed any open streams that you were using to write to it
        - and that you don't have any streams open to the target file, which would
          prevent it being overwritten

        If the file move succeeds, this returns false, and the temporary file will
        have disappeared. If it fails, the temporary file will probably still exist,
        but will be deleted when this object is destroyed.
    */
    bool overwriteTargetFileWithTemporary() const;

    /** Attempts to delete the temporary file, if it exists.
        @returns true if the file is successfully deleted (or if it didn't exist).
    */
    bool deleteTemporaryFile() const;

private:
    //==============================================================================
    const File temporaryFile, targetFile;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A type of FileFilter that works by wildcard pattern matching.

    This filter only allows files that match one of the specified patterns, but
    allows all directories through.

    @see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent
*/
class JUCE_API  WildcardFileFilter  : public FileFilter
{
public:
    //==============================================================================
    /**
        Creates a wildcard filter for one or more patterns.

        The wildcardPatterns parameter is a comma or semicolon-delimited set of
        patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav
        or .aiff.

        Passing an empty string as a pattern will fail to match anything, so by leaving
        either the file or directory pattern parameter empty means you can control
        whether files or directories are found.

        The description is a name to show the user in a list of possible patterns, so
        for the wav/aiff example, your description might be "audio files".
    */
    WildcardFileFilter (const String& fileWildcardPatterns,
                        const String& directoryWildcardPatterns,
                        const String& description);

    /** Destructor. */
    ~WildcardFileFilter();

    //==============================================================================
    /** Returns true if the filename matches one of the patterns specified. */
    bool isFileSuitable (const File& file) const override;

    /** This always returns true. */
    bool isDirectorySuitable (const File& file) const override;

private:
    //==============================================================================
    StringArray fileWildcards, directoryWildcards;

    JUCE_LEAK_DETECTOR (WildcardFileFilter)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Maps a file into virtual memory for easy reading and/or writing.
*/
class JUCE_API  MemoryMappedFile
{
public:
    /** The read/write flags used when opening a memory mapped file. */
    enum AccessMode
    {
        readOnly,   /**< Indicates that the memory can only be read. */
        readWrite   /**< Indicates that the memory can be read and written to - changes that are
                         made will be flushed back to disk at the whim of the OS. */
    };

    /** Opens a file and maps it to an area of virtual memory.

        The file should already exist, and should already be the size that you want to work with
        when you call this. If the file is resized after being opened, the behaviour is undefined.

        If the file exists and the operation succeeds, the getData() and getSize() methods will
        return the location and size of the data that can be read or written. Note that the entire
        file is not read into memory immediately - the OS simply creates a virtual mapping, which
        will lazily pull the data into memory when blocks are accessed.

        If the file can't be opened for some reason, the getData() method will return a null pointer.

        If exclusive is false then other apps can also open the same memory mapped file and use this
        mapping as an effective way of communicating. If exclusive is true then the mapped file will
        be opened exclusively - preventing other apps to access the file which may improve the
        performance of accessing the file.
    */
    MemoryMappedFile (const File& file, AccessMode mode, bool exclusive = false);

    /** Opens a section of a file and maps it to an area of virtual memory.

        The file should already exist, and should already be the size that you want to work with
        when you call this. If the file is resized after being opened, the behaviour is undefined.

        If the file exists and the operation succeeds, the getData() and getSize() methods will
        return the location and size of the data that can be read or written. Note that the entire
        file is not read into memory immediately - the OS simply creates a virtual mapping, which
        will lazily pull the data into memory when blocks are accessed.

        If the file can't be opened for some reason, the getData() method will return a null pointer.

        NOTE: the start of the actual range used may be rounded-down to a multiple of the OS's page-size,
        so do not assume that the mapped memory will begin at exactly the position you requested - always
        use getRange() to check the actual range that is being used.
    */
    MemoryMappedFile (const File& file,
                      const Range<int64>& fileRange,
                      AccessMode mode,
                      bool exclusive = false);

    /** Destructor. */
    ~MemoryMappedFile();

    /** Returns the address at which this file has been mapped, or a null pointer if
        the file couldn't be successfully mapped.
    */
    void* getData() const noexcept              { return address; }

    /** Returns the number of bytes of data that are available for reading or writing.
        This will normally be the size of the file.
    */
    size_t getSize() const noexcept             { return (size_t) range.getLength(); }

    /** Returns the section of the file at which the mapped memory represents. */
    Range<int64> getRange() const noexcept      { return range; }

private:
    //==============================================================================
    void* address = nullptr;
    Range<int64> range;

   #if JUCE_WINDOWS
    void* fileHandle = nullptr;
   #else
    int fileHandle = 0;
   #endif

    void openInternal (const File&, AccessMode, bool);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile)
};

} // namespace juce